diff --git a/client/galaxy/scripts/reports/grid/grid-model.js b/client/galaxy/scripts/reports/grid/grid-model.js new file mode 100644 index 000000000000..6e3fc1698f93 --- /dev/null +++ b/client/galaxy/scripts/reports/grid/grid-model.js @@ -0,0 +1,116 @@ +// dependencies + +// grid model +export default Backbone.Model.extend({ + defaults: { + url_base: "", + async: false, + async_ops: [], + categorical_filters: [], + filters: {}, + sort_key: null, + show_item_checkboxes: false, + advanced_search: false, + cur_page: 1, + num_pages: 1, + operation: undefined, + item_ids: undefined + }, + + /** + * Return true if operation can be done asynchronously. + */ + can_async_op: function(op) { + return _.indexOf(this.attributes.async_ops, op) !== -1; + }, + + /** + * Add filtering criterion. + */ + add_filter: function(key, value, append) { + // Update URL arg with new condition. + if (append) { + // Update or append value. + var cur_val = this.attributes.filters[key]; + + var new_val; + if (cur_val === null || cur_val === undefined) { + new_val = value; + } else if (typeof cur_val == "string") { + if (cur_val == "All" || cur_val == value) { + new_val = value; + } else { + // Replace string with array. + var values = []; + values[0] = cur_val; + values[1] = value; + new_val = values; + } + } else { + // Current value is an array. + new_val = cur_val; + if (new_val.indexOf(value) === -1) { + new_val.push(value); + } + } + this.attributes.filters[key] = new_val; + } else { + // Replace value. + this.attributes.filters[key] = value; + } + }, + + /** + * Remove filtering criterion. + */ + remove_filter: function(key, condition) { + var cur_val = this.attributes.filters[key]; + if (cur_val === null || cur_val === undefined) { + return false; + } + + if (typeof cur_val === "string") { + // overwrite/remove condition. + this.attributes.filters[key] = ""; + } else { + // filter contains an array of conditions. + var condition_index = _.indexOf(cur_val, condition); + if (condition_index !== -1) { + cur_val[condition_index] = ""; + } + } + }, + + /** + * Returns URL data for obtaining a new grid. + */ + get_url_data: function() { + var url_data = { + async: this.attributes.async, + sort: this.attributes.sort_key, + page: this.attributes.cur_page, + show_item_checkboxes: this.attributes.show_item_checkboxes, + advanced_search: this.attributes.advanced_search + }; + + // Add operation, item_ids only if they have values. + if (this.attributes.operation) { + url_data.operation = this.attributes.operation; + } + if (this.attributes.item_ids) { + url_data.id = this.attributes.item_ids; + } + + // Add filter arguments to data, placing "f-" in front of all arguments. + var self = this; + _.each(_.pairs(self.attributes.filters), k => { + url_data[`f-${k[0]}`] = k[1]; + }); + return url_data; + }, + + // Return URL for obtaining a new grid + get_url: function(args) { + return `${this.get("url_base")}?${$.param(this.get_url_data())}&${$.param(args)}`; + } +}); diff --git a/client/galaxy/scripts/reports/grid/grid-template.js b/client/galaxy/scripts/reports/grid/grid-template.js new file mode 100644 index 000000000000..5fafae695b16 --- /dev/null +++ b/client/galaxy/scripts/reports/grid/grid-template.js @@ -0,0 +1,574 @@ +// dependencies +import Utils from "utils/utils"; +// grid view templates +export default { + // template + grid: function(options) { + var tmpl = ""; + if (options.embedded) { + tmpl = this.grid_header(options) + this.grid_table(options); + } else { + tmpl = `
${this.grid_header( + options + )}
${this.grid_table( + options + )}`; + } + + // add info text + if (options.info_text) { + tmpl += `
${options.info_text}
`; + } + + // return + return tmpl; + }, + + // template + grid_table: function() { + return ` +
+ + + + +
+
`; + }, + + // template + grid_header: function(options) { + var tmpl = '
'; + if (!options.embedded) { + tmpl += `

${options.title}

`; + } + if (options.global_actions) { + tmpl += '"; + } + if (options.insert) { + tmpl += options.insert; + } + + // add grid filters + tmpl += this.grid_filters(options); + tmpl += "
"; + + // return template + return tmpl; + }, + + // template + header: function(options) { + // start + var tmpl = ""; + + // add checkbox + if (options.show_item_checkboxes) { + tmpl += ""; + if (options.items.length > 0) { + tmpl += + '' + + ''; + } + tmpl += ""; + } + + // create header elements + for (let column of options.columns) { + if (column.visible) { + tmpl += ``; + if (column.href) { + tmpl += `${column.label}`; + } else { + tmpl += column.label; + } + tmpl += `${column.extra}`; + } + } + + // finalize + tmpl += ""; + + // return template + return tmpl; + }, + + // template + body: function(options) { + // initialize + var tmpl = ""; + var num_rows_rendered = 0; + var items_length = options.items.length; + + // empty grid? + if (items_length === 0) { + // No results. + tmpl += 'No Items'; + num_rows_rendered = 1; + } + + // create rows + for (let item of options.items) { + // Tag current + tmpl += "`; + } + + // Data columns + for (let column of options.columns) { + if (column.visible) { + // Nowrap + var nowrap = ""; + if (column.nowrap) { + nowrap = 'style="white-space:nowrap;"'; + } + + // get column settings + var column_settings = item.column_config[column.label]; + + // load attributes + var link = column_settings.link; + var value = column_settings.value; + var target = column_settings.target; + + // unescape value + if (jQuery.type(value) === "string") { + value = value.replace(/\/\//g, "/"); + } + + // Attach popup menu? + var id = ""; + var cls = ""; + if (column.attach_popup) { + id = `grid-${item.encode_id}-popup`; + cls = "menubutton"; + if (link !== "") { + cls += " split"; + } + cls += " popup"; + } + + // Check for row wrapping + tmpl += ``; + + // Link + if (link) { + if (options.operations.length !== 0) { + tmpl += `
`; + } + tmpl += `${value}`; + if (options.operations.length !== 0) { + tmpl += "
"; + } + } else { + tmpl += `
`; + } + tmpl += ""; + } + } + tmpl += ""; + num_rows_rendered++; + } + return tmpl; + }, + + // template + footer: function(options) { + // create template string + var tmpl = ""; + + // paging + if (options.use_paging && options.num_pages > 1) { + // get configuration + var num_page_links = options.num_page_links; + var cur_page_num = options.cur_page_num; + var num_pages = options.num_pages; + + // First pass on min page. + var page_link_range = num_page_links / 2; + var min_page = cur_page_num - page_link_range; + var min_offset = 0; + if (min_page <= 0) { + // Min page is too low. + min_page = 1; + min_offset = page_link_range - (cur_page_num - min_page); + } + + // Set max page. + var max_range = page_link_range + min_offset; + var max_page = cur_page_num + max_range; + var max_offset; + if (max_page <= num_pages) { + // Max page is fine. + max_offset = 0; + } else { + // Max page is too high. + max_page = num_pages; + // +1 to account for the +1 in the loop below. + max_offset = max_range - (max_page + 1 - cur_page_num); + } + + // Second and final pass on min page to add any unused + // offset from max to min. + if (max_offset !== 0) { + min_page -= max_offset; + if (min_page < 1) { + min_page = 1; + } + } + + // template header + tmpl += ''; + if (options.show_item_checkboxes) { + tmpl += ""; + } + tmpl += '' + '' + "Page:"; + + if (min_page > 1) { + tmpl += + '1 ...'; + } + + // create page urls + for (var page_index = min_page; page_index < max_page + 1; page_index++) { + if (page_index == options.cur_page_num) { + tmpl += `${page_index}`; + } else { + tmpl += `${ + page_index + }`; + } + } + + // show last page + if (max_page < num_pages) { + tmpl += `...${ + num_pages + }`; + } + tmpl += ""; + + // Show all link + tmpl += ` + | Show All + + `; + } + + // Grid operations for multiple items. + if (options.show_item_checkboxes) { + // start template + tmpl += ` + + + + + For selected items: + `; + + // configure buttons for operations + for (let operation of options.operations) { + if (operation.allow_multiple) { + tmpl += ` `; + } + } + + // finalize template + tmpl += "" + ""; + } + + // count global operations + var found_global = false; + for (let operation of options.operations) { + if (operation.global_operation) { + found_global = true; + break; + } + } + + // add global operations + if (found_global) { + tmpl += "" + ''; + for (let operation of options.operations) { + if (operation.global_operation) { + tmpl += `${operation.label}`; + } + } + tmpl += "" + ""; + } + + // add legend + if (options.legend) { + tmpl += `${options.legend}`; + } + + // return + return tmpl; + }, + + // template + message: function(options) { + var status = options.status; + if (["success", "ok"].indexOf(status) != -1) { + status = "done"; + } + return `

${_.escape( + options.message + )}

`; + }, + + // template + grid_filters: function(options) { + // get filters + var default_filter_dict = options.default_filter_dict; + var filters = options.filters; + + // show advanced search if flag set or if there are filters for advanced search fields + var advanced_search_display = "none"; + if (options.advanced_search) { + advanced_search_display = "block"; + } + + // identify columns with advanced filtering + var show_advanced_search_link = false; + for (let column of options.columns) { + if (column.filterable == "advanced") { + var column_key = column.key; + var f_key = filters[column_key]; + var d_key = default_filter_dict[column_key]; + if (f_key && d_key && f_key != d_key) { + advanced_search_display = "block"; + } + show_advanced_search_link = true; + } + } + + // hide standard search if advanced is shown + var standard_search_display = "block"; + if (advanced_search_display == "block") { + standard_search_display = "none"; + } + + // + // standard search + // + var tmpl = `"; + + // + // advanced search + // + tmpl += `"; + + // return template + return tmpl; + }, + + // template + grid_column_filter: function(options, column) { + // collect parameters + var filters = options.filters; + var column_label = column.label; + var column_key = column.key; + if (column.filterable == "advanced") { + column_label = column_label.toLowerCase(); + } + + // start + var tmpl = ""; + + if (column.filterable == "advanced") { + tmpl += `${column_label}:`; + } + tmpl += ''; + if (column.is_text) { + tmpl += `
`; + // Carry forward filtering criteria with hidden inputs. + for (let column of options.columns) { + var filter_value = filters[column.key]; + if (filter_value) { + if (filter_value != "All") { + if (column.is_text) { + filter_value = JSON.stringify(filter_value); + } + tmpl += ``; + } + } + } + // Print current filtering criteria and links to delete. + tmpl += ``; + + // add filters + var column_filter = filters[column_key]; + if (column_filter) { + // identify type + var type = jQuery.type(column_filter); + + // single filter value + if (type == "string") { + if (column_filter != "All") { + // append template + tmpl += this.filter_element(column_key, column_filter); + } + } + + // multiple filter values + if (type == "array") { + for (let i in column_filter) { + // copy filters and remove entry + var params = column_filter; + params = params.slice(i); + + // append template + tmpl += this.filter_element(column_key, column_filter[i]); + } + } + } + + // close span + tmpl += ""; + + // Set value, size of search input field. Minimum size is 20 characters. + var value = ""; + var size = 20; + if (column.filterable == "standard") { + value = column.label.toLowerCase(); + if (value.length < 20) { + size = value.length; + } + // +4 to account for space after placeholder + size = size + 4; + } + + // print input field for column + tmpl += ` + + + + +
`; + } else { + // filter criteria + tmpl += ``; + + // add category filters + var seperator = false; + for (var cf_label in options.categorical_filters[column_key]) { + // get category filter + var cf = options.categorical_filters[column_key][cf_label]; + + // each filter will have only a single argument, so get that single argument + var cf_key = ""; + var cf_arg = ""; + for (var key in cf) { + cf_key = key; + cf_arg = cf[key]; + } + + // add seperator + if (seperator) { + tmpl += " | "; + } + seperator = true; + + // add category + var filter = filters[column_key]; + if (filter && cf[column_key] && filter == cf_arg) { + tmpl += `${cf_label}`; + } else { + tmpl += `${ + cf_label + }`; + } + } + tmpl += ""; + } + tmpl += "" + ""; + + // return template + return tmpl; + }, + + // template for filter items + filter_element: function(filter_key, filter_value) { + filter_value = Utils.sanitize(filter_value); + return `${filter_value}`; + } +}; diff --git a/client/galaxy/scripts/reports/grid/grid-view.js b/client/galaxy/scripts/reports/grid/grid-view.js new file mode 100644 index 000000000000..26d66e6a2db5 --- /dev/null +++ b/client/galaxy/scripts/reports/grid/grid-view.js @@ -0,0 +1,677 @@ +// This is necessary so that, when nested arrays are used in ajax/post/get methods, square brackets ('[]') are +// not appended to the identifier of a nested array. +jQuery.ajaxSettings.traditional = true; + +// dependencies +import Utils from "utils/utils"; +import GridModel from "mvc/grid/grid-model"; +import Templates from "mvc/grid/grid-template"; +import PopupMenu from "mvc/ui/popup-menu"; +// grid view +export default Backbone.View.extend({ + // model + grid: null, + + // Initialize + initialize: function(grid_config) { + this.grid = new GridModel(); + this.dict_format = grid_config.dict_format; + this.title = grid_config.title; + var self = this; + window.add_tag_to_grid_filter = (tag_name, tag_value) => { + // Put tag name and value together. + var tag = tag_name + (tag_value !== undefined && tag_value !== "" ? `:${tag_value}` : ""); + var advanced_search = $("#advanced-search").is(":visible"); + if (!advanced_search) { + $("#standard-search").slideToggle("fast"); + $("#advanced-search").slideToggle("fast"); + } + self.add_filter_condition("tags", tag); + }; + + // set element + if (this.dict_format) { + this.setElement("
"); + if (grid_config.url_base && !grid_config.items) { + var url_data = grid_config.url_data || {}; + _.each(grid_config.filters, (v, k) => { + url_data[`f-${k}`] = v; + }); + $.ajax({ + url: `${grid_config.url_base}?${$.param(url_data)}`, + success: function(response) { + response.embedded = grid_config.embedded; + response.filters = grid_config.filters || {}; + self.init_grid(response); + } + }); + } else { + this.init_grid(grid_config); + } + } else { + this.setElement("#grid-container"); + this.init_grid(grid_config); + } + + // fix padding + if (grid_config.use_panels) { + $("#center").css({ + padding: "10px", + overflow: "auto" + }); + } + }, + + // refresh frames + handle_refresh: function(refresh_frames) { + if (refresh_frames) { + if ($.inArray("history", refresh_frames) > -1) { + if (window.top.Galaxy && window.top.Galaxy.currHistoryPanel) { + window.top.Galaxy.currHistoryPanel.loadCurrentHistory(); + } + } + } + }, + + // Initialize + init_grid: function(grid_config) { + this.grid.set(grid_config); + + // get options + var options = this.grid.attributes; + + if (this.allow_title_display && options.title) { + Utils.setWindowTitle(options.title); + } + // handle refresh requests + this.handle_refresh(options.refresh_frames); + + // strip protocol and domain + var url = this.grid.get("url_base"); + url = url.replace(/^.*\/\/[^\/]+/, ""); + this.grid.set("url_base", url); + + // append main template + this.$el.html(Templates.grid(options)); + + // update div contents + this.$el.find("#grid-table-header").html(Templates.header(options)); + this.$el.find("#grid-table-body").html(Templates.body(options)); + this.$el.find("#grid-table-footer").html(Templates.footer(options)); + + // update message + if (options.message) { + this.$el.find("#grid-message").html(Templates.message(options)); + var self = this; + if (options.use_hide_message) { + setTimeout(() => { + self.$el.find("#grid-message").html(""); + }, 5000); + } + } + + // configure elements + this.init_grid_elements(); + this.init_grid_controls(); + + // attach global event handler + // TODO: redundant (the onload/standard page handlers do this) - but needed because these are constructed after page ready + if (window.init_refresh_on_change) { + window.init_refresh_on_change(); + } + }, + + // Initialize grid controls + init_grid_controls: function() { + // link + var self = this; + + // Initialize grid operation button. + this.$el.find(".operation-button").each(function() { + $(this).off(); + $(this).click(function() { + self.submit_operation(this); + return false; + }); + }); + + // Initialize text filters to select text on click and use normal font when user is typing. + this.$el.find("input[type=text]").each(function() { + $(this).off(); + $(this) + .click(function() { + $(this).select(); + }) + .keyup(function() { + $(this).css("font-style", "normal"); + }); + }); + + // Initialize sort links. + this.$el.find(".sort-link").each(function() { + $(this).off(); + $(this).click(function() { + self.set_sort_condition($(this).attr("sort_key")); + return false; + }); + }); + + // Initialize text filters. + this.$el.find(".text-filter-form").each(function() { + $(this).off(); + $(this).submit(function() { + var column_key = $(this).attr("column_key"); + var text_input_obj = $(`#input-${column_key}-filter`); + var text_input = text_input_obj.val(); + text_input_obj.val(""); + self.add_filter_condition(column_key, text_input); + return false; + }); + }); + + // Initialize categorical filters. + this.$el.find(".text-filter-val > a").each(function() { + $(this).off(); + $(this).click(function() { + // Remove visible element. + $(this) + .parent() + .remove(); + + // Remove filter condition. + self.remove_filter_condition($(this).attr("filter_key"), $(this).attr("filter_val")); + + // Return + return false; + }); + }); + + // Initialize categorical filters. + this.$el.find(".categorical-filter > a").each(function() { + $(this).off(); + $(this).click(function() { + self.set_categorical_filter($(this).attr("filter_key"), $(this).attr("filter_val")); + return false; + }); + }); + + // Initialize standard, advanced search toggles. + this.$el.find(".advanced-search-toggle").each(function() { + $(this).off(); + $(this).click(() => { + self.$el.find("#standard-search").slideToggle("fast"); + self.$el.find("#advanced-search").slideToggle("fast"); + return false; + }); + }); + + // Add event to check all box + this.$el.find("#check_all").off(); + this.$el.find("#check_all").on("click", () => { + self.check_all_items(); + }); + }, + + // Initialize grid elements. + init_grid_elements: function() { + // Initialize grid selection checkboxes. + this.$el.find(".grid").each(function() { + var checkboxes = $(this).find("input.grid-row-select-checkbox"); + var check_count = $(this).find("span.grid-selected-count"); + var update_checked = () => { + check_count.text($(checkboxes).filter(":checked").length); + }; + + $(checkboxes).each(function() { + $(this).change(update_checked); + }); + update_checked(); + }); + + // Initialize ratings. + if (this.$el.find(".community_rating_star").length !== 0) this.$el.find(".community_rating_star").rating({}); + + // get options + var options = this.grid.attributes; + var self = this; + + // + // add page click events + // + this.$el.find(".page-link > a").each(function() { + $(this).click(function() { + self.set_page($(this).attr("page_num")); + return false; + }); + }); + + // + // add inbound/outbound events + // + this.$el.find(".use-target").each(function() { + $(this).click(function() { + self.execute({ + href: $(this).attr("href"), + target: $(this).attr("target") + }); + return false; + }); + }); + + // empty grid? + var items_length = options.items.length; + if (items_length === 0) { + return; + } + + // add operation popup menus + _.each(options.items, (item, index) => { + var button = self.$(`#grid-${item.encode_id}-popup`).off(); + var popup = new PopupMenu(button); + _.each(options.operations, operation => { + self._add_operation(popup, operation, item); + }); + }); + }, + + /** Add an operation to the items menu */ + _add_operation: function(popup, operation, item) { + var self = this; + var settings = item.operation_config[operation.label]; + if (settings.allowed && operation.allow_popup) { + popup.addItem({ + html: operation.label, + href: settings.url_args, + target: settings.target, + confirmation_text: operation.confirm, + func: function(e) { + e.preventDefault(); + var label = $(e.target).html(); + if (operation.onclick) { + operation.onclick(item.encode_id); + } else { + self.execute(this.findItemByHtml(label)); + } + } + }); + } + }, + + // Add a condition to the grid filter; this adds the condition and refreshes the grid. + add_filter_condition: function(name, value) { + // Do nothing is value is empty. + if (value === "") { + return false; + } + + // Add condition to grid. + this.grid.add_filter(name, value, true); + + // Add button that displays filter and provides a button to delete it. + var t = $(Templates.filter_element(name, value)); + var self = this; + t.click(function() { + // Remove visible element. + $(this).remove(); + + // Remove filter condition. + self.remove_filter_condition(name, value); + }); + + // append to container + var container = this.$el.find(`#${name}-filtering-criteria`); + container.append(t); + + // execute + this.go_page_one(); + this.execute(); + }, + + // Remove a condition to the grid filter; this adds the condition and refreshes the grid. + remove_filter_condition: function(name, value) { + // Remove filter condition. + this.grid.remove_filter(name, value); + + // Execute + this.go_page_one(); + this.execute(); + }, + + // Set sort condition for grid. + set_sort_condition: function(col_key) { + // Set new sort condition. New sort is col_key if sorting new column; if reversing sort on + // currently sorted column, sort is reversed. + var cur_sort = this.grid.get("sort_key"); + var new_sort = col_key; + if (cur_sort.indexOf(col_key) !== -1) { + // Reverse sort. + if (cur_sort.substring(0, 1) !== "-") { + new_sort = `-${col_key}`; + } + } + + // Remove sort arrows elements. + this.$el.find(".sort-arrow").remove(); + + // Add sort arrow element to new sort column. + var sort_arrow = new_sort.substring(0, 1) == "-" ? "↑" : "↓"; + var t = $(`${sort_arrow}`).addClass("sort-arrow"); + + // Add to header + this.$el.find(`#${col_key}-header`).append(t); + + // Update grid. + this.grid.set("sort_key", new_sort); + this.go_page_one(); + this.execute(); + }, + + // Set new value for categorical filter. + set_categorical_filter: function(name, new_value) { + // Update filter hyperlinks to reflect new filter value. + var category_filter = this.grid.get("categorical_filters")[name]; + + var cur_value = this.grid.get("filters")[name]; + var self = this; + this.$el.find(`.${name}-filter`).each(function() { + var text = $.trim($(this).text()); + var filter = category_filter[text]; + var filter_value = filter[name]; + if (filter_value == new_value) { + // Remove filter link since grid will be using this filter. It is assumed that + // this element has a single child, a hyperlink/anchor with text. + $(this).empty(); + $(this).addClass("current-filter"); + $(this).append(text); + } else if (filter_value == cur_value) { + // Add hyperlink for this filter since grid will no longer be using this filter. It is assumed that + // this element has a single child, a hyperlink/anchor. + $(this).empty(); + var t = $(`${text}`); + t.click(() => { + self.set_categorical_filter(name, filter_value); + }); + $(this).removeClass("current-filter"); + $(this).append(t); + } + }); + + // Update grid. + this.grid.add_filter(name, new_value); + this.go_page_one(); + this.execute(); + }, + + // Set page to view. + set_page: function(new_page) { + // Update page hyperlink to reflect new page. + var self = this; + this.$el.find(".page-link").each(function() { + var id = $(this).attr("id"); + + var // Id has form 'page-link- + page_num = parseInt(id.split("-")[2], 10); + + var cur_page = self.grid.get("cur_page"); + var text; + if (page_num === new_page) { + // Remove link to page since grid will be on this page. It is assumed that + // this element has a single child, a hyperlink/anchor with text. + text = $(this) + .children() + .text(); + $(this).empty(); + $(this).addClass("inactive-link"); + $(this).text(text); + } else if (page_num === cur_page) { + // Add hyperlink to this page since grid will no longer be on this page. It is assumed that + // this element has a single child, a hyperlink/anchor. + text = $(this).text(); + $(this).empty(); + $(this).removeClass("inactive-link"); + var t = $(`${text}`); + t.click(() => { + self.set_page(page_num); + }); + $(this).append(t); + } + }); + + if (new_page === "all") { + this.grid.set("cur_page", new_page); + } else { + this.grid.set("cur_page", parseInt(new_page, 10)); + } + this.execute(); + }, + + // confirmation/submission of operation request + submit_operation: function(operation_button, confirmation_text) { + // identify operation + var operation_name = $(operation_button).val(); + + // verify any item is selected + var number_of_checked_ids = this.$el.find('input[name="id"]:checked').length; + if (number_of_checked_ids < 1) { + return false; + } + + // Check to see if there's grid confirmation text for this operation + var operation = _.findWhere(this.grid.attributes.operations, { + label: operation_name + }); + if (operation && !confirmation_text) { + confirmation_text = operation.confirm || ""; + } + + // collect ids + var item_ids = []; + this.$el.find("input[name=id]:checked").each(function() { + item_ids.push($(this).val()); + }); + + // execute operation + var options = { + operation: operation_name, + id: item_ids, + confirmation_text: confirmation_text + }; + if (operation.target == "top" || operation.target == "center") { + options = _.extend(options, { + href: operation.href, + target: operation.target + }); + } + this.execute(options); + return true; + }, + + check_all_items: function() { + var check = this.$(".grid-row-select-checkbox"); + var state = this.$("#check_all").prop("checked"); + _.each(check, c => { + $(c).prop("checked", state); + }); + this.init_grid_elements(); + }, + + // Go back to page one; this is useful when a filter is applied. + go_page_one: function() { + // Need to go back to page 1 if not showing all. + var cur_page = this.grid.get("cur_page"); + if (cur_page !== null && cur_page !== undefined && cur_page !== "all") { + this.grid.set("cur_page", 1); + } + }, + + // + // execute operations and hyperlink requests + // + execute: function(options) { + // get url + var id = null; + var href = null; + var operation = null; + var confirmation_text = null; + var target = null; + + // check for options + if (options) { + // get options + href = options.href; + operation = options.operation; + id = options.id; + confirmation_text = options.confirmation_text; + target = options.target; + + // check if input contains the operation tag + if (href !== undefined && href.indexOf("operation=") != -1) { + // Get operation, id in hyperlink's href. + var href_parts = href.split("?"); + if (href_parts.length > 1) { + var href_parms_str = href_parts[1]; + var href_parms = href_parms_str.split("&"); + for (var index = 0; index < href_parms.length; index++) { + if (href_parms[index].indexOf("operation") != -1) { + // Found operation parm; get operation value. + operation = href_parms[index].split("=")[1]; + operation = operation.replace(/\+/g, " "); + } else if (href_parms[index].indexOf("id") != -1) { + // Found id parm; get id value. + id = href_parms[index].split("=")[1]; + } + } + } + } + } + + // check for operation details + if (operation && id) { + // show confirmation box + if ( + confirmation_text && + confirmation_text !== "" && + confirmation_text != "None" && + confirmation_text != "null" + ) + if (!confirm(confirmation_text)) return false; + + // use small characters for operation?! + operation = operation.toLowerCase(); + + // Update grid. + this.grid.set({ + operation: operation, + item_ids: id + }); + + // Do operation. If operation cannot be performed asynchronously, redirect to location. + if (target == "top") { + window.top.location = `${href}?${$.param(this.grid.get_url_data())}`; + } else if (target == "center") { + $("#galaxy_main").attr("src", `${href}?${$.param(this.grid.get_url_data())}`); + } else if (this.grid.can_async_op(operation) || this.dict_format) { + this.update_grid(); + } else { + this.go_to(target, href); + } + + // done + return false; + } + + // refresh grid + if (href) { + this.go_to(target, href); + return false; + } + + // refresh grid + if (this.grid.get("async") || this.dict_format) { + this.update_grid(); + } else { + this.go_to(target, href); + } + + // done + return false; + }, + + // go to url + go_to: function(target, href) { + // get aysnc status + var async = this.grid.get("async"); + this.grid.set("async", false); + + // get slide status + var advanced_search = this.$el.find("#advanced-search").is(":visible"); + this.grid.set("advanced_search", advanced_search); + + // get default url + if (!href) { + href = `${this.grid.get("url_base")}?${$.param(this.grid.get_url_data())}`; + } + + // clear grid of transient request attributes. + this.grid.set({ + operation: undefined, + item_ids: undefined, + async: async + }); + switch (target) { + case "center": + $("#galaxy_main").attr("src", href); + break; + case "top": + window.top.location = href; + break; + default: + window.location = href; + } + }, + + // Update grid. + update_grid: function() { + // If there's an operation, do POST; otherwise, do GET. + var method = this.grid.get("operation") ? "POST" : "GET"; + + // Show overlay to indicate loading and prevent user actions. + this.$el.find(".loading-elt-overlay").show(); + var self = this; + $.ajax({ + type: method, + url: self.grid.get("url_base"), + data: self.grid.get_url_data(), + error: function() { + alert("Grid refresh failed"); + }, + success: function(response_text) { + // backup + var embedded = self.grid.get("embedded"); + var insert = self.grid.get("insert"); + var advanced_search = self.$el.find("#advanced-search").is(":visible"); + + // request new configuration + var json = self.dict_format ? response_text : $.parseJSON(response_text); + + // update + json.embedded = embedded; + json.insert = insert; + json.advanced_search = advanced_search; + + // Initialize new grid config + self.init_grid(json); + + // Hide loading overlay. + self.$el.find(".loading-elt-overlay").hide(); + }, + complete: function() { + // Clear grid of transient request attributes. + self.grid.set({ + operation: undefined, + item_ids: undefined + }); + } + }); + } +}); diff --git a/static/scripts/reports/grid/grid-model.js b/static/scripts/reports/grid/grid-model.js new file mode 100644 index 000000000000..f54abdf75cb4 --- /dev/null +++ b/static/scripts/reports/grid/grid-model.js @@ -0,0 +1 @@ +define("reports/grid/grid-model",["exports"],function(t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=Backbone.Model.extend({defaults:{url_base:"",async:!1,async_ops:[],categorical_filters:[],filters:{},sort_key:null,show_item_checkboxes:!1,advanced_search:!1,cur_page:1,num_pages:1,operation:void 0,item_ids:void 0},can_async_op:function(t){return-1!==_.indexOf(this.attributes.async_ops,t)},add_filter:function(t,e,i){if(i){var s,r=this.attributes.filters[t];if(null===r||void 0===r)s=e;else if("string"==typeof r)if("All"==r||r==e)s=e;else{var a=[];a[0]=r,a[1]=e,s=a}else-1===(s=r).indexOf(e)&&s.push(e);this.attributes.filters[t]=s}else this.attributes.filters[t]=e},remove_filter:function(t,e){var i=this.attributes.filters[t];if(null===i||void 0===i)return!1;if("string"==typeof i)this.attributes.filters[t]="";else{var s=_.indexOf(i,e);-1!==s&&(i[s]="")}},get_url_data:function(){var t={async:this.attributes.async,sort:this.attributes.sort_key,page:this.attributes.cur_page,show_item_checkboxes:this.attributes.show_item_checkboxes,advanced_search:this.attributes.advanced_search};this.attributes.operation&&(t.operation=this.attributes.operation),this.attributes.item_ids&&(t.id=this.attributes.item_ids);var e=this;return _.each(_.pairs(e.attributes.filters),function(e){t["f-"+e[0]]=e[1]}),t},get_url:function(t){return this.get("url_base")+"?"+$.param(this.get_url_data())+"&"+$.param(t)}})}); \ No newline at end of file diff --git a/static/scripts/reports/grid/grid-template.js b/static/scripts/reports/grid/grid-template.js new file mode 100644 index 000000000000..cffcd204d382 --- /dev/null +++ b/static/scripts/reports/grid/grid-template.js @@ -0,0 +1 @@ +define("reports/grid/grid-template",["exports","utils/utils"],function(t,e){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=function(t){return t&&t.__esModule?t:{default:t}}(e);t.default={grid:function(t){var e="";return e=t.embedded?this.grid_header(t)+this.grid_table(t):'
'+this.grid_header(t)+'
'+this.grid_table(t),t.info_text&&(e+='
'+t.info_text+"
"),e},grid_table:function(){return'\n
\n \n \n \n \n
\n
'},grid_header:function(t){var e='
';if(t.embedded||(e+="

"+t.title+"

"),t.global_actions){e+='
    ';var a=t.global_actions.length>=3;a&&(e+='
  • Actions
  • ');var r=!0,l=!1,i=void 0;try{for(var n,o=t.global_actions[Symbol.iterator]();!(r=(n=o.next()).done);r=!0){var s=n.value;e+='
  • '+s.label+"
  • "}}catch(t){l=!0,i=t}finally{try{!r&&o.return&&o.return()}finally{if(l)throw i}}a&&(e+="
    "),e+="
"}return t.insert&&(e+=t.insert),e+=this.grid_filters(t),e+="
"},header:function(t){var e="";t.show_item_checkboxes&&(e+="",t.items.length>0&&(e+=''),e+="");var a=!0,r=!1,l=void 0;try{for(var i,n=t.columns[Symbol.iterator]();!(a=(i=n.next()).done);a=!0){var o=i.value;o.visible&&(e+='',o.href?e+=''+o.label+"":e+=o.label,e+=''+o.extra+"")}}catch(t){r=!0,l=t}finally{try{!a&&n.return&&n.return()}finally{if(r)throw l}}return e+=""},body:function(t){var e="";0===t.items.length&&(e+='No Items');var a=!0,r=!1,l=void 0;try{for(var i,n=t.items[Symbol.iterator]();!(a=(i=n.next()).done);a=!0){var o=i.value;e+="",t.show_item_checkboxes&&(e+='');var s=!0,d=!1,c=void 0;try{for(var f,u=t.columns[Symbol.iterator]();!(s=(f=u.next()).done);s=!0){var p=f.value;if(p.visible){var v="";p.nowrap&&(v='style="white-space:nowrap;"');var g=o.column_config[p.label],h=g.link,b=g.value,y=g.target;"string"===jQuery.type(b)&&(b=b.replace(/\/\//g,"/"));var m="",_="";p.attach_popup&&(m="grid-"+o.encode_id+"-popup",_="menubutton",""!==h&&(_+=" split"),_+=" popup"),e+="",h?(0!==t.operations.length&&(e+='
'),e+=''+b+"",0!==t.operations.length&&(e+="
")):e+='
",e+=""}}}catch(t){d=!0,c=t}finally{try{!s&&u.return&&u.return()}finally{if(d)throw c}}e+="",0}}catch(t){r=!0,l=t}finally{try{!a&&n.return&&n.return()}finally{if(r)throw l}}return e},footer:function(t){var e="";if(t.use_paging&&t.num_pages>1){var a=t.num_page_links,r=t.cur_page_num,l=t.num_pages,i=a/2,n=r-i,o=0;n<=0&&(o=i-(r-(n=1)));var s,d=i+o,c=r+d;0!==(s=c<=l?0:d-((c=l)+1-r))&&(n-=s)<1&&(n=1),e+='',t.show_item_checkboxes&&(e+=""),e+='Page:',n>1&&(e+='1 ...');for(var f=n;f'+f+"":e+=''+f+"";c'+l+""),e+="",e+='\n | Show All\n \n '}if(t.show_item_checkboxes){e+='\n \n \n \n \n For selected items: \n ';var u=!0,p=!1,v=void 0;try{for(var g,h=t.operations[Symbol.iterator]();!(u=(g=h.next()).done);u=!0){var b=g.value;b.allow_multiple&&(e+=' ')}}catch(t){p=!0,v=t}finally{try{!u&&h.return&&h.return()}finally{if(p)throw v}}e+=""}var y=!1,m=!0,_=!1,k=void 0;try{for(var x,w=t.operations[Symbol.iterator]();!(m=(x=w.next()).done);m=!0)if(x.value.global_operation){y=!0;break}}catch(t){_=!0,k=t}finally{try{!m&&w.return&&w.return()}finally{if(_)throw k}}if(y){e+='';var S=!0,j=!1,A=void 0;try{for(var C,O=t.operations[Symbol.iterator]();!(S=(C=O.next()).done);S=!0){var P=C.value;P.global_operation&&(e+=''+P.label+"")}}catch(t){j=!0,A=t}finally{try{!S&&O.return&&O.return()}finally{if(j)throw A}}e+=""}return t.legend&&(e+=''+t.legend+""),e},message:function(t){var e=t.status;return-1!=["success","ok"].indexOf(e)&&(e="done"),'

'+_.escape(t.message)+'

'},grid_filters:function(t){var e=t.default_filter_dict,a=t.filters,r="none";t.advanced_search&&(r="block");var l=!1,i=!0,n=!1,o=void 0;try{for(var s,d=t.columns[Symbol.iterator]();!(i=(s=d.next()).done);i=!0){var c=s.value;if("advanced"==c.filterable){var f=c.key,u=a[f],p=e[f];u&&p&&u!=p&&(r="block"),l=!0}}}catch(t){n=!0,o=t}finally{try{!i&&d.return&&d.return()}finally{if(n)throw o}}var v="block";"block"==r&&(v="none");var g='",g+='"},grid_column_filter:function(t,e){var a=t.filters,r=e.label,l=e.key;"advanced"==e.filterable&&(r=r.toLowerCase());var i="";if("advanced"==e.filterable&&(i+=''+r+":"),i+='',e.is_text){i+='
';var n=!0,o=!1,s=void 0;try{for(var d,c=t.columns[Symbol.iterator]();!(n=(d=c.next()).done);n=!0){var f=d.value,u=a[f.key];u&&"All"!=u&&(f.is_text&&(u=JSON.stringify(u)),i+='')}}catch(t){o=!0,s=t}finally{try{!n&&c.return&&c.return()}finally{if(o)throw s}}i+='';var p=a[l];if(p){var v=jQuery.type(p);if("string"==v&&"All"!=p&&(i+=this.filter_element(l,p)),"array"==v)for(var g in p){var h=p;h=h.slice(g),i+=this.filter_element(l,p[g])}}i+="";var b="",y=20;"standard"==e.filterable&&((b=e.label.toLowerCase()).length<20&&(y=b.length),y+=4),i+='\n \n \n \n \n
'}else{i+='';var m=!1;for(var _ in t.categorical_filters[l]){var k=t.categorical_filters[l][_],x="",w="";for(var S in k)x=S,w=k[S];m&&(i+=" | "),m=!0;var j=a[l];j&&k[l]&&j==w?i+=''+_+"":i+=''+_+""}i+=""}return i+=""},filter_element:function(t,e){return''+(e=a.default.sanitize(e))+''}}}); \ No newline at end of file diff --git a/static/scripts/reports/grid/grid-view.js b/static/scripts/reports/grid/grid-view.js new file mode 100644 index 000000000000..0064bf089625 --- /dev/null +++ b/static/scripts/reports/grid/grid-view.js @@ -0,0 +1 @@ +define("reports/grid/grid-view",["exports","utils/utils","mvc/grid/grid-model","mvc/grid/grid-template","mvc/ui/popup-menu"],function(t,i,e,r,n){"use strict";function a(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(t,"__esModule",{value:!0});var s=a(i),o=a(e),d=a(r),l=a(n);jQuery.ajaxSettings.traditional=!0,t.default=Backbone.View.extend({grid:null,initialize:function(t){this.grid=new o.default,this.dict_format=t.dict_format,this.title=t.title;var i=this;if(window.add_tag_to_grid_filter=function(t,e){var r=t+(void 0!==e&&""!==e?":"+e:"");$("#advanced-search").is(":visible")||($("#standard-search").slideToggle("fast"),$("#advanced-search").slideToggle("fast")),i.add_filter_condition("tags",r)},this.dict_format)if(this.setElement("
"),t.url_base&&!t.items){var e=t.url_data||{};_.each(t.filters,function(t,i){e["f-"+i]=t}),$.ajax({url:t.url_base+"?"+$.param(e),success:function(e){e.embedded=t.embedded,e.filters=t.filters||{},i.init_grid(e)}})}else this.init_grid(t);else this.setElement("#grid-container"),this.init_grid(t);t.use_panels&&$("#center").css({padding:"10px",overflow:"auto"})},handle_refresh:function(t){t&&$.inArray("history",t)>-1&&window.top.Galaxy&&window.top.Galaxy.currHistoryPanel&&window.top.Galaxy.currHistoryPanel.loadCurrentHistory()},init_grid:function(t){this.grid.set(t);var i=this.grid.attributes;this.allow_title_display&&i.title&&s.default.setWindowTitle(i.title),this.handle_refresh(i.refresh_frames);var e=this.grid.get("url_base");if(e=e.replace(/^.*\/\/[^\/]+/,""),this.grid.set("url_base",e),this.$el.html(d.default.grid(i)),this.$el.find("#grid-table-header").html(d.default.header(i)),this.$el.find("#grid-table-body").html(d.default.body(i)),this.$el.find("#grid-table-footer").html(d.default.footer(i)),i.message){this.$el.find("#grid-message").html(d.default.message(i));var r=this;i.use_hide_message&&setTimeout(function(){r.$el.find("#grid-message").html("")},5e3)}this.init_grid_elements(),this.init_grid_controls(),window.init_refresh_on_change&&window.init_refresh_on_change()},init_grid_controls:function(){var t=this;this.$el.find(".operation-button").each(function(){$(this).off(),$(this).click(function(){return t.submit_operation(this),!1})}),this.$el.find("input[type=text]").each(function(){$(this).off(),$(this).click(function(){$(this).select()}).keyup(function(){$(this).css("font-style","normal")})}),this.$el.find(".sort-link").each(function(){$(this).off(),$(this).click(function(){return t.set_sort_condition($(this).attr("sort_key")),!1})}),this.$el.find(".text-filter-form").each(function(){$(this).off(),$(this).submit(function(){var i=$(this).attr("column_key"),e=$("#input-"+i+"-filter"),r=e.val();return e.val(""),t.add_filter_condition(i,r),!1})}),this.$el.find(".text-filter-val > a").each(function(){$(this).off(),$(this).click(function(){return $(this).parent().remove(),t.remove_filter_condition($(this).attr("filter_key"),$(this).attr("filter_val")),!1})}),this.$el.find(".categorical-filter > a").each(function(){$(this).off(),$(this).click(function(){return t.set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val")),!1})}),this.$el.find(".advanced-search-toggle").each(function(){$(this).off(),$(this).click(function(){return t.$el.find("#standard-search").slideToggle("fast"),t.$el.find("#advanced-search").slideToggle("fast"),!1})}),this.$el.find("#check_all").off(),this.$el.find("#check_all").on("click",function(){t.check_all_items()})},init_grid_elements:function(){this.$el.find(".grid").each(function(){var t=$(this).find("input.grid-row-select-checkbox"),i=$(this).find("span.grid-selected-count"),e=function(){i.text($(t).filter(":checked").length)};$(t).each(function(){$(this).change(e)}),e()}),0!==this.$el.find(".community_rating_star").length&&this.$el.find(".community_rating_star").rating({});var t=this.grid.attributes,i=this;this.$el.find(".page-link > a").each(function(){$(this).click(function(){return i.set_page($(this).attr("page_num")),!1})}),this.$el.find(".use-target").each(function(){$(this).click(function(){return i.execute({href:$(this).attr("href"),target:$(this).attr("target")}),!1})}),0!==t.items.length&&_.each(t.items,function(e,r){var n=i.$("#grid-"+e.encode_id+"-popup").off(),a=new l.default(n);_.each(t.operations,function(t){i._add_operation(a,t,e)})})},_add_operation:function(t,i,e){var r=this,n=e.operation_config[i.label];n.allowed&&i.allow_popup&&t.addItem({html:i.label,href:n.url_args,target:n.target,confirmation_text:i.confirm,func:function(t){t.preventDefault();var n=$(t.target).html();i.onclick?i.onclick(e.encode_id):r.execute(this.findItemByHtml(n))}})},add_filter_condition:function(t,i){if(""===i)return!1;this.grid.add_filter(t,i,!0);var e=$(d.default.filter_element(t,i)),r=this;e.click(function(){$(this).remove(),r.remove_filter_condition(t,i)}),this.$el.find("#"+t+"-filtering-criteria").append(e),this.go_page_one(),this.execute()},remove_filter_condition:function(t,i){this.grid.remove_filter(t,i),this.go_page_one(),this.execute()},set_sort_condition:function(t){var i=this.grid.get("sort_key"),e=t;-1!==i.indexOf(t)&&"-"!==i.substring(0,1)&&(e="-"+t),this.$el.find(".sort-arrow").remove();var r="-"==e.substring(0,1)?"↑":"↓",n=$(""+r+"").addClass("sort-arrow");this.$el.find("#"+t+"-header").append(n),this.grid.set("sort_key",e),this.go_page_one(),this.execute()},set_categorical_filter:function(t,i){var e=this.grid.get("categorical_filters")[t],r=this.grid.get("filters")[t],n=this;this.$el.find("."+t+"-filter").each(function(){var a=$.trim($(this).text()),s=e[a][t];if(s==i)$(this).empty(),$(this).addClass("current-filter"),$(this).append(a);else if(s==r){$(this).empty();var o=$(''+a+"");o.click(function(){n.set_categorical_filter(t,s)}),$(this).removeClass("current-filter"),$(this).append(o)}}),this.grid.add_filter(t,i),this.go_page_one(),this.execute()},set_page:function(t){var i=this;this.$el.find(".page-link").each(function(){var e,r=$(this).attr("id"),n=parseInt(r.split("-")[2],10),a=i.grid.get("cur_page");if(n===t)e=$(this).children().text(),$(this).empty(),$(this).addClass("inactive-link"),$(this).text(e);else if(n===a){e=$(this).text(),$(this).empty(),$(this).removeClass("inactive-link");var s=$(''+e+"");s.click(function(){i.set_page(n)}),$(this).append(s)}}),"all"===t?this.grid.set("cur_page",t):this.grid.set("cur_page",parseInt(t,10)),this.execute()},submit_operation:function(t,i){var e=$(t).val();if(this.$el.find('input[name="id"]:checked').length<1)return!1;var r=_.findWhere(this.grid.attributes.operations,{label:e});r&&!i&&(i=r.confirm||"");var n=[];this.$el.find("input[name=id]:checked").each(function(){n.push($(this).val())});var a={operation:e,id:n,confirmation_text:i};return"top"!=r.target&&"center"!=r.target||(a=_.extend(a,{href:r.href,target:r.target})),this.execute(a),!0},check_all_items:function(){var t=this.$(".grid-row-select-checkbox"),i=this.$("#check_all").prop("checked");_.each(t,function(t){$(t).prop("checked",i)}),this.init_grid_elements()},go_page_one:function(){var t=this.grid.get("cur_page");null!==t&&void 0!==t&&"all"!==t&&this.grid.set("cur_page",1)},execute:function(t){var i=null,e=null,r=null,n=null,a=null;if(t&&(e=t.href,r=t.operation,i=t.id,n=t.confirmation_text,a=t.target,void 0!==e&&-1!=e.indexOf("operation="))){var s=e.split("?");if(s.length>1)for(var o=s[1].split("&"),d=0;d