commit/galaxy-central: guerler: Grids: Reduce mako templating to json generation, introduce GridsView, bug fixes
1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/f1cef709a9e5/ Changeset: f1cef709a9e5 User: guerler Date: 2013-11-17 11:08:25 Summary: Grids: Reduce mako templating to json generation, introduce GridsView, bug fixes iG: changed templates/webapps/galaxy/tracks/history_select_grid.mako Affected #: 14 files diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 lib/galaxy/web/framework/helpers/grids.py --- a/lib/galaxy/web/framework/helpers/grids.py +++ b/lib/galaxy/web/framework/helpers/grids.py @@ -321,7 +321,7 @@ class GridColumn( object ): def __init__( self, label, key=None, model_class=None, method=None, format=None, \ - link=None, attach_popup=False, visible=True, ncells=1, nowrap=False, \ + link=None, attach_popup=False, visible=True, nowrap=False, \ # Valid values for filterable are ['standard', 'advanced', None] filterable=None, sortable=True, label_id_prefix=None ): """Create a grid column.""" @@ -334,7 +334,6 @@ self.nowrap = nowrap self.attach_popup = attach_popup self.visible = visible - self.ncells = ncells self.filterable = filterable # Column must have a key to be sortable. self.sortable = ( self.key is not None and sortable ) diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 lib/galaxy/webapps/galaxy/controllers/dataset.py --- a/lib/galaxy/webapps/galaxy/controllers/dataset.py +++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py @@ -100,7 +100,7 @@ grids.TextColumn( "Name", key="name", # Link name to dataset's history. link=( lambda item: iff( item.history.deleted, None, dict( operation="switch", id=item.id ) ) ), filterable="advanced", attach_popup=True ), - HistoryColumn( "History", key="history", + HistoryColumn( "History", key="history", sortable=False, link=( lambda item: iff( item.history.deleted, None, dict( operation="switch_history", id=item.id ) ) ) ), grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.HistoryDatasetAssociationTagAssociation, filterable="advanced", grid_name="HistoryDatasetAssocationListGrid" ), StatusColumn( "Status", key="deleted", attach_popup=False ), @@ -113,11 +113,12 @@ key="free-text-search", visible=False, filterable="standard" ) ) operations = [ - grids.GridOperation( "Copy to current history", condition=( lambda item: not item.deleted ), async_compatible=False ), + grids.GridOperation( "Copy to current history", condition=( lambda item: not item.deleted ), async_compatible=True ), ] standard_filters = [] default_filter = dict( name="All", deleted="False", tags="All" ) preserve_state = False + use_async = True use_paging = True num_rows_per_page = 50 def build_initial_query( self, trans, **kwargs ): diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 lib/galaxy/webapps/galaxy/controllers/history.py --- a/lib/galaxy/webapps/galaxy/controllers/history.py +++ b/lib/galaxy/webapps/galaxy/controllers/history.py @@ -30,16 +30,13 @@ def get_value( self, trans, grid, history ): state_count_dict = self.get_hda_state_counts( trans, history ) - rval = [] + rval = '' for state in ( 'ok', 'running', 'queued', 'error' ): count = state_count_dict.get( state, 0 ) if count: - rval.append( '<div class="count-box state-color-%s">%s</div>' % ( state, count ) ) - else: - rval.append( '' ) + rval += '<div class="count-box state-color-%s">%s</div>' % (state, count) return rval - class HistoryListNameColumn( NameColumn ): def get_link( self, trans, grid, history ): link = None @@ -72,7 +69,7 @@ default_sort_key = "-update_time" columns = [ HistoryListNameColumn( "Name", key="name", attach_popup=True, filterable="advanced" ), - DatasetsByStateColumn( "Datasets", key="datasets_by_state", ncells=4, sortable=False ), + DatasetsByStateColumn( "Datasets", key="datasets_by_state", sortable=False ), grids.IndividualTagsColumn( "Tags", key="tags", model_tag_association_class=model.HistoryTagAssociation, \ filterable="advanced", grid_name="HistoryListGrid" ), grids.SharingStatusColumn( "Sharing", key="sharing", filterable="advanced", sortable=False ), @@ -118,13 +115,11 @@ # Custom column types class DatasetsByStateColumn( grids.GridColumn ): def get_value( self, trans, grid, history ): - rval = [] + rval = '' for state in ( 'ok', 'running', 'queued', 'error' ): total = sum( 1 for d in history.active_datasets if d.state == state ) if total: - rval.append( '<div class="count-box state-color-%s">%s</div>' % ( state, total ) ) - else: - rval.append( '' ) + rval += '<div class="count-box state-color-%s">%s</div>' % ( state, total ) return rval class SharedByColumn( grids.GridColumn ): @@ -138,7 +133,7 @@ default_filter = {} columns = [ grids.GridColumn( "Name", key="name", attach_popup=True ), # link=( lambda item: dict( operation="View", id=item.id ) ), attach_popup=True ), - DatasetsByStateColumn( "Datasets", ncells=4, sortable=False ), + DatasetsByStateColumn( "Datasets", sortable=False ), grids.GridColumn( "Created", key="create_time", format=time_ago ), grids.GridColumn( "Last Updated", key="update_time", format=time_ago ), SharedByColumn( "Shared by", key="user_id" ) diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 lib/galaxy/webapps/galaxy/controllers/visualization.py --- a/lib/galaxy/webapps/galaxy/controllers/visualization.py +++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py @@ -147,11 +147,11 @@ # Grid definition. title = "Insert into visualization" template = "/tracks/add_to_viz.mako" - async_template = "/page/select_items_grid_async.mako" model_class = model.Visualization default_sort_key = "-update_time" use_async = True use_paging = False + show_item_checkboxes = True columns = [ grids.TextColumn( "Title", key="title", model_class=model.Visualization, filterable="standard" ), grids.TextColumn( "Dbkey", key="dbkey", model_class=model.Visualization ), diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 static/scripts/galaxy.grids.js --- a/static/scripts/galaxy.grids.js +++ b/static/scripts/galaxy.grids.js @@ -119,433 +119,456 @@ } // Add filter arguments to data, placing "f-" in front of all arguments. - // FIXME: when underscore updated, use pairs function(). var self = this; - _.each(_.keys(self.attributes.filters), function(k) { - url_data['f-' + k] = self.attributes.filters[k]; + _.each(_.pairs(self.attributes.filters), function(k) { + url_data['f-' + k[0]] = k[1]; }); return url_data; } }); -// -// Code to handle grid operations: filtering, sorting, paging, and operations. -// +// grid view +var GridView = Backbone.View.extend({ -// Initialize grid controls -function init_grid_controls() { - // Initialize submit image elements. - $('.submit-image').each( function() { - // On mousedown, add class to simulate click. - $(this).mousedown( function() { - $(this).addClass('gray-background'); + // model + grid: null, + + // Initialize + initialize: function(grid) + { + this.grid = grid; + this.init_grid_elements(); + this.init_grid_controls(); + + // Initialize text filters to select text on click and use normal font when user is typing. + $('input[type=text]').each(function() { + $(this).click(function() { $(this).select(); } ) + .keyup(function () { $(this).css("font-style", "normal"); }); + }); + }, + + // Initialize grid controls + init_grid_controls: function() { + // Initialize submit image elements. + $('.submit-image').each( function() { + // On mousedown, add class to simulate click. + $(this).mousedown( function() { + $(this).addClass('gray-background'); + }); + + // On mouseup, add class to simulate click. + $(this).mouseup( function() { + $(this).removeClass('gray-background'); + }); }); - // On mouseup, add class to simulate click. - $(this).mouseup( function() { - $(this).removeClass('gray-background'); + // link + var self = this; + + // Initialize sort links. + $('.sort-link').each( function() { + $(this).click( function() { + self.set_sort_condition( $(this).attr('sort_key') ); + return false; + }); }); - }); + + // Initialize page links. + $('.page-link > a').each( function() { + $(this).click( function() { + self.set_page( $(this).attr('page_num') ); + return false; + }); + }); + + // Initialize categorical filters. + $('.categorical-filter > a').each( function() { + $(this).click( function() { + self.set_categorical_filter( $(this).attr('filter_key'), $(this).attr('filter_val') ); + return false; + }); + }); + + // Initialize text filters. + $('.text-filter-form').each( function() { + $(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, true); + return false; + }); + }); + + // Initialize autocomplete for text inputs in search UI. + var t = $("#input-tags-filter"); + if (t.length) { + t.autocomplete(this.grid.history_tag_autocomplete_url, + { selectFirst: false, autoFill: false, highlight: false, mustMatch: false }); + } + + var t2 = $("#input-name-filter"); + if (t2.length) { + t2.autocomplete(this.grid.history_name_autocomplete_url, + { selectFirst: false, autoFill: false, highlight: false, mustMatch: false }); + } + + // Initialize standard, advanced search toggles. + $('.advanced-search-toggle').each( function() { + $(this).click( function() { + $("#standard-search").slideToggle('fast'); + $('#advanced-search').slideToggle('fast'); + return false; + }); + }); + }, + + // Initialize grid elements. + init_grid_elements : function() { - // Initialize sort links. - $('.sort-link').each( function() { - $(this).click( function() { - set_sort_condition( $(this).attr('sort_key') ); - return false; + // Initialize grid selection checkboxes. + $(".grid").each( function() { + var checkboxes = $(this).find("input.grid-row-select-checkbox"); + var check_count = $(this).find("span.grid-selected-count"); + var update_checked = function() { + check_count.text( $(checkboxes).filter(":checked").length ); + }; + + $(checkboxes).each( function() { + $(this).change(update_checked); + }); + update_checked(); }); - }); - - // Initialize page links. - $('.page-link > a').each( function() { - $(this).click( function() { - set_page( $(this).attr('page_num') ); - return false; + + // Initialize item labels. + var self = this; + $(".label").each( function() { + // If href has an operation in it, do operation when clicked. Otherwise do nothing. + var href = $(this).attr('href'); + if ( href !== undefined && href.indexOf('operation=') != -1 ) { + $(this).click( function() { + self.do_operation_from_href( $(this).attr('href') ); + return false; + }); + } }); - }); + + // Initialize ratings. + if ($('.community_rating_star').length !== 0) + $('.community_rating_star').rating({}); + + // Initialize item menu operations. + make_popup_menus(); + }, - // Initialize categorical filters. - $('.categorical-filter > a').each( function() { - $(this).click( function() { - set_categorical_filter( $(this).attr('filter_key'), $(this).attr('filter_val') ); + // 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); + } + }, + + // Add a condition to the grid filter; this adds the condition and refreshes the grid. + add_filter_condition: function (name, value, append) { + // Do nothing is value is empty. + if (value === "") { return false; + } + + // Add condition to grid. + this.grid.add_filter(name, value, append); + + // Add button that displays filter and provides a button to delete it. + var t = $("<span>" + value + "<a href='javascript:void(0);'><span class='delete-search-icon' /></a></span>"); + t.addClass('text-filter-val'); + var self = this; + t.click(function() { + // Remove filter condition. + self.grid.remove_filter(name, value); + + // Remove visible element. + $(this).remove(); + + self.go_page_one(); + self.update_grid(); }); - }); - - // Initialize text filters. - $('.text-filter-form').each( function() { - $(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(''); - add_filter_condition(column_key, text_input, true); + + var container = $('#' + name + "-filtering-criteria"); + container.append(t); + + this.go_page_one(); + this.update_grid(); + }, + + // 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; + } else { + // Sort reversed by using just col_key. + } + } + + // Remove sort arrows elements. + $('.sort-arrow').remove(); + + // Add sort arrow element to new sort column. + var sort_arrow = (new_sort.substring(0,1) == '-') ? "↑" : "↓"; + var t = $("<span>" + sort_arrow + "</span>").addClass('sort-arrow'); + var th = $("#" + col_key + '-header'); + th.append(t); + + // Update grid. + this.grid.set('sort_key', new_sort); + this.go_page_one(); + this.update_grid(); + }, + + // 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], + cur_value = this.grid.get('filters')[name]; + var self = this; + $("." + 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 = $("<a href='#'>" + text + "</a>"); + t.click(function() { + 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.update_grid(); + }, + + // Set page to view. + set_page: function (new_page) { + // Update page hyperlink to reflect new page. + var self = this; + $(".page-link").each( function() { + var id = $(this).attr('id'), + page_num = parseInt( id.split("-")[2], 10 ), // Id has form 'page-link-<page_num> + cur_page = self.grid.get('cur_page'), + 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 = $("<a href='#'>" + text + "</a>"); + t.click(function() { + set_page(page_num); + }); + $(this).append(t); + } + }); + + var maintain_page_links = true; + if (new_page === "all") { + this.grid.set('cur_page', new_page); + maintain_page_links = false; + } else { + this.grid.set('cur_page', parseInt(new_page, 10)); + } + this.update_grid(maintain_page_links); + }, + + // Perform a grid operation. + do_operation: function (operation, item_ids) { + operation = operation.toLowerCase(); + + // Update grid. + this.grid.set({ + operation: operation, + item_ids: item_ids + }); + + // Do operation. If operation cannot be performed asynchronously, redirect to location. + if (this.grid.can_async_op(operation)) { + this.update_grid(true); + } + else { + this.go_to_URL(); + } + }, + + // Perform a hyperlink click that initiates an operation. If there is no operation, ignore click. + do_operation_from_href: function (href) { + // 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("&"); + var operation = null; + var id = -1; + 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]; + } else if (href_parms[index].indexOf('id') != -1) { + // Found id parm; get id value. + id = href_parms[index].split('=')[1]; + } + } + // Do operation. + this.do_operation(operation, id); return false; + } + }, + + // Navigate window to the URL defined by url_args. This method can be used to short-circuit grid AJAXing. + go_to_URL: function () { + // Not async request. + this.grid.set('async', false); + + // Go. + window.location = 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 }); - }); - - // Initialize autocomplete for text inputs in search UI. - var t = $("#input-tags-filter"); - if (t.length) { - t.autocomplete(history_tag_autocomplete_url, - { selectFirst: false, autoFill: false, highlight: false, mustMatch: false }); - } - - var t2 = $("#input-name-filter"); - if (t2.length) { - t2.autocomplete(history_name_autocomplete_url, - { selectFirst: false, autoFill: false, highlight: false, mustMatch: false }); - } - - // Initialize standard, advanced search toggles. - $('.advanced-search-toggle').each( function() { - $(this).click( function() { - $("#standard-search").slideToggle('fast'); - $('#advanced-search').slideToggle('fast'); - return false; - }); - }); -} - -// Initialize grid elements. -function init_grid_elements() { - // Initialize grid selection checkboxes. - $(".grid").each( function() { - var checkboxes = $(this).find("input.grid-row-select-checkbox"); - var check_count = $(this).find("span.grid-selected-count"); - var update_checked = function() { - check_count.text( $(checkboxes).filter(":checked").length ); - }; - - $(checkboxes).each( function() { - $(this).change(update_checked); - }); - update_checked(); - }); - - // Initialize item labels. - $(".label").each( function() { - // If href has an operation in it, do operation when clicked. Otherwise do nothing. - var href = $(this).attr('href'); - if ( href !== undefined && href.indexOf('operation=') != -1 ) { - $(this).click( function() { - do_operation_from_href( $(this).attr('href') ); - return false; - }); - } - }); - - // Initialize ratings. - if ($('.community_rating_star').length !== 0) - $('.community_rating_star').rating({}); - - // Initialize item menu operations. - make_popup_menus(); -} - -// Go back to page one; this is useful when a filter is applied. -function go_page_one() { - // Need to go back to page 1 if not showing all. - var cur_page = grid.get('cur_page'); - if (cur_page !== null && cur_page !== undefined && cur_page !== 'all') { - grid.set('cur_page', 1); - } -} - -// Add a condition to the grid filter; this adds the condition and refreshes the grid. -function add_filter_condition(name, value, append) { - // Do nothing is value is empty. - if (value === "") { - return false; - } - - // Add condition to grid. - grid.add_filter(name, value, append); - - // Add button that displays filter and provides a button to delete it. - var t = $("<span>" + value + "<a href='javascript:void(0);'><span class='delete-search-icon' /></a></span>"); - t.addClass('text-filter-val'); - t.click(function() { - // Remove filter condition. - grid.remove_filter(name, value); - - // Remove visible element. - $(this).remove(); - - go_page_one(); - update_grid(); - }); - - var container = $('#' + name + "-filtering-criteria"); - container.append(t); - - go_page_one(); - update_grid(); -} - -// Add tag to grid filter. -function 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 : ""); - $('#advanced-search').show('fast'); - add_filter_condition("tags", tag, true); -} - -// Set sort condition for grid. -function set_sort_condition(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 = 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; - } else { - // Sort reversed by using just col_key. - } - } - - // Remove sort arrows elements. - $('.sort-arrow').remove(); - - // Add sort arrow element to new sort column. - var sort_arrow = (new_sort.substring(0,1) == '-') ? "↑" : "↓"; - var t = $("<span>" + sort_arrow + "</span>").addClass('sort-arrow'); - var th = $("#" + col_key + '-header'); - th.append(t); - - // Update grid. - grid.set('sort_key', new_sort); - go_page_one(); - update_grid(); -} - -// Set new value for categorical filter. -function set_categorical_filter(name, new_value) { - // Update filter hyperlinks to reflect new filter value. - var category_filter = grid.get('categorical_filters')[name], - cur_value = grid.get('filters')[name]; - $("." + 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 = $("<a href='#'>" + text + "</a>"); - t.click(function() { - set_categorical_filter( name, filter_value ); - }); - $(this).removeClass("current-filter"); - $(this).append(t); - } - }); - - // Update grid. - grid.add_filter(name, new_value); - go_page_one(); - update_grid(); -} - -// Set page to view. -function set_page(new_page) { - // Update page hyperlink to reflect new page. - $(".page-link").each( function() { - var id = $(this).attr('id'), - page_num = parseInt( id.split("-")[2], 10 ), // Id has form 'page-link-<page_num> - cur_page = grid.get('cur_page'), - 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 = $("<a href='#'>" + text + "</a>"); - t.click(function() { - set_page(page_num); - }); - $(this).append(t); - } - }); - - var maintain_page_links = true; - if (new_page === "all") { - grid.set('cur_page', new_page); - maintain_page_links = false; - } else { - grid.set('cur_page', parseInt(new_page, 10)); - } - update_grid(maintain_page_links); -} - -// Perform a grid operation. -function do_operation(operation, item_ids) { - operation = operation.toLowerCase(); + }, // Update grid. - grid.set({ - operation: operation, - item_ids: item_ids - }); - - // Do operation. If operation cannot be performed asynchronously, redirect to location. - if (grid.can_async_op(operation)) { - update_grid(true); - } - else { - go_to_URL(); - } -} + update_grid: function (maintain_page_links) { + // If grid is not using async, then go to URL. + if (!this.grid.get('async')) { + this.go_to_URL(); + return; + } -// Perform a hyperlink click that initiates an operation. If there is no operation, ignore click. -function do_operation_from_href(href) { - // 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("&"); - var operation = null; - var id = -1; - 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]; - } else if (href_parms[index].indexOf('id') != -1) { - // Found id parm; get id value. - id = href_parms[index].split('=')[1]; + // If there's an operation, do POST; otherwise, do GET. + var method = (this.grid.get('operation') ? "POST" : "GET" ); + $('.loading-elt-overlay').show(); // Show overlay to indicate loading and prevent user actions. + 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) { + // HACK: use a simple string to separate the elements in the + // response: (1) table body; (2) number of pages in table; and (3) message. + var parsed_response_text = response_text.split("*****"); + + // Update grid body and footer. + $('#grid-table-body').html(parsed_response_text[0]); + // FIXME: this does not work at all; what's needed is a function + // that updates page links when number of pages changes. + $('#grid-table-footer').html(parsed_response_text[1]); + + // Trigger custom event to indicate grid body has changed. + $('#grid-table-body').trigger('update'); + + // Init grid. + self.init_grid_elements(); + make_popup_menus(); + + // Hide loading overlay. + $('.loading-elt-overlay').hide(); + + // Show message if there is one. + var message = $.trim( parsed_response_text[2] ); + if (message !== "") { + $('#grid-message').html( message ).show(); + setTimeout( function() { $('#grid-message').hide(); }, 5000); + } + }, + complete: function() { + // Clear grid of transient request attributes. + self.grid.set({ + operation: undefined, + item_ids: undefined + }); + } + }); + }, + + check_all_items: function () { + var chk_all = document.getElementById('check_all'), + checks = document.getElementsByTagName('input'), + total = 0, + i; + if ( chk_all.checked === true ) { + for ( i=0; i < checks.length; i++ ) { + if ( checks[i].name.indexOf( 'id' ) !== -1) { + checks[i].checked = true; + total++; + } } } - // Do operation. - do_operation(operation, id); - return false; - } -} + else { + for ( i=0; i < checks.length; i++ ) { + if ( checks[i].name.indexOf( 'id' ) !== -1) { + checks[i].checked = false; + } -// Navigate window to the URL defined by url_args. This method can be used to short-circuit grid AJAXing. -function go_to_URL() { - // Not async request. - grid.set('async', false); - - // Go. - window.location = grid.get('url_base') + "?" + $.param(grid.get_url_data()); -} - -// Update grid. -function update_grid(maintain_page_links) { - // If grid is not using async, then go to URL. - if (!grid.get('async')) { - go_to_URL(); - return; - } - - // If there's an operation, do POST; otherwise, do GET. - var method = (grid.get('operation') ? "POST" : "GET" ); - $('.loading-elt-overlay').show(); // Show overlay to indicate loading and prevent user actions. - $.ajax({ - type: method, - url: grid.get('url_base'), - data: grid.get_url_data(), - error: function() { alert( "Grid refresh failed" ); }, - success: function(response_text) { - // HACK: use a simple string to separate the elements in the - // response: (1) table body; (2) number of pages in table; and (3) message. - var parsed_response_text = response_text.split("*****"); - - // Update grid body and footer. - $('#grid-table-body').html(parsed_response_text[0]); - // FIXME: this does not work at all; what's needed is a function - // that updates page links when number of pages changes. - $('#grid-table-footer').html(parsed_response_text[1]); - - // Trigger custom event to indicate grid body has changed. - $('#grid-table-body').trigger('update'); - - // Init grid. - init_grid_elements(); - make_popup_menus(); - - // Hide loading overlay. - $('.loading-elt-overlay').hide(); - - // Show message if there is one. - var message = $.trim( parsed_response_text[2] ); - if (message !== "") { - $('#grid-message').html( message ).show(); - setTimeout( function() { $('#grid-message').hide(); }, 5000); - } - }, - complete: function() { - // Clear grid of transient request attributes. - grid.set({ - operation: undefined, - item_ids: undefined - }); - } - }); -} - -function check_all_items() { - var chk_all = document.getElementById('check_all'), - checks = document.getElementsByTagName('input'), - total = 0, - i; - if ( chk_all.checked === true ) { - for ( i=0; i < checks.length; i++ ) { - if ( checks[i].name.indexOf( 'id' ) !== -1) { - checks[i].checked = true; - total++; } } + this.init_grid_elements(); + }, + + // confirmation/submission of operation request + submit_operation: function (selected_button, confirmation_text) + { + // verify in any item is selected + var number_of_checked_ids = $('input[name="id"]:checked').length; + if (!number_of_checked_ids > 0) + return false; + + // show confirmation box + if (confirmation_text != 'None' && confirmation_text != '') + if(!confirm(confirmation_text)) + return false; + + // collect ids + var operation_name = $(selected_button).val(); + var item_ids = []; + $('input[name=id]:checked').each(function() { + item_ids.push( $(this).val() ); + }); + this.do_operation(operation_name, item_ids); + + // return + return true; } - else { - for ( i=0; i < checks.length; i++ ) { - if ( checks[i].name.indexOf( 'id' ) !== -1) { - checks[i].checked = false; - } - } - } - init_grid_elements(); -} - - -// confirmation/submission of operation request -function submit_operation(selected_button, confirmation_text) -{ - // verify in any item is selected - var number_of_checked_ids = $('input[name="id"]:checked').length; - if (!number_of_checked_ids > 0) - return false; - - // show confirmation box - if (confirmation_text != 'None' && confirmation_text != '') - if(!confirm(confirmation_text)) - return false; - - // add ids - var operation_name = selected_button.value; - var item_ids = []; - $('input[name=id]:checked').each(function() { - item_ids.push( $(this).val() ); - }); - do_operation(operation_name, item_ids); - - // return - return true; -} +}); diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 static/scripts/packed/galaxy.grids.js --- a/static/scripts/packed/galaxy.grids.js +++ b/static/scripts/packed/galaxy.grids.js @@ -1,1 +1,1 @@ -jQuery.ajaxSettings.traditional=true;var Grid=Backbone.Model.extend({defaults:{url_base:"",async:false,async_ops:[],categorical_filters:[],filters:{},sort_key:null,show_item_checkboxes:false,cur_page:1,num_pages:1,operation:undefined,item_ids:undefined},can_async_op:function(a){return _.indexOf(this.attributes.async_ops,a)!==-1},add_filter:function(e,f,b){if(b){var c=this.attributes.filters[e],a;if(c===null||c===undefined){a=f}else{if(typeof(c)=="string"){if(c=="All"){a=f}else{var d=[];d[0]=c;d[1]=f;a=d}}else{a=c;a.push(f)}}this.attributes.filters[e]=a}else{this.attributes.filters[e]=f}},remove_filter:function(b,e){var a=this.attributes.filters[b];if(a===null||a===undefined){return false}var d=true;if(typeof(a)==="string"){if(a=="All"){d=false}else{delete this.attributes.filters[b]}}else{var c=_.indexOf(a,e);if(c!==-1){a.splice(c,1)}else{d=false}}return d},get_url_data:function(){var a={async:this.attributes.async,sort:this.attributes.sort_key,page:this.attributes.cur_page,show_item_checkboxes:this.attributes.show_item_checkboxes};if(this.attributes.operation){a.operation=this.attributes.operation}if(this.attributes.item_ids){a.id=this.attributes.item_ids}var b=this;_.each(_.keys(b.attributes.filters),function(c){a["f-"+c]=b.attributes.filters[c]});return a}});function init_grid_controls(){$(".submit-image").each(function(){$(this).mousedown(function(){$(this).addClass("gray-background")});$(this).mouseup(function(){$(this).removeClass("gray-background")})});$(".sort-link").each(function(){$(this).click(function(){set_sort_condition($(this).attr("sort_key"));return false})});$(".page-link > a").each(function(){$(this).click(function(){set_page($(this).attr("page_num"));return false})});$(".categorical-filter > a").each(function(){$(this).click(function(){set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});$(".text-filter-form").each(function(){$(this).submit(function(){var d=$(this).attr("column_key");var c=$("#input-"+d+"-filter");var e=c.val();c.val("");add_filter_condition(d,e,true);return false})});var a=$("#input-tags-filter");if(a.length){a.autocomplete(history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var b=$("#input-name-filter");if(b.length){b.autocomplete(history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}$(".advanced-search-toggle").each(function(){$(this).click(function(){$("#standard-search").slideToggle("fast");$("#advanced-search").slideToggle("fast");return false})})}function init_grid_elements(){$(".grid").each(function(){var b=$(this).find("input.grid-row-select-checkbox");var a=$(this).find("span.grid-selected-count");var c=function(){a.text($(b).filter(":checked").length)};$(b).each(function(){$(this).change(c)});c()});$(".label").each(function(){var a=$(this).attr("href");if(a!==undefined&&a.indexOf("operation=")!=-1){$(this).click(function(){do_operation_from_href($(this).attr("href"));return false})}});if($(".community_rating_star").length!==0){$(".community_rating_star").rating({})}make_popup_menus()}function go_page_one(){var a=grid.get("cur_page");if(a!==null&&a!==undefined&&a!=="all"){grid.set("cur_page",1)}}function add_filter_condition(c,e,a){if(e===""){return false}grid.add_filter(c,e,a);var d=$("<span>"+e+"<a href='javascript:void(0);'><span class='delete-search-icon' /></a></span>");d.addClass("text-filter-val");d.click(function(){grid.remove_filter(c,e);$(this).remove();go_page_one();update_grid()});var b=$("#"+c+"-filtering-criteria");b.append(d);go_page_one();update_grid()}function add_tag_to_grid_filter(c,b){var a=c+(b!==undefined&&b!==""?":"+b:"");$("#advanced-search").show("fast");add_filter_condition("tags",a,true)}function set_sort_condition(f){var e=grid.get("sort_key");var d=f;if(e.indexOf(f)!==-1){if(e.substring(0,1)!=="-"){d="-"+f}else{}}$(".sort-arrow").remove();var c=(d.substring(0,1)=="-")?"↑":"↓";var a=$("<span>"+c+"</span>").addClass("sort-arrow");var b=$("#"+f+"-header");b.append(a);grid.set("sort_key",d);go_page_one();update_grid()}function set_categorical_filter(b,d){var a=grid.get("categorical_filters")[b],c=grid.get("filters")[b];$("."+b+"-filter").each(function(){var h=$.trim($(this).text());var f=a[h];var g=f[b];if(g==d){$(this).empty();$(this).addClass("current-filter");$(this).append(h)}else{if(g==c){$(this).empty();var e=$("<a href='#'>"+h+"</a>");e.click(function(){set_categorical_filter(b,g)});$(this).removeClass("current-filter");$(this).append(e)}}});grid.add_filter(b,d);go_page_one();update_grid()}function set_page(a){$(".page-link").each(function(){var g=$(this).attr("id"),e=parseInt(g.split("-")[2],10),c=grid.get("cur_page"),f;if(e===a){f=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(f)}else{if(e===c){f=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var d=$("<a href='#'>"+f+"</a>");d.click(function(){set_page(e)});$(this).append(d)}}});var b=true;if(a==="all"){grid.set("cur_page",a);b=false}else{grid.set("cur_page",parseInt(a,10))}update_grid(b)}function do_operation(b,a){b=b.toLowerCase();grid.set({operation:b,item_ids:a});if(grid.can_async_op(b)){update_grid(true)}else{go_to_URL()}}function do_operation_from_href(c){var f=c.split("?");if(f.length>1){var a=f[1];var e=a.split("&");var b=null;var g=-1;for(var d=0;d<e.length;d++){if(e[d].indexOf("operation")!=-1){b=e[d].split("=")[1]}else{if(e[d].indexOf("id")!=-1){g=e[d].split("=")[1]}}}do_operation(b,g);return false}}function go_to_URL(){grid.set("async",false);window.location=grid.get("url_base")+"?"+$.param(grid.get_url_data())}function update_grid(a){if(!grid.get("async")){go_to_URL();return}var b=(grid.get("operation")?"POST":"GET");$(".loading-elt-overlay").show();$.ajax({type:b,url:grid.get("url_base"),data:grid.get_url_data(),error:function(){alert("Grid refresh failed")},success:function(d){var c=d.split("*****");$("#grid-table-body").html(c[0]);$("#grid-table-footer").html(c[1]);$("#grid-table-body").trigger("update");init_grid_elements();make_popup_menus();$(".loading-elt-overlay").hide();var e=$.trim(c[2]);if(e!==""){$("#grid-message").html(e).show();setTimeout(function(){$("#grid-message").hide()},5000)}},complete:function(){grid.set({operation:undefined,item_ids:undefined})}})}function check_all_items(){var a=document.getElementById("check_all"),b=document.getElementsByTagName("input"),d=0,c;if(a.checked===true){for(c=0;c<b.length;c++){if(b[c].name.indexOf("id")!==-1){b[c].checked=true;d++}}}else{for(c=0;c<b.length;c++){if(b[c].name.indexOf("id")!==-1){b[c].checked=false}}}init_grid_elements()}function submit_operation(d,e){var c=$('input[name="id"]:checked').length;if(!c>0){return false}if(e!="None"&&e!=""){if(!confirm(e)){return false}}var b=d.value;var a=[];$("input[name=id]:checked").each(function(){a.push($(this).val())});do_operation(b,a);return true}; \ No newline at end of file +jQuery.ajaxSettings.traditional=true;var Grid=Backbone.Model.extend({defaults:{url_base:"",async:false,async_ops:[],categorical_filters:[],filters:{},sort_key:null,show_item_checkboxes:false,cur_page:1,num_pages:1,operation:undefined,item_ids:undefined},can_async_op:function(a){return _.indexOf(this.attributes.async_ops,a)!==-1},add_filter:function(e,f,b){if(b){var c=this.attributes.filters[e],a;if(c===null||c===undefined){a=f}else{if(typeof(c)=="string"){if(c=="All"){a=f}else{var d=[];d[0]=c;d[1]=f;a=d}}else{a=c;a.push(f)}}this.attributes.filters[e]=a}else{this.attributes.filters[e]=f}},remove_filter:function(b,e){var a=this.attributes.filters[b];if(a===null||a===undefined){return false}var d=true;if(typeof(a)==="string"){if(a=="All"){d=false}else{delete this.attributes.filters[b]}}else{var c=_.indexOf(a,e);if(c!==-1){a.splice(c,1)}else{d=false}}return d},get_url_data:function(){var a={async:this.attributes.async,sort:this.attributes.sort_key,page:this.attributes.cur_page,show_item_checkboxes:this.attributes.show_item_checkboxes};if(this.attributes.operation){a.operation=this.attributes.operation}if(this.attributes.item_ids){a.id=this.attributes.item_ids}var b=this;_.each(_.pairs(b.attributes.filters),function(c){a["f-"+c[0]]=c[1]});return a}});var GridView=Backbone.View.extend({grid:null,initialize:function(a){this.grid=a;this.init_grid_elements();this.init_grid_controls();$("input[type=text]").each(function(){$(this).click(function(){$(this).select()}).keyup(function(){$(this).css("font-style","normal")})})},init_grid_controls:function(){$(".submit-image").each(function(){$(this).mousedown(function(){$(this).addClass("gray-background")});$(this).mouseup(function(){$(this).removeClass("gray-background")})});var a=this;$(".sort-link").each(function(){$(this).click(function(){a.set_sort_condition($(this).attr("sort_key"));return false})});$(".page-link > a").each(function(){$(this).click(function(){a.set_page($(this).attr("page_num"));return false})});$(".categorical-filter > a").each(function(){$(this).click(function(){a.set_categorical_filter($(this).attr("filter_key"),$(this).attr("filter_val"));return false})});$(".text-filter-form").each(function(){$(this).submit(function(){var e=$(this).attr("column_key");var d=$("#input-"+e+"-filter");var f=d.val();d.val("");a.add_filter_condition(e,f,true);return false})});var b=$("#input-tags-filter");if(b.length){b.autocomplete(this.grid.history_tag_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}var c=$("#input-name-filter");if(c.length){c.autocomplete(this.grid.history_name_autocomplete_url,{selectFirst:false,autoFill:false,highlight:false,mustMatch:false})}$(".advanced-search-toggle").each(function(){$(this).click(function(){$("#standard-search").slideToggle("fast");$("#advanced-search").slideToggle("fast");return false})})},init_grid_elements:function(){$(".grid").each(function(){var c=$(this).find("input.grid-row-select-checkbox");var b=$(this).find("span.grid-selected-count");var d=function(){b.text($(c).filter(":checked").length)};$(c).each(function(){$(this).change(d)});d()});var a=this;$(".label").each(function(){var b=$(this).attr("href");if(b!==undefined&&b.indexOf("operation=")!=-1){$(this).click(function(){a.do_operation_from_href($(this).attr("href"));return false})}});if($(".community_rating_star").length!==0){$(".community_rating_star").rating({})}make_popup_menus()},go_page_one:function(){var a=this.grid.get("cur_page");if(a!==null&&a!==undefined&&a!=="all"){this.grid.set("cur_page",1)}},add_filter_condition:function(d,f,a){if(f===""){return false}this.grid.add_filter(d,f,a);var e=$("<span>"+f+"<a href='javascript:void(0);'><span class='delete-search-icon' /></a></span>");e.addClass("text-filter-val");var c=this;e.click(function(){c.grid.remove_filter(d,f);$(this).remove();c.go_page_one();c.update_grid()});var b=$("#"+d+"-filtering-criteria");b.append(e);this.go_page_one();this.update_grid()},set_sort_condition:function(f){var e=this.grid.get("sort_key");var d=f;if(e.indexOf(f)!==-1){if(e.substring(0,1)!=="-"){d="-"+f}else{}}$(".sort-arrow").remove();var c=(d.substring(0,1)=="-")?"↑":"↓";var a=$("<span>"+c+"</span>").addClass("sort-arrow");var b=$("#"+f+"-header");b.append(a);this.grid.set("sort_key",d);this.go_page_one();this.update_grid()},set_categorical_filter:function(c,e){var b=this.grid.get("categorical_filters")[c],d=this.grid.get("filters")[c];var a=this;$("."+c+"-filter").each(function(){var i=$.trim($(this).text());var g=b[i];var h=g[c];if(h==e){$(this).empty();$(this).addClass("current-filter");$(this).append(i)}else{if(h==d){$(this).empty();var f=$("<a href='#'>"+i+"</a>");f.click(function(){a.set_categorical_filter(c,h)});$(this).removeClass("current-filter");$(this).append(f)}}});this.grid.add_filter(c,e);this.go_page_one();this.update_grid()},set_page:function(a){var b=this;$(".page-link").each(function(){var h=$(this).attr("id"),f=parseInt(h.split("-")[2],10),d=b.grid.get("cur_page"),g;if(f===a){g=$(this).children().text();$(this).empty();$(this).addClass("inactive-link");$(this).text(g)}else{if(f===d){g=$(this).text();$(this).empty();$(this).removeClass("inactive-link");var e=$("<a href='#'>"+g+"</a>");e.click(function(){set_page(f)});$(this).append(e)}}});var c=true;if(a==="all"){this.grid.set("cur_page",a);c=false}else{this.grid.set("cur_page",parseInt(a,10))}this.update_grid(c)},do_operation:function(b,a){b=b.toLowerCase();this.grid.set({operation:b,item_ids:a});if(this.grid.can_async_op(b)){this.update_grid(true)}else{this.go_to_URL()}},do_operation_from_href:function(c){var f=c.split("?");if(f.length>1){var a=f[1];var e=a.split("&");var b=null;var g=-1;for(var d=0;d<e.length;d++){if(e[d].indexOf("operation")!=-1){b=e[d].split("=")[1]}else{if(e[d].indexOf("id")!=-1){g=e[d].split("=")[1]}}}this.do_operation(b,g);return false}},go_to_URL:function(){this.grid.set("async",false);window.location=this.grid.get("url_base")+"?"+$.param(this.grid.get_url_data());this.grid.set({operation:undefined,item_ids:undefined})},update_grid:function(b){if(!this.grid.get("async")){this.go_to_URL();return}var c=(this.grid.get("operation")?"POST":"GET");$(".loading-elt-overlay").show();var a=this;$.ajax({type:c,url:a.grid.get("url_base"),data:a.grid.get_url_data(),error:function(){alert("Grid refresh failed")},success:function(e){var d=e.split("*****");$("#grid-table-body").html(d[0]);$("#grid-table-footer").html(d[1]);$("#grid-table-body").trigger("update");a.init_grid_elements();make_popup_menus();$(".loading-elt-overlay").hide();var f=$.trim(d[2]);if(f!==""){$("#grid-message").html(f).show();setTimeout(function(){$("#grid-message").hide()},5000)}},complete:function(){a.grid.set({operation:undefined,item_ids:undefined})}})},check_all_items:function(){var a=document.getElementById("check_all"),b=document.getElementsByTagName("input"),d=0,c;if(a.checked===true){for(c=0;c<b.length;c++){if(b[c].name.indexOf("id")!==-1){b[c].checked=true;d++}}}else{for(c=0;c<b.length;c++){if(b[c].name.indexOf("id")!==-1){b[c].checked=false}}}this.init_grid_elements()},submit_operation:function(d,e){var c=$('input[name="id"]:checked').length;if(!c>0){return false}if(e!="None"&&e!=""){if(!confirm(e)){return false}}var b=$(d).val();var a=[];$("input[name=id]:checked").each(function(){a.push($(this).val())});this.do_operation(b,a);return true}}); \ No newline at end of file diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 templates/base.mako --- a/templates/base.mako +++ b/templates/base.mako @@ -2,6 +2,7 @@ <!DOCTYPE HTML><html><!--base.mako--> + ${self.init()} <head><title>${self.title()}</title><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> @@ -17,6 +18,9 @@ ## Default title <%def name="title()"></%def> +## Default init +<%def name="init()"></%def> + ## Default stylesheets <%def name="stylesheets()"> ${h.css('base')} diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 templates/grid_base.mako --- a/templates/grid_base.mako +++ b/templates/grid_base.mako @@ -12,16 +12,167 @@ return '/base.mako' %><%inherit file="${inherit(context)}"/> -<%namespace file="/display_common.mako" import="render_message" /> +<%namespace file="./grid_common.mako" import="*" /><%namespace file="/refresh_frames.mako" import="handle_refresh_frames" /> +<%namespace file="/display_common.mako" import="get_class_plural" /><%def name="init()"><% - self.has_left_panel=False - self.has_right_panel=False - self.message_box_visible=False - self.active_view="user" - self.overlay_visible=False + self.has_left_panel = False + self.has_right_panel = False + self.message_box_visible = False + self.overlay_visible = False + self.active_view = 'user' + + self.grid_config = { + 'title' : grid.title, + 'url_base' : trans.request.path_url, + 'async' : grid.use_async, + 'async_ops' : [], + 'categorical_filters' : {}, + 'filters' : cur_filter_dict, + 'sort_key' : sort_key, + 'show_item_checkboxes' : context.get('show_item_checkboxes', False), + 'cur_page_num' : cur_page_num, + 'num_pages' : num_pages, + 'history_tag_autocomplete_url' : url( controller='tag', action='tag_autocomplete_data', item_class='History' ), + 'history_name_autocomplete_url' : url( controller='history', action='name_autocomplete_data' ), + 'status' : status, + 'message' : util.restore_text(message), + 'global_actions' : [], + 'operations' : [], + 'items' : [], + 'columns' : [], + 'multiple_item_ops_exist' : None, + 'get_class_plural' : get_class_plural( grid.model_class ).lower(), + 'use_paging' : grid.use_paging, + 'legend' : grid.legend, + 'current_item_id' : '' + } + + ## add current item if exists + if current_item: + self.grid_config['current_item_id'] = current_item.id + endif + + ## column + for column in grid.columns: + + ## add column sort links + href = None + extra = '' + if column.sortable: + if sort_key.endswith(column.key): + if not sort_key.startswith("-"): + href = url( sort=( "-" + column.key ) ) + extra = "↓" + else: + href = url( sort=( column.key ) ) + extra = "↑" + else: + href = url( sort=column.key ) + + ## add to configuration + self.grid_config['columns'].append({ + 'key' : column.key, + 'visible' : column.visible, + 'nowrap' : column.nowrap, + 'attach_popup' : column.attach_popup, + 'label_id_prefix' : column.label_id_prefix, + 'sortable' : column.sortable, + 'label' : column.label, + 'href' : href, + 'extra' : extra + }) + endfor + + ## operations + for operation in grid.operations: + self.grid_config['operations'].append({ + 'allow_multiple' : operation.allow_multiple, + 'allow_popup' : operation.allow_popup, + 'target' : operation.target, + 'label' : operation.label, + 'confirm' : operation.confirm, + 'global_operation' : False + }) + if operation.allow_multiple: + self.grid_config['multiple_item_ops_exist'] = True + + if operation.global_operation: + self.grid_config['global_operation'] = url( ** (operation.global_operation()) ) + endfor + + ## global actions + for action in grid.global_actions: + self.grid_config['global_actions'].append({ + 'url_args' : url(**action.url_args), + 'label' : action.label + }) + endfor + + ## Operations that are async (AJAX) compatible. + for operation in [op for op in grid.operations if op.async_compatible]: + self.grid_config['async_ops'].append(operation.label.lower()); + endfor + + ## Filter values for categorical filters. + for column in grid.columns: + if column.filterable is not None and not isinstance( column, TextColumn ): + self.grid_config['categorical_filters'][column.key] = dict([ (filter.label, filter.args) for filter in column.get_accepted_filters() ]) + endif + endfor + + # items + for i, item in enumerate( query ): + item_dict = { + 'id' : item.id, + 'encode_id' : trans.security.encode_id(item.id), + 'link' : [], + 'operation_config' : {}, + 'column_config' : {} + } + + ## data columns + for column in grid.columns: + if column.visible: + ## get link + link = column.get_link(trans, grid, item) + if link: + link = url(**link) + else: + link = None + endif + + ## get value + value = column.get_value( trans, grid, item ) + + # Handle non-ascii chars. + if isinstance(value, str): + value = unicode(value, 'utf-8') + + # escape string + value = value.replace('/', '//') + endif + + ## Item dictionary + item_dict['column_config'][column.label] = { + 'link' : link, + 'value' : value + } + endif + endfor + ## add operation details to item + for operation in grid.operations: + item_dict['operation_config'][operation.label] = { + 'allowed' : operation.allowed(item), + 'url_args' : url( **operation.get_url_args( item ) ) + } + endfor + + ## add item to list + self.grid_config['items'].append(item_dict) + endfor %></%def> @@ -45,7 +196,7 @@ ${self.make_grid( grid )} </%def> -<%def name="title()">${grid.title}</%def> +<%def name="title()">${self.grid_config['title']}</%def><%def name="javascripts()"> ${parent.javascripts()} @@ -57,65 +208,33 @@ ${handle_refresh_frames()} <script type="text/javascript"> - - // Needed URLs for grid history searching. - var history_tag_autocomplete_url = "${url( controller='tag', action='tag_autocomplete_data', item_class='History' )}", - history_name_autocomplete_url = "${url( controller='history', action='name_autocomplete_data' )}"; - // - // Create grid object. - // + var gridView = null; - // Operations that are async (AJAX) compatible. - var async_ops = []; - %for operation in [op for op in grid.operations if op.async_compatible]: - async_ops.push('${operation.label.lower()}'); - %endfor + function 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 : ""); + $('#advanced-search').show('fast'); + gridView.add_filter_condition("tags", tag, true); + }; - // Filter values for categorical filters. - var categorical_filters = {}; - %for column in grid.columns: - %if column.filterable is not None and not isinstance( column, TextColumn ): - var ${column.key}_filters = ${ h.to_json_string( dict([ (filter.label, filter.args) for filter in column.get_accepted_filters() ]) ) }; - categorical_filters['${column.key}'] = ${column.key}_filters; - %endif - %endfor - - /** Returns true if string denotes true. */ - var is_true = function(s) { return _.indexOf(['True', 'true', 't'], s) !== -1; }; - - // Create grid. - var grid = new Grid({ - url_base: '${trans.request.path_url}', - async: is_true('${grid.use_async}'), - async_ops: async_ops, - categorical_filters: categorical_filters, - filters: ${h.to_json_string( cur_filter_dict )}, - sort_key: '${sort_key}', - show_item_checkboxes: is_true('${context.get('show_item_checkboxes', False)}'), - cur_page: ${cur_page_num}, - // persistent page="all" - //cur_page: ('${cur_page_num}' === 'all')?('all'):(Number('${cur_page_num}')), - num_pages: ${num_pages} - }); - - // Initialize grid objects on load. - // FIXME: use a grid view object eventually. - $(document).ready(function() { + $(function() { + ## get configuration + var grid_config = ${ h.to_json_string( self.grid_config ) }; + console.log(grid_config); + // Create grid. + var grid = new Grid(grid_config); // strip protocol and domain var url = grid.get('url_base'); url = url.replace(/^.*\/\/[^\/]+/, ''); grid.set('url_base', url); - - init_grid_elements(); - init_grid_controls(); - // Initialize text filters to select text on click and use normal font when user is typing. - $('input[type=text]').each(function() { - $(this).click(function() { $(this).select(); } ) - .keyup(function () { $(this).css("font-style", "normal"); }); - }); + + // Create view. + gridView = new GridView(grid); }); + </script></%def> @@ -127,17 +246,12 @@ %if context.get('use_panels'): div#center { padding: 10px; + overflow: auto; } %endif </style></%def> -## -## Custom grid methods. -## - -<%namespace file="./grid_common.mako" import="*" /> - <%def name="make_grid( grid )"><div class="loading-elt-overlay"></div><table> @@ -147,7 +261,14 @@ <td></td></tr><tr> - <td width="100%" id="grid-message" valign="top">${render_message( message, status )}</td> + <td width="100%" id="grid-message" valign="top"> + %if message: + <p> + <div class="${self.grid_config['status']}message transient-message">${self.grid_config['message']}</div> + <div style="clear: both"></div> + </p> + %endif + </td><td></td><td></td></tr> @@ -157,7 +278,7 @@ </%def><%def name="grid_title()"> - <h2>${grid.title}</h2> + <h2>${self.grid_config['title']}</h2></%def> ## Render grid header. @@ -166,18 +287,18 @@ %if render_title: ${self.grid_title()} %endif - - %if grid.global_actions: + + %if self.grid_config['global_actions']: <ul class="manage-table-actions"> - %if len( grid.global_actions ) < 3: - %for action in grid.global_actions: - <li><a class="action-button" href="${url( **action.url_args )}">${action.label}</a></li> + %if len( self.grid_config['global_actions'] ) < 3: + %for action in self.grid_config['global_actions']: + <li><a class="action-button" href="${action['url_args']}">${action['label']}</a></li> %endfor %else: <li><a class="action-button" id="action-8675309-popup" class="menubutton">Actions</a></li><div popupmenu="action-8675309-popup"> - %for action in grid.global_actions: - <a class="action-button" href="${url( **action.url_args )}">${action.label}</a> + %for action in self.grid_config['global_actions']: + <a class="action-button" href="${action['url_args']}">${action['label']}</a> %endfor </div> %endif @@ -191,55 +312,37 @@ ## Render grid. <%def name="render_grid_table( grid, show_item_checkboxes=False)"><% - # Set flag to indicate whether grid has operations that operate on multiple items. - multiple_item_ops_exist = False - for operation in grid.operations: - if operation.allow_multiple: - multiple_item_ops_exist = True - + # get configuration + show_item_checkboxes = self.grid_config['show_item_checkboxes'] + multiple_item_ops_exist = self.grid_config['multiple_item_ops_exist'] + sort_key = self.grid_config['sort_key'] + # Show checkboxes if flag is set or if multiple item ops exist. if show_item_checkboxes or multiple_item_ops_exist: show_item_checkboxes = True %> - <form action="${url()}" method="post" onsubmit="return false;"> + <form method="post" onsubmit="return false;"><table id="grid-table" class="grid"><thead id="grid-table-header"><tr> %if show_item_checkboxes: <th> - %if query.count() > 0: - <input type="checkbox" id="check_all" name=select_all_checkbox value="true" onclick='check_all_items(1);'><input type="hidden" name=select_all_checkbox value="true"> + %if len(self.grid_config['items']) > 0: + <input type="checkbox" id="check_all" name=select_all_checkbox value="true" onclick='gridView.check_all_items(1);'><input type="hidden" name=select_all_checkbox value="true"> %endif </th> %endif - %for column in grid.columns: - %if column.visible: - <% - href = "" - extra = "" - if column.sortable: - if sort_key.endswith(column.key): - if not sort_key.startswith("-"): - href = url( sort=( "-" + column.key ) ) - extra = "↓" - else: - href = url( sort=( column.key ) ) - extra = "↑" - else: - href = url( sort=column.key ) - %> + %for column in self.grid_config['columns']: + %if column['visible']: <th\ - id="${column.key}-header" - %if column.ncells > 1: - colspan="${column.ncells}" - %endif + id="${column['key']}-header" > - %if href: - <a href="${href}" class="sort-link" sort_key="${column.key}">${column.label}</a> + %if column['href']: + <a href="${column['href']}" class="sort-link" sort_key="${column['key']}">${column['label']}</a> %else: - ${column.label} + ${column['label']} %endif - <span class="sort-arrow">${extra}</span> + <span class="sort-arrow">${column['extra']}</span></th> %endif %endfor @@ -259,15 +362,16 @@ ## Render grid table body contents. <%def name="render_grid_table_body_contents(grid, show_item_checkboxes=False)"><% num_rows_rendered = 0 %> - %if query.count() == 0: + %if len(self.grid_config['items']) == 0: ## No results. <tr><td colspan="100"><em>No Items</em></td></tr><% num_rows_rendered = 1 %> %endif - %for i, item in enumerate( query ): - <% encoded_id = trans.security.encode_id( item.id ) %> + %for i, item in enumerate( self.grid_config['items'] ): + <% encoded_id = item['encode_id'] %> + <% popupmenu_id = "grid-" + str(i) + "-popup" %><tr \ - %if current_item == item: + %if self.grid_config['current_item_id'] == item['id']: class="current" \ %endif > @@ -278,71 +382,72 @@ </td> %endif ## Data columns - %for column in grid.columns: - %if column.visible: + %for column in self.grid_config['columns']: + %if column['visible']: <% - # Nowrap + ## Nowrap nowrap = "" - if column.nowrap: - nowrap = 'style="white-space:nowrap;"' - # Link - link = column.get_link( trans, grid, item ) - if link: - href = url( **link ) - else: - href = None - # Value (coerced to list so we can loop) - value = column.get_value( trans, grid, item ) - if column.ncells == 1: - value = [ value ] + if column['nowrap']: + nowrap = 'style="white-space:nowrap;"' + + # get column settings + column_settings = item['column_config'][column['label']] + + # load attributes + link = column_settings['link'] + value = column_settings['value'] + + # check if formatting is defined + value = str(value).replace('//', '/') + + # Attach popup menu? + id = "" + if column['attach_popup']: + id = 'grid-%d-popup' % i + endif + + # Determine appropriate class + cls = "" + if column['attach_popup']: + cls = "menubutton" + if link: + cls += " split" + endif + endif %> - %for cellnum, v in enumerate( value ): - <% - id = "" - # Handle non-ascii chars. - if isinstance(v, str): - v = unicode(v, 'utf-8') - # Attach popup menu? - if column.attach_popup and cellnum == 0: - id = 'grid-%d-popup' % i - # Determine appropriate class - cls = "" - if column.attach_popup: - cls = "menubutton" - if href: - cls += " split" - - %> - <td ${nowrap}> - %if href: - %if len(grid.operations) != 0: + <td ${nowrap}> + %if link: + %if len(self.grid_config['operations']) != 0: <div id="${id}" class="${cls}" style="float: left;"> - %endif - <a class="label" href="${href}">${v}</a> - %if len(grid.operations) != 0: + %endif + <a class="label" href="${link}">${value}</a> + %if len(self.grid_config['operations']) != 0: </div> - %endif - %else: - <div id="${id}" class="${cls}"><label id="${column.label_id_prefix}${encoded_id}" for="${encoded_id}">${v}</label></div> %endif - </td> - %endfor + %else: + <div id="${id}" class="${cls}"><label id="${column['label_id_prefix']}${encoded_id}" for="${encoded_id}">${value}</label></div> + %endif + </td> %endif %endfor ## Actions column <td> - <div popupmenu="grid-${i}-popup"> - %for operation in grid.operations: - %if operation.allowed( item ) and operation.allow_popup: + <div popupmenu="${popupmenu_id}"> + %for operation in self.grid_config['operations']: + <% + operation_id = operation['label'] + operation_settings = item['operation_config'][operation_id] + %> + %if operation_settings['allowed'] and operation['allow_popup']: <% target = "" - if operation.target: - target = "target='" + operation.target + "'" + if operation['target']: + target = "target='" + operation['target'] + "'" %> - %if operation.confirm: - <a class="action-button" ${target} confirm="${operation.confirm}" href="${ url( **operation.get_url_args( item ) ) }">${operation.label}</a> + %if operation['confirm']: + <a class="action-button" ${target} confirm="${operation['confirm']}" href="${operation_settings['url_args']}">${operation_id}</a> %else: - <a class="action-button" ${target} href="${ url( **operation.get_url_args( item ) ) }">${operation.label}</a> + <a class="action-button" ${target} href="${operation_settings['url_args']}">${operation_id}</a> %endif %endif %endfor @@ -351,20 +456,16 @@ </tr><% num_rows_rendered += 1 %> %endfor - ## Dummy rows to prevent table for moving too much. - ##%if grid.use_paging: - ## %for i in range( num_rows_rendered , grid.num_rows_per_page ): - ## <tr><td colspan="1000"></td></tr> - ## %endfor - ##%endif </%def> ## Render grid table footer contents. <%def name="render_grid_table_footer_contents(grid, show_item_checkboxes=False)"> - ## Row for navigating among pages. - <%namespace file="/display_common.mako" import="get_class_plural" /> - <% items_plural = get_class_plural( grid.model_class ).lower() %> - %if grid.use_paging and num_pages > 1: + <% + items_plural = self.grid_config['get_class_plural'] + num_pages = self.grid_config['num_pages'] + cur_page_num = self.grid_config['cur_page_num'] + %> + %if self.grid_config['use_paging'] and num_pages > 1: <tr id="page-links-row"> %if show_item_checkboxes: <td></td> @@ -438,33 +539,29 @@ <td></td><td colspan="100"> For <span class="grid-selected-count"></span> selected ${items_plural}: - %for operation in grid.operations: - %if operation.allow_multiple: - <input type="button" value="${operation.label}" class="action-button" onclick="submit_operation(this, '${operation.confirm}')"> + %for operation in self.grid_config['operations']: + %if operation['allow_multiple']: + <input type="button" value="${operation['label']}" class="action-button" onclick="gridView.submit_operation(this, '${operation['confirm']}')"> %endif %endfor </td></tr> %endif - %if len([o for o in grid.operations if o.global_operation]) > 0: + %if len([o for o in self.grid_config['operations'] if o['global_operation']]) > 0: <tr><td colspan="100"> - %for operation in grid.operations: - %if operation.global_operation: - <% - link = operation.global_operation() - href = url( **link ) - %> - <a class="action-button" href="${href}">${operation.label}</a> - %endif + %for operation in self.grid_config['operations']: + %if operation['global_operation']: + <a class="action-button" href="${operation['global_operation']}">${operation['label']}</a> + %endif %endfor </td></tr> %endif - %if grid.legend: + %if self.grid_config['legend']: <tr><td colspan="100"> - ${grid.legend} + ${self.grid_config['legend']} </td></tr> %endif diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 templates/grid_base_async.mako --- a/templates/grid_base_async.mako +++ b/templates/grid_base_async.mako @@ -1,6 +1,8 @@ <%namespace file="./grid_base.mako" import="*" /><%namespace file="/display_common.mako" import="render_message" /> +${init()} + <% # Set flag to indicate whether grid has operations that operate on multiple items. multiple_item_ops_exist = False diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 templates/webapps/galaxy/page/select_items_grid.mako --- a/templates/webapps/galaxy/page/select_items_grid.mako +++ b/templates/webapps/galaxy/page/select_items_grid.mako @@ -1,6 +1,6 @@ ## Template generates a grid that enables user to select items. <%namespace file="../grid_base.mako" import="*" /> - +${init()} ${grid_javascripts()} ${stylesheets()} ${render_grid_header( grid, False )} diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 templates/webapps/galaxy/page/select_items_grid_async.mako --- a/templates/webapps/galaxy/page/select_items_grid_async.mako +++ b/templates/webapps/galaxy/page/select_items_grid_async.mako @@ -1,6 +1,6 @@ <%namespace file="../grid_base.mako" import="*" /><%namespace file="/display_common.mako" import="render_message" /> - +${init()} ## Always show item checkboxes so that users can select items. ${render_grid_table_body_contents( grid, show_item_checkboxes=show_item_checkboxes )} ***** diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 templates/webapps/galaxy/tracks/add_to_viz.mako --- a/templates/webapps/galaxy/tracks/add_to_viz.mako +++ b/templates/webapps/galaxy/tracks/add_to_viz.mako @@ -1,6 +1,6 @@ ## Template generates a grid that enables user to add tracks <%namespace file="../grid_base.mako" import="*" /> - +${init()} ${stylesheets()} ${grid_javascripts()} ${render_grid_header( grid, False )} diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 templates/webapps/galaxy/tracks/add_tracks.mako --- a/templates/webapps/galaxy/tracks/add_tracks.mako +++ b/templates/webapps/galaxy/tracks/add_tracks.mako @@ -1,6 +1,6 @@ ## Template generates a grid that enables user to add tracks <%namespace file="../grid_base.mako" import="*" /> - +${init()} ${stylesheets()} ${grid_javascripts()} ${render_grid_header( grid, False )} diff -r 5e534cc8da856ad598d63b8b9f03fe20629df05f -r f1cef709a9e5631cae2126ffff71dbba64611f87 templates/webapps/galaxy/tracks/history_select_grid.mako --- a/templates/webapps/galaxy/tracks/history_select_grid.mako +++ b/templates/webapps/galaxy/tracks/history_select_grid.mako @@ -4,7 +4,7 @@ ## not make it possible to easily subclass templates. ## <%namespace file="../grid_base.mako" import="*" /> - +${init()} ${stylesheets()} ${grid_javascripts()} @@ -12,18 +12,16 @@ <script type="text/javascript"> // Load all grid URLs into modal-body element so that // grid + links stays embedded. - var load_urls_into_modal_body = function() { + $(document).ready(function() { + $(".addtracktab, #grid-table a").off(); $(".addtracktab, #grid-table a").click(function() { + console.log($(this).attr("href")); var modal_body = $(".modal-body"); if (modal_body.length !== 0) { modal_body.load($(this).attr("href")); return false; } }); - }; - // Need to process label URLs when document loaded and when grid changes. - $(document).ready(function() { - load_urls_into_modal_body(); }); </script><style> Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.
participants (1)
-
commits-noreply@bitbucket.org