commit/galaxy-central: carlfeberhard: UI, history multi-view: simplify header UI, label current history, some clean up
1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/a99aafec2a89/ Changeset: a99aafec2a89 User: carlfeberhard Date: 2015-02-12 15:06:37+00:00 Summary: UI, history multi-view: simplify header UI, label current history, some clean up Affected #: 4 files diff -r 5b2f1eab281920eca6c9d1b84e6cebf02866ea09 -r a99aafec2a89d95795cf1296ddf43c9fd6a38c8e client/galaxy/scripts/mvc/history/multi-panel.js --- a/client/galaxy/scripts/mvc/history/multi-panel.js +++ b/client/galaxy/scripts/mvc/history/multi-panel.js @@ -6,7 +6,6 @@ "ui/mode-button", "ui/search-input" ], function( HISTORY_MODEL, HPANEL_EDIT, baseMVC, ajaxQueue ){ -window.HISTORY_MODEL = HISTORY_MODEL; //============================================================================== /** */ function historyCopyDialog( history, options ){ @@ -265,7 +264,7 @@ return ( this.currentHistory )? [ '<div class="pull-left">', - '<button class="create-new btn btn-default">', _l( 'Create new' ), '</button> ', + '<strong class="current-label">', _l( 'Current History' ), '</strong>', '</div>' ].join( '' ) :[ @@ -328,6 +327,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({ //logger : console, + className : 'multi-history-columns', // ------------------------------------------------------------------------ set up /** Set up internals, history collection, and columns to display the history */ @@ -366,9 +366,8 @@ /** model id to column map */ this.columnMap = {}; -//TODO: why create here? + //TODO: why create here? this.createColumns( options.columnOptions ); - this.setUpListeners(); }, @@ -386,10 +385,7 @@ var multipanel = this; multipanel.stopListening( multipanel.collection ); multipanel.collection = models; -//TODO: slow... esp. on start up - //if( multipanel.order !== 'update' ){ - multipanel.sortCollection( multipanel.order, { silent: true }); - //} + multipanel.sortCollection( multipanel.order, { silent: true }); multipanel.setUpCollectionListeners(); multipanel.trigger( 'new-collection', multipanel ); return multipanel; @@ -408,11 +404,6 @@ 'change:deleted': multipanel.handleDeletedHistory, 'sort' : function(){ multipanel.renderColumns( 0 ); } - - // debugging - //'all' : function(){ - // console.info( 'collection:', arguments ); - //} }); }, @@ -430,11 +421,6 @@ this.sortCollection(); -////TODO: this actually means these render twice (1st from setCollection) - good enough for now - //if( oldCurrentColumn ){ oldCurrentColumn.render().delegateEvents(); } - //TODO:?? this occasionally causes race with hdaQueue - //newCurrentColumn.panel.render( 'fast' ).delegateEvents(); - multipanel._recalcFirstColumnHeight(); return newCurrentColumn; }, @@ -451,14 +437,14 @@ // if it's the current column, create a new, empty history as the new current if( column.model.id === this.currentHistoryId ){ -//TODO: figuring out the order of async here is tricky - for now let the user handle the two step process + //TODO: figuring out the order of async here is tricky + // - for now let the user handle the two step process //multipanel.collection.create().done( function(){ // if( !multipanel.collection.includeDeleted ){ multipanel.removeColumn( column, false ); } //}); } else if( !multipanel.collection.includeDeleted ){ multipanel.removeColumn( column ); } -//TODO: prob. be done better } }, @@ -475,13 +461,12 @@ //TODO: we can use a 2 arg version and return 1/0/-1 //this.collection.comparator = function( h1, h2 ){ this.collection.comparator = function( h ){ -//TODO: this won't do reverse order well + //TODO: this won't do reverse order well return [ h.id !== currentHistoryId, h.get( 'name' ).toLowerCase() ]; }; break; case 'size': this.collection.comparator = function( h ){ -//console.debug( 'name sort', arguments ) return [ h.id !== currentHistoryId, h.get( 'size' ) ]; }; break; @@ -501,6 +486,8 @@ order = 'update'; } this.order = order; + this.trigger( 'order:change', order, this ); + this.$( '.current-order' ).text( order ); this.sortCollection(); return this; }, @@ -533,6 +520,9 @@ return column; }, + /** return array of Columns filtered by filters and sorted to match the collection + * @param: filters Function[] array of filter fns + */ sortedFilteredColumns : function( filters ){ filters = filters || this.filters; if( !filters || !filters.length ){ @@ -547,6 +537,7 @@ }); }, + /** return array of Columns sorted to match the collection */ sortedColumns : function(){ var multipanel = this; var sorted = this.collection.map( function( history, index ){ @@ -555,9 +546,9 @@ return sorted; }, - /** */ + /** add a new column for history and render all columns if render is true */ addColumn : function add( history, render ){ -//console.debug( 'adding column for:', history ); + //this.debug( 'adding column for:', history ); render = render !== undefined? render: true; var newColumn = this.createColumn( history ); this.columnMap[ history.id ] = newColumn; @@ -567,9 +558,9 @@ return newColumn; }, - /** */ + /** add a new column for history and make it the current history/column */ addAsCurrentColumn : function add( history ){ -//console.log( 'adding current column for:', history ); + //this.log( 'adding current column for:', history ); var multipanel = this, newColumn = this.addColumn( history, false ); this.setCurrentHistory( history ); @@ -579,7 +570,7 @@ return newColumn; }, - /** */ + /** remove the given column, it's listeners, and optionally render */ removeColumn : function remove( column, render ){ render = render !== undefined? render : true; this.log( 'removeColumn', column ); @@ -657,79 +648,17 @@ var multipanel = this; multipanel.log( multipanel + '.render' ); - multipanel.$el.html( multipanel.template( multipanel.options ) ); - //console.debug( multipanel.$( '.loading-overlay' ).fadeIn( 0 ) ); + multipanel.$el.html( multipanel.mainTemplate( multipanel ) ); multipanel.renderColumns( speed ); - //console.debug( multipanel.$( '.loading-overlay' ).fadeOut( 'fast' ) ); // set the columns to full height allowed and set up behaviors for thie multipanel multipanel.setUpBehaviors(); -//TODO: wrong - has to wait for columns to render + //TODO: wrong - has to wait for columns to render + // - create a column listener that fires this when all columns are rendered multipanel.trigger( 'rendered', multipanel ); return multipanel; }, - /** Template - overall structure relies on flex-boxes and is 3 components: header, middle, footer */ - template : function template( options ){ - options = options || {}; - var html = []; - if( this.options.headerHeight ){ - html = html.concat([ - // a loading overlay - '<div class="loading-overlay flex-row"><div class="loading-overlay-message">loading...</div></div>', - '<div class="header flex-column-container">', - // page & history controls - '<div class="header-control header-control-left flex-column">', - '<button class="done btn btn-default">', _l( 'Done' ), '</button>', - '<button class="include-deleted btn btn-default"></button>', - '<div class="order btn-group">', - '<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">', - _l( 'Order histories by' ) + '... <span class="caret"></span>', - '</button>', - '<ul class="dropdown-menu" role="menu">', - '<li><a href="javascript:void(0);" class="order-update">', - _l( 'Time of last update' ), - '</a></li>', - '<li><a href="javascript:void(0);" class="order-name">', - _l( 'Name' ), - '</a></li>', - '<li><a href="javascript:void(0);" class="order-size">', - _l( 'Size' ), - '</a></li>', - '</ul>', - '</div>', - '<div id="search-histories" class="header-search"></div>', - '</div>', - // feedback - '<div class="header-control header-control-center flex-column">', - '<div class="header-info">', - '</div>', - '</div>', - // dataset controls - '<div class="header-control header-control-right flex-column">', - '<div id="search-datasets" class="header-search"></div>', - '<button id="toggle-deleted" class="btn btn-default">', - _l( 'Include deleted datasets' ), - '</button>', - '<button id="toggle-hidden" class="btn btn-default">', - _l( 'Include hidden datasets' ), - '</button>', - '</div>', - '</div>' - ]); - } - - html = html.concat([ - // middle - where the columns go - '<div class="outer-middle flex-row flex-row-container">', - '<div class="middle flex-column-container flex-row"></div>', - '</div>', - // footer - '<div class="footer flex-column-container">','</div>' - ]); - return $( html.join( '' ) ); - }, - /** Render the columns and panels */ renderColumns : function renderColumns( speed ){ speed = speed !== undefined? speed: this.fxSpeed; @@ -737,61 +666,21 @@ // render columns and track the total number rendered, firing an event when all are rendered var multipanel = this, sortedAndFiltered = multipanel.sortedFilteredColumns(); -//console.log( '\t columnMapLength:', this.columnMapLength(), this.columnMap ); //this.log( '\t sortedAndFiltered:', sortedAndFiltered ); // set up width based on collection size -//console.debug( '(render) width before:', multipanel.$( '.middle' ).width() ) multipanel.$( '.middle' ).width( sortedAndFiltered.length //TODO: magic number 16 === the amount that safely prevents stacking of columns when adding a new one * ( this.options.columnWidth + this.options.columnGap ) + this.options.columnGap + 16 ); -//console.debug( '(render) width now:', multipanel.$( '.middle' ).width() ) - -//console.debug( 'sortedAndFiltered:', sortedAndFiltered ) - //multipanel.$( '.middle' ).empty(); - -// this.$( '.middle' ).html( sortedAndFiltered.map( function( column, i ){ -// return column.$el.hide(); -// })); -// sortedAndFiltered.forEach( function( column, i ){ -////console.debug( 'rendering:', column, i ) -// //multipanel.$( '.middle' ).append( column.$el.hide() ); -// column.delegateEvents(); -////TODO: current column in-view is never fired -// multipanel.renderColumn( column, speed ).$el.show(); -// }); - -// var $els = sortedAndFiltered.map( function( column, i ){ -////console.debug( 'rendering:', column, i ) -// multipanel.renderColumn( column, speed ); -// return column.$el; -// }); -//// this breaks the event map -// //this.$( '.middle' ).html( $els ); -//// this doesn't -// this.$( '.middle' ).append( $els ); var $middle = multipanel.$( '.middle' ); $middle.empty(); sortedAndFiltered.forEach( function( column, i ){ -//console.debug( 'rendering:', column, i, column.panel ) - column.$el.appendTo( $middle ); column.delegateEvents(); multipanel.renderColumn( column, speed ); - - //column.$el.hide().appendTo( $middle ); - //multipanel.renderColumn( column, speed ); - // //.panel.on( 'all', function(){ - // // console.debug( 'column rendered:', arguments ); - // //}); - //// this won't work until we checkColumnsInView after the render - ////column.$el.fadeIn( speed ); - //column.$el.show(); }); - //this.log( 'column rendering done' ); - -//TODO: event columns-rendered + //TODO: event columns-rendered if( this.searchFor && sortedAndFiltered.length <= 1 ){ } else { @@ -808,15 +697,11 @@ renderColumn : function( column, speed ){ speed = speed !== undefined? speed: this.fxSpeed; return column.render( speed ); - //TODO: causes weirdness - //return _.delay( function(){ - // return column.render( speed ); - //}, 0 ); }, -//TODO: combine the following two more sensibly -//TODO: could have HistoryContents.haveDetails return false -// if column.model.contents.length === 0 && !column.model.get( 'empty' ) then just check that + //TODO: combine the following two more sensibly + //TODO: could have HistoryContents.haveDetails return false + // if column.model.contents.length === 0 && !column.model.get( 'empty' ) then just check that /** Get the *summary* contents of a column's history (and details on any expanded contents), * queueing the ajax call and using a named queue to prevent the call being sent twice */ @@ -874,14 +759,17 @@ events : { // will move to the server root (gen. Analyze data) 'click .done.btn' : 'close', - //TODO:?? could just go back - but that's not always correct/desired behav. - //'click .done.btn' : function(){ window.history.back(); }, // creates a new empty history and makes it current 'click .create-new.btn' : 'create', + + 'click #include-deleted' : '_clickToggleDeletedHistories', // these change the collection and column sort order 'click .order .order-update' : function( e ){ this.setOrder( 'update' ); }, 'click .order .order-name' : function( e ){ this.setOrder( 'name' ); }, - 'click .order .order-size' : function( e ){ this.setOrder( 'size' ); } + 'click .order .order-size' : function( e ){ this.setOrder( 'size' ); }, + + 'click #toggle-deleted' : '_clickToggleDeletedDatasets', + 'click #toggle-hidden' : '_clickToggleHiddenDatasets' //'dragstart .list-item .title-bar' : function( e ){ console.debug( 'ok' ); } }, @@ -897,37 +785,48 @@ window.location = destination; }, + _clickToggleDeletedHistories : function( ev ){ + return this.toggleDeletedHistories( $( ev.currentTarget ).is( ':checked' ) ); + }, /** Include deleted histories in the collection */ - includeDeletedHistories : function(){ - //TODO: better through API/limit+offset - window.location += ( /\?/.test( window.location.toString() ) )?( '&' ):( '?' ) - + 'include_deleted_histories=True'; + toggleDeletedHistories : function( show ){ + if( show ){ + window.location = Galaxy.options.root + 'history/view_multiple?include_deleted_histories=True'; + } else { + window.location = Galaxy.options.root + 'history/view_multiple'; + } }, - /** Show only non-deleted histories */ - excludeDeletedHistories : function(){ - //TODO: better through API/limit+offset - window.location = window.location.toString().replace( /[&\?]include_deleted_histories=True/g, '' ); + _clickToggleDeletedDatasets : function( ev ){ + return this.toggleDeletedDatasets( $( ev.currentTarget ).is( ':checked' ) ); + }, + toggleDeletedDatasets : function( show ){ + show = show !== undefined? show : false; + var multipanel = this; + multipanel.sortedFilteredColumns().forEach( function( column, i ){ + _.delay( function(){ + column.panel.toggleShowDeleted( show, false ); + }, i * 200 ); + }); + }, + + _clickToggleHiddenDatasets : function( ev ){ + return this.toggleHiddenDatasets( $( ev.currentTarget ).is( ':checked' ) ); + }, + toggleHiddenDatasets : function( show ){ + show = show !== undefined? show : false; + var multipanel = this; + multipanel.sortedFilteredColumns().forEach( function( column, i ){ + _.delay( function(){ + column.panel.toggleShowHidden( show, false ); + }, i * 200 ); + }); }, /** Set up any view plugins */ setUpBehaviors : function(){ var multipanel = this; - -//TODO: currently doesn't need to be a mode button - // toggle button for include deleted - multipanel.$( '.include-deleted' ).modeButton({ - initialMode : this.collection.includeDeleted? 'exclude' : 'include', - switchModesOnClick : false, - modes: [ - { mode: 'include', html: _l( 'Include deleted histories' ), - onclick: _.bind( multipanel.includeDeletedHistories, multipanel ) - }, - { mode: 'exclude', html: _l( 'Exclude deleted histories' ), - onclick: _.bind( multipanel.excludeDeletedHistories, multipanel ) - } - ] - }); + multipanel._moreOptionsPopover(); // input to search histories multipanel.$( '#search-histories' ).searchInput({ @@ -942,7 +841,7 @@ }, onclear : function( searchFor ){ multipanel.searchFor = null; -//TODO: remove specifically not just reset + //TODO: remove specifically not just reset multipanel.filters = []; multipanel.renderColumns( 0 ); } @@ -986,53 +885,25 @@ } }); -//TODO: each panel stores the hidden/deleted state - and that isn't reflected in the buttons - // toggle button for showing deleted history contents - multipanel.$( '#toggle-deleted' ).modeButton({ - initialMode : 'include', - modes: [ - { mode: 'exclude', html: _l( 'Exclude deleted datasets' ) }, - { mode: 'include', html: _l( 'Include deleted datasets' ) } - ] - }).click( function(){ - var show = $( this ).modeButton( 'getMode' ).mode === 'exclude'; - multipanel.sortedFilteredColumns().forEach( function( column, i ){ - _.delay( function(){ - column.panel.toggleShowDeleted( show, false ); - }, i * 200 ); - }); - }); - - // toggle button for showing hidden history contents - multipanel.$( '#toggle-hidden' ).modeButton({ - initialMode : 'include', - modes: [ - { mode: 'exclude', html: _l( 'Exclude hidden datasets' ) }, - { mode: 'include', html: _l( 'Include hidden datasets' ) } - ] - }).click( function(){ - var show = $( this ).modeButton( 'getMode' ).mode === 'exclude'; - multipanel.sortedFilteredColumns().forEach( function( column, i ){ - _.delay( function(){ - column.panel.toggleShowHidden( show, false ); - }, i * 200 ); - }); - }); - // resize first (fixed position) column on page resize $( window ).resize( function(){ multipanel._recalcFirstColumnHeight(); }); // when scrolling - check for histories now in view: they will fire 'in-view' and queueHdaLoading if necc. -//TODO:?? might be able to simplify and not use pub-sub + //TODO:?? might be able to simplify and not use pub-sub var debouncedInView = _.debounce( _.bind( this.checkColumnsInView, this ), 100 ); this.$( '.middle' ).parent().scroll( debouncedInView ); }, - ///** Put the in-view columns then the other columns in a queue, rendering each one at a time */ - //panelRenderQueue : function( columns, fn, args, renderEventName ){ - //}, + _moreOptionsPopover : function(){ + return this.$( '.open-more-options.btn' ).popover({ + container : '.header', + placement : 'bottom', + html : true, + content : $( this.optionsPopoverTemplate( this ) ) + }); + }, /** Adjust the height of the first, current column since flex-boxes won't work with fixed postiion elements */ _recalcFirstColumnHeight : function(){ @@ -1052,18 +923,17 @@ /** returns the columns currently in the viewport */ columnsInView : function(){ //TODO: uses offset which is render intensive -//TODO: 2N - could use arg filter (sortedFilteredColumns( filter )) instead + //TODO: 2N - could use arg filter (sortedFilteredColumns( filter )) instead var vp = this._viewport(); return this.sortedFilteredColumns().filter( function( column ){ return column.currentHistory || column.inView( vp.left, vp.right ); }); }, -//TODO: sortByInView - return cols in view, then others + //TODO: sortByInView - return cols in view, then others /** trigger in-view from columns in-view */ checkColumnsInView : function(){ -//TODO: assbackward -//console.debug( 'checking columns in view', this.columnsInView() ); + //TODO: assbackward - don't fire from the column, fire from here and listen from here this.columnsInView().forEach( function( column ){ column.trigger( 'in-view', column ); }); @@ -1073,7 +943,7 @@ currentColumnDropTargetOn : function(){ var currentColumn = this.columnMap[ this.currentHistoryId ]; if( !currentColumn ){ return; } -//TODO: fix this - shouldn't need monkeypatch + //TODO: fix this - shouldn't need monkeypatch currentColumn.panel.dataDropped = function( data ){}; currentColumn.panel.dropTargetOn(); }, @@ -1090,7 +960,73 @@ /** String rep */ toString : function(){ return 'MultiPanelColumns(' + ( this.columns? this.columns.length : 0 ) + ')'; - } + }, + + // ------------------------------------------------------------------------ templates + mainTemplate : _.template([ + '<div class="header flex-column-container">', + '<div class="control-column control-column-left flex-column">', + '<button class="create-new btn btn-default">', _l( 'Create new' ), '</button> ', + '<div id="search-histories" class="search-control"></div>', + '<div id="search-datasets" class="search-control"></div>', + '<button class="open-more-options btn btn-default">', + //TODO: tip not working + '<span class="fa fa-ellipsis-h" title="More options"></span>', + '</button>', + '</div>', + // feedback + '<div class="control-column control-column-center flex-column">', + '<div class="header-info">', + '</div>', + '</div>', + '<div class="control-column control-column-right flex-column">', + '<button class="done btn btn-default">', _l( 'Done' ), '</button>', + '</div>', + '</div>', + // middle - where the columns go + '<div class="outer-middle flex-row flex-row-container">', + '<div class="middle flex-column-container flex-row"></div>', + '</div>', + // footer + '<div class="footer flex-column-container">', + '</div>' + ].join(''), { variable: 'view' }), + + optionsPopoverTemplate : _.template([ + '<div class="more-options">', + '<div class="checkbox"><label><input id="include-deleted" type="checkbox"', + '<%= view.collection.includeDeleted? " checked" : "" %>>', + _l( 'Include deleted histories' ), + '</label></div>', + + '<div class="order btn-group">', + '<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">', + _l( 'Order histories by' ) + ' ', + '<span class="current-order"><%= view.order %></span> ', + '<span class="caret"></span>', + '</button>', + '<ul class="dropdown-menu" role="menu">', + '<li><a href="javascript:void(0);" class="order-update">', + _l( 'Time of last update' ), + '</a></li>', + '<li><a href="javascript:void(0);" class="order-name">', + _l( 'Name' ), + '</a></li>', + '<li><a href="javascript:void(0);" class="order-size">', + _l( 'Size' ), + '</a></li>', + '</ul>', + '</div>', + + '<div class="checkbox"><label><input id="toggle-deleted" type="checkbox">', + _l( 'Include deleted datasets' ), + '</label></div>', + '<div class="checkbox"><label><input id="toggle-hidden" type="checkbox">', + _l( 'Include hidden datasets' ), + '</label></div>', + '</div>' + ].join(''), { variable: 'view' }), + }); diff -r 5b2f1eab281920eca6c9d1b84e6cebf02866ea09 -r a99aafec2a89d95795cf1296ddf43c9fd6a38c8e static/scripts/mvc/history/multi-panel.js --- a/static/scripts/mvc/history/multi-panel.js +++ b/static/scripts/mvc/history/multi-panel.js @@ -6,7 +6,6 @@ "ui/mode-button", "ui/search-input" ], function( HISTORY_MODEL, HPANEL_EDIT, baseMVC, ajaxQueue ){ -window.HISTORY_MODEL = HISTORY_MODEL; //============================================================================== /** */ function historyCopyDialog( history, options ){ @@ -265,7 +264,7 @@ return ( this.currentHistory )? [ '<div class="pull-left">', - '<button class="create-new btn btn-default">', _l( 'Create new' ), '</button> ', + '<strong class="current-label">', _l( 'Current History' ), '</strong>', '</div>' ].join( '' ) :[ @@ -328,6 +327,7 @@ var MultiPanelColumns = Backbone.View.extend( baseMVC.LoggableMixin ).extend({ //logger : console, + className : 'multi-history-columns', // ------------------------------------------------------------------------ set up /** Set up internals, history collection, and columns to display the history */ @@ -366,9 +366,8 @@ /** model id to column map */ this.columnMap = {}; -//TODO: why create here? + //TODO: why create here? this.createColumns( options.columnOptions ); - this.setUpListeners(); }, @@ -386,10 +385,7 @@ var multipanel = this; multipanel.stopListening( multipanel.collection ); multipanel.collection = models; -//TODO: slow... esp. on start up - //if( multipanel.order !== 'update' ){ - multipanel.sortCollection( multipanel.order, { silent: true }); - //} + multipanel.sortCollection( multipanel.order, { silent: true }); multipanel.setUpCollectionListeners(); multipanel.trigger( 'new-collection', multipanel ); return multipanel; @@ -408,11 +404,6 @@ 'change:deleted': multipanel.handleDeletedHistory, 'sort' : function(){ multipanel.renderColumns( 0 ); } - - // debugging - //'all' : function(){ - // console.info( 'collection:', arguments ); - //} }); }, @@ -430,11 +421,6 @@ this.sortCollection(); -////TODO: this actually means these render twice (1st from setCollection) - good enough for now - //if( oldCurrentColumn ){ oldCurrentColumn.render().delegateEvents(); } - //TODO:?? this occasionally causes race with hdaQueue - //newCurrentColumn.panel.render( 'fast' ).delegateEvents(); - multipanel._recalcFirstColumnHeight(); return newCurrentColumn; }, @@ -451,14 +437,14 @@ // if it's the current column, create a new, empty history as the new current if( column.model.id === this.currentHistoryId ){ -//TODO: figuring out the order of async here is tricky - for now let the user handle the two step process + //TODO: figuring out the order of async here is tricky + // - for now let the user handle the two step process //multipanel.collection.create().done( function(){ // if( !multipanel.collection.includeDeleted ){ multipanel.removeColumn( column, false ); } //}); } else if( !multipanel.collection.includeDeleted ){ multipanel.removeColumn( column ); } -//TODO: prob. be done better } }, @@ -475,13 +461,12 @@ //TODO: we can use a 2 arg version and return 1/0/-1 //this.collection.comparator = function( h1, h2 ){ this.collection.comparator = function( h ){ -//TODO: this won't do reverse order well + //TODO: this won't do reverse order well return [ h.id !== currentHistoryId, h.get( 'name' ).toLowerCase() ]; }; break; case 'size': this.collection.comparator = function( h ){ -//console.debug( 'name sort', arguments ) return [ h.id !== currentHistoryId, h.get( 'size' ) ]; }; break; @@ -501,6 +486,8 @@ order = 'update'; } this.order = order; + this.trigger( 'order:change', order, this ); + this.$( '.current-order' ).text( order ); this.sortCollection(); return this; }, @@ -533,6 +520,9 @@ return column; }, + /** return array of Columns filtered by filters and sorted to match the collection + * @param: filters Function[] array of filter fns + */ sortedFilteredColumns : function( filters ){ filters = filters || this.filters; if( !filters || !filters.length ){ @@ -547,6 +537,7 @@ }); }, + /** return array of Columns sorted to match the collection */ sortedColumns : function(){ var multipanel = this; var sorted = this.collection.map( function( history, index ){ @@ -555,9 +546,9 @@ return sorted; }, - /** */ + /** add a new column for history and render all columns if render is true */ addColumn : function add( history, render ){ -//console.debug( 'adding column for:', history ); + //this.debug( 'adding column for:', history ); render = render !== undefined? render: true; var newColumn = this.createColumn( history ); this.columnMap[ history.id ] = newColumn; @@ -567,9 +558,9 @@ return newColumn; }, - /** */ + /** add a new column for history and make it the current history/column */ addAsCurrentColumn : function add( history ){ -//console.log( 'adding current column for:', history ); + //this.log( 'adding current column for:', history ); var multipanel = this, newColumn = this.addColumn( history, false ); this.setCurrentHistory( history ); @@ -579,7 +570,7 @@ return newColumn; }, - /** */ + /** remove the given column, it's listeners, and optionally render */ removeColumn : function remove( column, render ){ render = render !== undefined? render : true; this.log( 'removeColumn', column ); @@ -657,79 +648,17 @@ var multipanel = this; multipanel.log( multipanel + '.render' ); - multipanel.$el.html( multipanel.template( multipanel.options ) ); - //console.debug( multipanel.$( '.loading-overlay' ).fadeIn( 0 ) ); + multipanel.$el.html( multipanel.mainTemplate( multipanel ) ); multipanel.renderColumns( speed ); - //console.debug( multipanel.$( '.loading-overlay' ).fadeOut( 'fast' ) ); // set the columns to full height allowed and set up behaviors for thie multipanel multipanel.setUpBehaviors(); -//TODO: wrong - has to wait for columns to render + //TODO: wrong - has to wait for columns to render + // - create a column listener that fires this when all columns are rendered multipanel.trigger( 'rendered', multipanel ); return multipanel; }, - /** Template - overall structure relies on flex-boxes and is 3 components: header, middle, footer */ - template : function template( options ){ - options = options || {}; - var html = []; - if( this.options.headerHeight ){ - html = html.concat([ - // a loading overlay - '<div class="loading-overlay flex-row"><div class="loading-overlay-message">loading...</div></div>', - '<div class="header flex-column-container">', - // page & history controls - '<div class="header-control header-control-left flex-column">', - '<button class="done btn btn-default">', _l( 'Done' ), '</button>', - '<button class="include-deleted btn btn-default"></button>', - '<div class="order btn-group">', - '<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">', - _l( 'Order histories by' ) + '... <span class="caret"></span>', - '</button>', - '<ul class="dropdown-menu" role="menu">', - '<li><a href="javascript:void(0);" class="order-update">', - _l( 'Time of last update' ), - '</a></li>', - '<li><a href="javascript:void(0);" class="order-name">', - _l( 'Name' ), - '</a></li>', - '<li><a href="javascript:void(0);" class="order-size">', - _l( 'Size' ), - '</a></li>', - '</ul>', - '</div>', - '<div id="search-histories" class="header-search"></div>', - '</div>', - // feedback - '<div class="header-control header-control-center flex-column">', - '<div class="header-info">', - '</div>', - '</div>', - // dataset controls - '<div class="header-control header-control-right flex-column">', - '<div id="search-datasets" class="header-search"></div>', - '<button id="toggle-deleted" class="btn btn-default">', - _l( 'Include deleted datasets' ), - '</button>', - '<button id="toggle-hidden" class="btn btn-default">', - _l( 'Include hidden datasets' ), - '</button>', - '</div>', - '</div>' - ]); - } - - html = html.concat([ - // middle - where the columns go - '<div class="outer-middle flex-row flex-row-container">', - '<div class="middle flex-column-container flex-row"></div>', - '</div>', - // footer - '<div class="footer flex-column-container">','</div>' - ]); - return $( html.join( '' ) ); - }, - /** Render the columns and panels */ renderColumns : function renderColumns( speed ){ speed = speed !== undefined? speed: this.fxSpeed; @@ -737,61 +666,21 @@ // render columns and track the total number rendered, firing an event when all are rendered var multipanel = this, sortedAndFiltered = multipanel.sortedFilteredColumns(); -//console.log( '\t columnMapLength:', this.columnMapLength(), this.columnMap ); //this.log( '\t sortedAndFiltered:', sortedAndFiltered ); // set up width based on collection size -//console.debug( '(render) width before:', multipanel.$( '.middle' ).width() ) multipanel.$( '.middle' ).width( sortedAndFiltered.length //TODO: magic number 16 === the amount that safely prevents stacking of columns when adding a new one * ( this.options.columnWidth + this.options.columnGap ) + this.options.columnGap + 16 ); -//console.debug( '(render) width now:', multipanel.$( '.middle' ).width() ) - -//console.debug( 'sortedAndFiltered:', sortedAndFiltered ) - //multipanel.$( '.middle' ).empty(); - -// this.$( '.middle' ).html( sortedAndFiltered.map( function( column, i ){ -// return column.$el.hide(); -// })); -// sortedAndFiltered.forEach( function( column, i ){ -////console.debug( 'rendering:', column, i ) -// //multipanel.$( '.middle' ).append( column.$el.hide() ); -// column.delegateEvents(); -////TODO: current column in-view is never fired -// multipanel.renderColumn( column, speed ).$el.show(); -// }); - -// var $els = sortedAndFiltered.map( function( column, i ){ -////console.debug( 'rendering:', column, i ) -// multipanel.renderColumn( column, speed ); -// return column.$el; -// }); -//// this breaks the event map -// //this.$( '.middle' ).html( $els ); -//// this doesn't -// this.$( '.middle' ).append( $els ); var $middle = multipanel.$( '.middle' ); $middle.empty(); sortedAndFiltered.forEach( function( column, i ){ -//console.debug( 'rendering:', column, i, column.panel ) - column.$el.appendTo( $middle ); column.delegateEvents(); multipanel.renderColumn( column, speed ); - - //column.$el.hide().appendTo( $middle ); - //multipanel.renderColumn( column, speed ); - // //.panel.on( 'all', function(){ - // // console.debug( 'column rendered:', arguments ); - // //}); - //// this won't work until we checkColumnsInView after the render - ////column.$el.fadeIn( speed ); - //column.$el.show(); }); - //this.log( 'column rendering done' ); - -//TODO: event columns-rendered + //TODO: event columns-rendered if( this.searchFor && sortedAndFiltered.length <= 1 ){ } else { @@ -808,15 +697,11 @@ renderColumn : function( column, speed ){ speed = speed !== undefined? speed: this.fxSpeed; return column.render( speed ); - //TODO: causes weirdness - //return _.delay( function(){ - // return column.render( speed ); - //}, 0 ); }, -//TODO: combine the following two more sensibly -//TODO: could have HistoryContents.haveDetails return false -// if column.model.contents.length === 0 && !column.model.get( 'empty' ) then just check that + //TODO: combine the following two more sensibly + //TODO: could have HistoryContents.haveDetails return false + // if column.model.contents.length === 0 && !column.model.get( 'empty' ) then just check that /** Get the *summary* contents of a column's history (and details on any expanded contents), * queueing the ajax call and using a named queue to prevent the call being sent twice */ @@ -874,14 +759,17 @@ events : { // will move to the server root (gen. Analyze data) 'click .done.btn' : 'close', - //TODO:?? could just go back - but that's not always correct/desired behav. - //'click .done.btn' : function(){ window.history.back(); }, // creates a new empty history and makes it current 'click .create-new.btn' : 'create', + + 'click #include-deleted' : '_clickToggleDeletedHistories', // these change the collection and column sort order 'click .order .order-update' : function( e ){ this.setOrder( 'update' ); }, 'click .order .order-name' : function( e ){ this.setOrder( 'name' ); }, - 'click .order .order-size' : function( e ){ this.setOrder( 'size' ); } + 'click .order .order-size' : function( e ){ this.setOrder( 'size' ); }, + + 'click #toggle-deleted' : '_clickToggleDeletedDatasets', + 'click #toggle-hidden' : '_clickToggleHiddenDatasets' //'dragstart .list-item .title-bar' : function( e ){ console.debug( 'ok' ); } }, @@ -897,37 +785,48 @@ window.location = destination; }, + _clickToggleDeletedHistories : function( ev ){ + return this.toggleDeletedHistories( $( ev.currentTarget ).is( ':checked' ) ); + }, /** Include deleted histories in the collection */ - includeDeletedHistories : function(){ - //TODO: better through API/limit+offset - window.location += ( /\?/.test( window.location.toString() ) )?( '&' ):( '?' ) - + 'include_deleted_histories=True'; + toggleDeletedHistories : function( show ){ + if( show ){ + window.location = Galaxy.options.root + 'history/view_multiple?include_deleted_histories=True'; + } else { + window.location = Galaxy.options.root + 'history/view_multiple'; + } }, - /** Show only non-deleted histories */ - excludeDeletedHistories : function(){ - //TODO: better through API/limit+offset - window.location = window.location.toString().replace( /[&\?]include_deleted_histories=True/g, '' ); + _clickToggleDeletedDatasets : function( ev ){ + return this.toggleDeletedDatasets( $( ev.currentTarget ).is( ':checked' ) ); + }, + toggleDeletedDatasets : function( show ){ + show = show !== undefined? show : false; + var multipanel = this; + multipanel.sortedFilteredColumns().forEach( function( column, i ){ + _.delay( function(){ + column.panel.toggleShowDeleted( show, false ); + }, i * 200 ); + }); + }, + + _clickToggleHiddenDatasets : function( ev ){ + return this.toggleHiddenDatasets( $( ev.currentTarget ).is( ':checked' ) ); + }, + toggleHiddenDatasets : function( show ){ + show = show !== undefined? show : false; + var multipanel = this; + multipanel.sortedFilteredColumns().forEach( function( column, i ){ + _.delay( function(){ + column.panel.toggleShowHidden( show, false ); + }, i * 200 ); + }); }, /** Set up any view plugins */ setUpBehaviors : function(){ var multipanel = this; - -//TODO: currently doesn't need to be a mode button - // toggle button for include deleted - multipanel.$( '.include-deleted' ).modeButton({ - initialMode : this.collection.includeDeleted? 'exclude' : 'include', - switchModesOnClick : false, - modes: [ - { mode: 'include', html: _l( 'Include deleted histories' ), - onclick: _.bind( multipanel.includeDeletedHistories, multipanel ) - }, - { mode: 'exclude', html: _l( 'Exclude deleted histories' ), - onclick: _.bind( multipanel.excludeDeletedHistories, multipanel ) - } - ] - }); + multipanel._moreOptionsPopover(); // input to search histories multipanel.$( '#search-histories' ).searchInput({ @@ -942,7 +841,7 @@ }, onclear : function( searchFor ){ multipanel.searchFor = null; -//TODO: remove specifically not just reset + //TODO: remove specifically not just reset multipanel.filters = []; multipanel.renderColumns( 0 ); } @@ -986,53 +885,25 @@ } }); -//TODO: each panel stores the hidden/deleted state - and that isn't reflected in the buttons - // toggle button for showing deleted history contents - multipanel.$( '#toggle-deleted' ).modeButton({ - initialMode : 'include', - modes: [ - { mode: 'exclude', html: _l( 'Exclude deleted datasets' ) }, - { mode: 'include', html: _l( 'Include deleted datasets' ) } - ] - }).click( function(){ - var show = $( this ).modeButton( 'getMode' ).mode === 'exclude'; - multipanel.sortedFilteredColumns().forEach( function( column, i ){ - _.delay( function(){ - column.panel.toggleShowDeleted( show, false ); - }, i * 200 ); - }); - }); - - // toggle button for showing hidden history contents - multipanel.$( '#toggle-hidden' ).modeButton({ - initialMode : 'include', - modes: [ - { mode: 'exclude', html: _l( 'Exclude hidden datasets' ) }, - { mode: 'include', html: _l( 'Include hidden datasets' ) } - ] - }).click( function(){ - var show = $( this ).modeButton( 'getMode' ).mode === 'exclude'; - multipanel.sortedFilteredColumns().forEach( function( column, i ){ - _.delay( function(){ - column.panel.toggleShowHidden( show, false ); - }, i * 200 ); - }); - }); - // resize first (fixed position) column on page resize $( window ).resize( function(){ multipanel._recalcFirstColumnHeight(); }); // when scrolling - check for histories now in view: they will fire 'in-view' and queueHdaLoading if necc. -//TODO:?? might be able to simplify and not use pub-sub + //TODO:?? might be able to simplify and not use pub-sub var debouncedInView = _.debounce( _.bind( this.checkColumnsInView, this ), 100 ); this.$( '.middle' ).parent().scroll( debouncedInView ); }, - ///** Put the in-view columns then the other columns in a queue, rendering each one at a time */ - //panelRenderQueue : function( columns, fn, args, renderEventName ){ - //}, + _moreOptionsPopover : function(){ + return this.$( '.open-more-options.btn' ).popover({ + container : '.header', + placement : 'bottom', + html : true, + content : $( this.optionsPopoverTemplate( this ) ) + }); + }, /** Adjust the height of the first, current column since flex-boxes won't work with fixed postiion elements */ _recalcFirstColumnHeight : function(){ @@ -1052,18 +923,17 @@ /** returns the columns currently in the viewport */ columnsInView : function(){ //TODO: uses offset which is render intensive -//TODO: 2N - could use arg filter (sortedFilteredColumns( filter )) instead + //TODO: 2N - could use arg filter (sortedFilteredColumns( filter )) instead var vp = this._viewport(); return this.sortedFilteredColumns().filter( function( column ){ return column.currentHistory || column.inView( vp.left, vp.right ); }); }, -//TODO: sortByInView - return cols in view, then others + //TODO: sortByInView - return cols in view, then others /** trigger in-view from columns in-view */ checkColumnsInView : function(){ -//TODO: assbackward -//console.debug( 'checking columns in view', this.columnsInView() ); + //TODO: assbackward - don't fire from the column, fire from here and listen from here this.columnsInView().forEach( function( column ){ column.trigger( 'in-view', column ); }); @@ -1073,7 +943,7 @@ currentColumnDropTargetOn : function(){ var currentColumn = this.columnMap[ this.currentHistoryId ]; if( !currentColumn ){ return; } -//TODO: fix this - shouldn't need monkeypatch + //TODO: fix this - shouldn't need monkeypatch currentColumn.panel.dataDropped = function( data ){}; currentColumn.panel.dropTargetOn(); }, @@ -1090,7 +960,73 @@ /** String rep */ toString : function(){ return 'MultiPanelColumns(' + ( this.columns? this.columns.length : 0 ) + ')'; - } + }, + + // ------------------------------------------------------------------------ templates + mainTemplate : _.template([ + '<div class="header flex-column-container">', + '<div class="control-column control-column-left flex-column">', + '<button class="create-new btn btn-default">', _l( 'Create new' ), '</button> ', + '<div id="search-histories" class="search-control"></div>', + '<div id="search-datasets" class="search-control"></div>', + '<button class="open-more-options btn btn-default">', + //TODO: tip not working + '<span class="fa fa-ellipsis-h" title="More options"></span>', + '</button>', + '</div>', + // feedback + '<div class="control-column control-column-center flex-column">', + '<div class="header-info">', + '</div>', + '</div>', + '<div class="control-column control-column-right flex-column">', + '<button class="done btn btn-default">', _l( 'Done' ), '</button>', + '</div>', + '</div>', + // middle - where the columns go + '<div class="outer-middle flex-row flex-row-container">', + '<div class="middle flex-column-container flex-row"></div>', + '</div>', + // footer + '<div class="footer flex-column-container">', + '</div>' + ].join(''), { variable: 'view' }), + + optionsPopoverTemplate : _.template([ + '<div class="more-options">', + '<div class="checkbox"><label><input id="include-deleted" type="checkbox"', + '<%= view.collection.includeDeleted? " checked" : "" %>>', + _l( 'Include deleted histories' ), + '</label></div>', + + '<div class="order btn-group">', + '<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">', + _l( 'Order histories by' ) + ' ', + '<span class="current-order"><%= view.order %></span> ', + '<span class="caret"></span>', + '</button>', + '<ul class="dropdown-menu" role="menu">', + '<li><a href="javascript:void(0);" class="order-update">', + _l( 'Time of last update' ), + '</a></li>', + '<li><a href="javascript:void(0);" class="order-name">', + _l( 'Name' ), + '</a></li>', + '<li><a href="javascript:void(0);" class="order-size">', + _l( 'Size' ), + '</a></li>', + '</ul>', + '</div>', + + '<div class="checkbox"><label><input id="toggle-deleted" type="checkbox">', + _l( 'Include deleted datasets' ), + '</label></div>', + '<div class="checkbox"><label><input id="toggle-hidden" type="checkbox">', + _l( 'Include hidden datasets' ), + '</label></div>', + '</div>' + ].join(''), { variable: 'view' }), + }); diff -r 5b2f1eab281920eca6c9d1b84e6cebf02866ea09 -r a99aafec2a89d95795cf1296ddf43c9fd6a38c8e static/scripts/packed/mvc/history/multi-panel.js --- a/static/scripts/packed/mvc/history/multi-panel.js +++ b/static/scripts/packed/mvc/history/multi-panel.js @@ -1,1 +1,1 @@ -define(["mvc/history/history-model","mvc/history/history-panel-edit","mvc/base-mvc","utils/ajax-queue","ui/mode-button","ui/search-input"],function(d,l,z,a){window.HISTORY_MODEL=d;function g(I,E){E=E||{};if(!(Galaxy&&Galaxy.modal)){return I.copy()}var F=I.get("name"),C="Copy of '"+F+"'";function D(K){if(!K){if(!Galaxy.modal.$("#invalid-title").size()){var J=$("<p/>").attr("id","invalid-title").css({color:"red","margin-top":"8px"}).addClass("bg-danger").text(_l("Please enter a valid history title"));Galaxy.modal.$(".modal-body").append(J)}return false}return K}function G(J){var K=$('<p><span class="fa fa-spinner fa-spin"></span> Copying history...</p>').css("margin-top","8px");Galaxy.modal.$(".modal-body").append(K);I.copy(true,J).fail(function(){alert(_l("History could not be copied. Please contact a Galaxy administrator"))}).always(function(){Galaxy.modal.hide()})}function H(){var J=Galaxy.modal.$("#copy-modal-title").val();if(!D(J)){return}G(J)}Galaxy.modal.show(_.extend({title:_l("Copying history")+' "'+F+'"',body:$(['<label for="copy-modal-title">',_l("Enter a title for the copied history"),":","</label><br />",'<input id="copy-modal-title" class="form-control" style="width: 100%" value="',C,'" />'].join("")),buttons:{Cancel:function(){Galaxy.modal.hide()},Copy:H}},E));$("#copy-modal-title").focus().select();$("#copy-modal-title").on("keydown",function(J){if(J.keyCode===13){H()}})}var B=Backbone.View.extend(z.LoggableMixin).extend({tagName:"div",className:"history-column flex-column flex-row-container",id:function q(){if(!this.model){return""}return"history-column-"+this.model.get("id")},initialize:function c(C){C=C||{};this.panel=C.panel||this.createPanel(C);this.setUpListeners()},createPanel:function u(D){D=_.extend({model:this.model,dragItems:true},D);var C=new l.HistoryPanelEdit(D);C._renderEmptyMessage=this.__patch_renderEmptyMessage;return C},__patch_renderEmptyMessage:function(E){var D=this,F=_.chain(this.model.get("state_ids")).values().flatten().value().length,C=D.$emptyMessage(E);if(!_.isEmpty(D.hdaViews)){C.hide()}else{if(F&&!this.model.contents.length){C.empty().append($('<span class="fa fa-spinner fa-spin"></span><i>loading datasets...</i>')).show()}else{if(D.searchFor){C.text(D.noneFoundMsg).show()}else{C.text(D.emptyMsg).show()}}}return C},setUpListeners:function f(){var C=this;this.once("rendered",function(){C.trigger("rendered:initial",C)});this.setUpPanelListeners()},setUpPanelListeners:function k(){var C=this;this.listenTo(this.panel,{rendered:function(){C.trigger("rendered",C)}},this)},inView:function(C,D){var F=this.$el.offset().left,E=F+this.$el.width();if(E<C){return false}if(F>D){return false}return true},$panel:function e(){return this.$(".history-panel")},render:function A(D){D=(D!==undefined)?(D):("fast");var C=this.model?this.model.toJSON():{};this.$el.html(this.template(C));this.renderPanel(D);this.setUpBehaviors();return this},setUpBehaviors:function v(){},template:function w(D){D=D||{};var C=['<div class="panel-controls clear flex-row">',this.controlsLeftTemplate(),'<div class="pull-right">','<button class="delete-history btn btn-default">',D.deleted?_l("Undelete"):_l("Delete"),"</button>",'<button class="copy-history btn btn-default">',_l("Copy"),"</button>","</div>","</div>",'<div class="inner flex-row flex-column-container">','<div id="history-',D.id,'" class="history-column history-panel flex-column"></div>',"</div>"].join("");return $(C)},controlsLeftTemplate:function(){return(this.currentHistory)?['<div class="pull-left">','<button class="create-new btn btn-default">',_l("Create new"),"</button> ","</div>"].join(""):['<div class="pull-left">','<button class="switch-to btn btn-default">',_l("Switch to"),"</button>","</div>"].join("")},renderPanel:function h(C){C=(C!==undefined)?(C):("fast");this.panel.setElement(this.$panel()).render(C);return this},events:{"click .switch-to.btn":function(){this.model.setAsCurrent()},"click .delete-history.btn":function(){var C=this,D;if(this.model.get("deleted")){D=this.model.undelete()}else{D=this.model._delete()}D.fail(function(G,E,F){alert(_l("Could not delete the history")+":\n"+F)}).done(function(E){C.render()})},"click .copy-history.btn":"copy"},copy:function s(){g(this.model)},toString:function(){return"HistoryPanelColumn("+(this.panel?this.panel:"")+")"}});var m=Backbone.View.extend(z.LoggableMixin).extend({initialize:function c(C){C=C||{};this.log(this+".init",C);if(!C.currentHistoryId){throw new Error(this+" requires a currentHistoryId in the options")}this.currentHistoryId=C.currentHistoryId;this.options={columnWidth:312,borderWidth:1,columnGap:8,headerHeight:29,footerHeight:0,controlsHeight:20};this.order=C.order||"update";this.hdaQueue=new a.NamedAjaxQueue([],false);this.collection=null;this.setCollection(C.histories||[]);this.columnMap={};this.createColumns(C.columnOptions);this.setUpListeners()},setUpListeners:function f(){},setCollection:function y(D){var C=this;C.stopListening(C.collection);C.collection=D;C.sortCollection(C.order,{silent:true});C.setUpCollectionListeners();C.trigger("new-collection",C);return C},setUpCollectionListeners:function(){var C=this,D=C.collection;C.listenTo(D,{add:C.addAsCurrentColumn,"set-as-current":C.setCurrentHistory,"change:deleted":C.handleDeletedHistory,sort:function(){C.renderColumns(0)}})},setCurrentHistory:function p(D){var C=this.columnMap[this.currentHistoryId];if(C){C.currentHistory=false;C.$el.height("")}this.currentHistoryId=D.id;var E=this.columnMap[this.currentHistoryId];E.currentHistory=true;this.sortCollection();multipanel._recalcFirstColumnHeight();return E},handleDeletedHistory:function b(D){if(D.get("deleted")){this.log("handleDeletedHistory",this.collection.includeDeleted,D);var C=this;column=C.columnMap[D.id];if(!column){return}if(column.model.id===this.currentHistoryId){}else{if(!C.collection.includeDeleted){C.removeColumn(column)}}}},sortCollection:function(C,D){C=C||this.order;var E=this.currentHistoryId;switch(C){case"name":this.collection.comparator=function(F){return[F.id!==E,F.get("name").toLowerCase()]};break;case"size":this.collection.comparator=function(F){return[F.id!==E,F.get("size")]};break;default:this.collection.comparator=function(F){return[F.id!==E,Date(F.get("update_time"))]}}this.collection.sort(D);return this.collection},setOrder:function(C){if(["update","name","size"].indexOf(C)===-1){C="update"}this.order=C;this.sortCollection();return this},create:function(C){return this.collection.create({current:true})},createColumns:function r(D){D=D||{};var C=this;this.columnMap={};C.collection.each(function(E,F){var G=C.createColumn(E,D);C.columnMap[E.id]=G})},createColumn:function t(E,C){C=_.extend({},C,{model:E});var D=new B(C);if(E.id===this.currentHistoryId){D.currentHistory=true}this.setUpColumnListeners(D);return D},sortedFilteredColumns:function(C){C=C||this.filters;if(!C||!C.length){return this.sortedColumns()}var D=this;return D.sortedColumns().filter(function(G,F){var E=G.currentHistory||_.every(C.map(function(H){return H.call(G)}));return E})},sortedColumns:function(){var D=this;var C=this.collection.map(function(F,E){return D.columnMap[F.id]});return C},addColumn:function o(E,C){C=C!==undefined?C:true;var D=this.createColumn(E);this.columnMap[E.id]=D;if(C){this.renderColumns()}return D},addAsCurrentColumn:function o(E){var D=this,C=this.addColumn(E,false);this.setCurrentHistory(E);C.once("rendered",function(){D.queueHdaFetch(C)});return C},removeColumn:function x(E,D){D=D!==undefined?D:true;this.log("removeColumn",E);if(!E){return}var F=this,C=this.options.columnWidth+this.options.columnGap;E.$el.fadeOut("fast",function(){if(D){$(this).remove();F.$(".middle").width(F.$(".middle").width()-C);F.checkColumnsInView();F._recalcFirstColumnHeight()}F.stopListening(E.panel);F.stopListening(E);delete F.columnMap[E.model.id];E.remove()})},setUpColumnListeners:function n(C){var D=this;D.listenTo(C,{"in-view":D.queueHdaFetch});D.listenTo(C.panel,{"view:draggable:dragstart":function(H,F,E,G){D._dropData=JSON.parse(H.dataTransfer.getData("text"));D.currentColumnDropTargetOn()},"view:draggable:dragend":function(H,F,E,G){D._dropData=null;D.currentColumnDropTargetOff()},"droptarget:drop":function(G,H,F){var I=D._dropData.filter(function(J){return(_.isObject(J)&&J.id&&J.model_class==="HistoryDatasetAssociation")});D._dropData=null;var E=new a.NamedAjaxQueue();I.forEach(function(J){E.add({name:"copy-"+J.id,fn:function(){return F.model.contents.copy(J.id)}})});E.start();E.done(function(J){F.model.fetch()})}})},columnMapLength:function(){return Object.keys(this.columnMap).length},render:function A(D){D=D!==undefined?D:this.fxSpeed;var C=this;C.log(C+".render");C.$el.html(C.template(C.options));C.renderColumns(D);C.setUpBehaviors();C.trigger("rendered",C);return C},template:function w(C){C=C||{};var D=[];if(this.options.headerHeight){D=D.concat(['<div class="loading-overlay flex-row"><div class="loading-overlay-message">loading...</div></div>','<div class="header flex-column-container">','<div class="header-control header-control-left flex-column">','<button class="done btn btn-default">',_l("Done"),"</button>",'<button class="include-deleted btn btn-default"></button>','<div class="order btn-group">','<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">',_l("Order histories by")+'... <span class="caret"></span>',"</button>",'<ul class="dropdown-menu" role="menu">','<li><a href="javascript:void(0);" class="order-update">',_l("Time of last update"),"</a></li>",'<li><a href="javascript:void(0);" class="order-name">',_l("Name"),"</a></li>",'<li><a href="javascript:void(0);" class="order-size">',_l("Size"),"</a></li>","</ul>","</div>",'<div id="search-histories" class="header-search"></div>',"</div>",'<div class="header-control header-control-center flex-column">','<div class="header-info">',"</div>","</div>",'<div class="header-control header-control-right flex-column">','<div id="search-datasets" class="header-search"></div>','<button id="toggle-deleted" class="btn btn-default">',_l("Include deleted datasets"),"</button>",'<button id="toggle-hidden" class="btn btn-default">',_l("Include hidden datasets"),"</button>","</div>","</div>"])}D=D.concat(['<div class="outer-middle flex-row flex-row-container">','<div class="middle flex-column-container flex-row"></div>',"</div>",'<div class="footer flex-column-container">',"</div>"]);return $(D.join(""))},renderColumns:function j(F){F=F!==undefined?F:this.fxSpeed;var E=this,C=E.sortedFilteredColumns();E.$(".middle").width(C.length*(this.options.columnWidth+this.options.columnGap)+this.options.columnGap+16);var D=E.$(".middle");D.empty();C.forEach(function(H,G){H.$el.appendTo(D);H.delegateEvents();E.renderColumn(H,F)});if(this.searchFor&&C.length<=1){}else{E.checkColumnsInView();this._recalcFirstColumnHeight()}return C},renderColumn:function(C,D){D=D!==undefined?D:this.fxSpeed;return C.render(D)},queueHdaFetch:function i(E){if(E.model.contents.length===0&&!E.model.get("empty")){var C={},D=_.values(E.panel.storage.get("expandedIds")).join();if(D){C.dataset_details=D}this.hdaQueue.add({name:E.model.id,fn:function(){var F=E.model.contents.fetch({data:C,silent:true});return F.done(function(G){E.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},queueHdaFetchDetails:function(C){if((C.model.contents.length===0&&!C.model.get("empty"))||(!C.model.contents.haveDetails())){this.hdaQueue.add({name:C.model.id,fn:function(){var D=C.model.contents.fetch({data:{details:"all"},silent:true});return D.done(function(E){C.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},renderInfo:function(C){this.$(".header .header-info").text(C)},events:{"click .done.btn":"close","click .create-new.btn":"create","click .order .order-update":function(C){this.setOrder("update")},"click .order .order-name":function(C){this.setOrder("name")},"click .order .order-size":function(C){this.setOrder("size")}},close:function(D){var C="/";if(Galaxy&&Galaxy.options&&Galaxy.options.root){C=Galaxy.options.root}else{if(galaxy_config&&galaxy_config.root){C=galaxy_config.root}}window.location=C},includeDeletedHistories:function(){window.location+=(/\?/.test(window.location.toString()))?("&"):("?")+"include_deleted_histories=True"},excludeDeletedHistories:function(){window.location=window.location.toString().replace(/[&\?]include_deleted_histories=True/g,"")},setUpBehaviors:function(){var D=this;D.$(".include-deleted").modeButton({initialMode:this.collection.includeDeleted?"exclude":"include",switchModesOnClick:false,modes:[{mode:"include",html:_l("Include deleted histories"),onclick:_.bind(D.includeDeletedHistories,D)},{mode:"exclude",html:_l("Exclude deleted histories"),onclick:_.bind(D.excludeDeletedHistories,D)}]});D.$("#search-histories").searchInput({name:"search-histories",placeholder:_l("search histories"),onsearch:function(E){D.searchFor=E;D.filters=[function(){return this.model.matchesAll(D.searchFor)}];D.renderColumns(0)},onclear:function(E){D.searchFor=null;D.filters=[];D.renderColumns(0)}});D.$("#search-datasets").searchInput({name:"search-datasets",placeholder:_l("search all datasets"),onfirstsearch:function(E){D.hdaQueue.clear();D.$("#search-datasets").searchInput("toggle-loading");D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E);D.queueHdaFetchDetails(F)});D.hdaQueue.progress(function(F){D.renderInfo([_l("searching"),(F.curr+1),_l("of"),F.total].join(" "))});D.hdaQueue.deferred.done(function(){D.renderInfo("");D.$("#search-datasets").searchInput("toggle-loading")})},onsearch:function(E){D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E)})},onclear:function(E){D.searchFor=null;D.sortedFilteredColumns().forEach(function(F){F.panel.clearSearch()})}});D.$("#toggle-deleted").modeButton({initialMode:"include",modes:[{mode:"exclude",html:_l("Exclude deleted datasets")},{mode:"include",html:_l("Include deleted datasets")}]}).click(function(){var E=$(this).modeButton("getMode").mode==="exclude";D.sortedFilteredColumns().forEach(function(G,F){_.delay(function(){G.panel.toggleShowDeleted(E,false)},F*200)})});D.$("#toggle-hidden").modeButton({initialMode:"include",modes:[{mode:"exclude",html:_l("Exclude hidden datasets")},{mode:"include",html:_l("Include hidden datasets")}]}).click(function(){var E=$(this).modeButton("getMode").mode==="exclude";D.sortedFilteredColumns().forEach(function(G,F){_.delay(function(){G.panel.toggleShowHidden(E,false)},F*200)})});$(window).resize(function(){D._recalcFirstColumnHeight()});var C=_.debounce(_.bind(this.checkColumnsInView,this),100);this.$(".middle").parent().scroll(C)},_recalcFirstColumnHeight:function(){var C=this.$(".history-column").first(),E=this.$(".middle").height(),D=C.find(".panel-controls").height();C.height(E).find(".inner").height(E-D)},_viewport:function(){var C=this.$(".middle").parent().offset().left;return{left:C,right:C+this.$(".middle").parent().width()}},columnsInView:function(){var C=this._viewport();return this.sortedFilteredColumns().filter(function(D){return D.currentHistory||D.inView(C.left,C.right)})},checkColumnsInView:function(){this.columnsInView().forEach(function(C){C.trigger("in-view",C)})},currentColumnDropTargetOn:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=function(D){};C.panel.dropTargetOn()},currentColumnDropTargetOff:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=l.HistoryPanelEdit.prototype.dataDrop;C.panel.dropTargetOff()},toString:function(){return"MultiPanelColumns("+(this.columns?this.columns.length:0)+")"}});return{MultiPanelColumns:m}}); \ No newline at end of file +define(["mvc/history/history-model","mvc/history/history-panel-edit","mvc/base-mvc","utils/ajax-queue","ui/mode-button","ui/search-input"],function(d,l,z,a){function g(I,E){E=E||{};if(!(Galaxy&&Galaxy.modal)){return I.copy()}var F=I.get("name"),C="Copy of '"+F+"'";function D(K){if(!K){if(!Galaxy.modal.$("#invalid-title").size()){var J=$("<p/>").attr("id","invalid-title").css({color:"red","margin-top":"8px"}).addClass("bg-danger").text(_l("Please enter a valid history title"));Galaxy.modal.$(".modal-body").append(J)}return false}return K}function G(J){var K=$('<p><span class="fa fa-spinner fa-spin"></span> Copying history...</p>').css("margin-top","8px");Galaxy.modal.$(".modal-body").append(K);I.copy(true,J).fail(function(){alert(_l("History could not be copied. Please contact a Galaxy administrator"))}).always(function(){Galaxy.modal.hide()})}function H(){var J=Galaxy.modal.$("#copy-modal-title").val();if(!D(J)){return}G(J)}Galaxy.modal.show(_.extend({title:_l("Copying history")+' "'+F+'"',body:$(['<label for="copy-modal-title">',_l("Enter a title for the copied history"),":","</label><br />",'<input id="copy-modal-title" class="form-control" style="width: 100%" value="',C,'" />'].join("")),buttons:{Cancel:function(){Galaxy.modal.hide()},Copy:H}},E));$("#copy-modal-title").focus().select();$("#copy-modal-title").on("keydown",function(J){if(J.keyCode===13){H()}})}var B=Backbone.View.extend(z.LoggableMixin).extend({tagName:"div",className:"history-column flex-column flex-row-container",id:function q(){if(!this.model){return""}return"history-column-"+this.model.get("id")},initialize:function c(C){C=C||{};this.panel=C.panel||this.createPanel(C);this.setUpListeners()},createPanel:function u(D){D=_.extend({model:this.model,dragItems:true},D);var C=new l.HistoryPanelEdit(D);C._renderEmptyMessage=this.__patch_renderEmptyMessage;return C},__patch_renderEmptyMessage:function(E){var D=this,F=_.chain(this.model.get("state_ids")).values().flatten().value().length,C=D.$emptyMessage(E);if(!_.isEmpty(D.hdaViews)){C.hide()}else{if(F&&!this.model.contents.length){C.empty().append($('<span class="fa fa-spinner fa-spin"></span><i>loading datasets...</i>')).show()}else{if(D.searchFor){C.text(D.noneFoundMsg).show()}else{C.text(D.emptyMsg).show()}}}return C},setUpListeners:function f(){var C=this;this.once("rendered",function(){C.trigger("rendered:initial",C)});this.setUpPanelListeners()},setUpPanelListeners:function k(){var C=this;this.listenTo(this.panel,{rendered:function(){C.trigger("rendered",C)}},this)},inView:function(C,D){var F=this.$el.offset().left,E=F+this.$el.width();if(E<C){return false}if(F>D){return false}return true},$panel:function e(){return this.$(".history-panel")},render:function A(D){D=(D!==undefined)?(D):("fast");var C=this.model?this.model.toJSON():{};this.$el.html(this.template(C));this.renderPanel(D);this.setUpBehaviors();return this},setUpBehaviors:function v(){},template:function w(D){D=D||{};var C=['<div class="panel-controls clear flex-row">',this.controlsLeftTemplate(),'<div class="pull-right">','<button class="delete-history btn btn-default">',D.deleted?_l("Undelete"):_l("Delete"),"</button>",'<button class="copy-history btn btn-default">',_l("Copy"),"</button>","</div>","</div>",'<div class="inner flex-row flex-column-container">','<div id="history-',D.id,'" class="history-column history-panel flex-column"></div>',"</div>"].join("");return $(C)},controlsLeftTemplate:function(){return(this.currentHistory)?['<div class="pull-left">','<strong class="current-label">',_l("Current History"),"</strong>","</div>"].join(""):['<div class="pull-left">','<button class="switch-to btn btn-default">',_l("Switch to"),"</button>","</div>"].join("")},renderPanel:function h(C){C=(C!==undefined)?(C):("fast");this.panel.setElement(this.$panel()).render(C);return this},events:{"click .switch-to.btn":function(){this.model.setAsCurrent()},"click .delete-history.btn":function(){var C=this,D;if(this.model.get("deleted")){D=this.model.undelete()}else{D=this.model._delete()}D.fail(function(G,E,F){alert(_l("Could not delete the history")+":\n"+F)}).done(function(E){C.render()})},"click .copy-history.btn":"copy"},copy:function s(){g(this.model)},toString:function(){return"HistoryPanelColumn("+(this.panel?this.panel:"")+")"}});var m=Backbone.View.extend(z.LoggableMixin).extend({className:"multi-history-columns",initialize:function c(C){C=C||{};this.log(this+".init",C);if(!C.currentHistoryId){throw new Error(this+" requires a currentHistoryId in the options")}this.currentHistoryId=C.currentHistoryId;this.options={columnWidth:312,borderWidth:1,columnGap:8,headerHeight:29,footerHeight:0,controlsHeight:20};this.order=C.order||"update";this.hdaQueue=new a.NamedAjaxQueue([],false);this.collection=null;this.setCollection(C.histories||[]);this.columnMap={};this.createColumns(C.columnOptions);this.setUpListeners()},setUpListeners:function f(){},setCollection:function y(D){var C=this;C.stopListening(C.collection);C.collection=D;C.sortCollection(C.order,{silent:true});C.setUpCollectionListeners();C.trigger("new-collection",C);return C},setUpCollectionListeners:function(){var C=this,D=C.collection;C.listenTo(D,{add:C.addAsCurrentColumn,"set-as-current":C.setCurrentHistory,"change:deleted":C.handleDeletedHistory,sort:function(){C.renderColumns(0)}})},setCurrentHistory:function p(D){var C=this.columnMap[this.currentHistoryId];if(C){C.currentHistory=false;C.$el.height("")}this.currentHistoryId=D.id;var E=this.columnMap[this.currentHistoryId];E.currentHistory=true;this.sortCollection();multipanel._recalcFirstColumnHeight();return E},handleDeletedHistory:function b(D){if(D.get("deleted")){this.log("handleDeletedHistory",this.collection.includeDeleted,D);var C=this;column=C.columnMap[D.id];if(!column){return}if(column.model.id===this.currentHistoryId){}else{if(!C.collection.includeDeleted){C.removeColumn(column)}}}},sortCollection:function(C,D){C=C||this.order;var E=this.currentHistoryId;switch(C){case"name":this.collection.comparator=function(F){return[F.id!==E,F.get("name").toLowerCase()]};break;case"size":this.collection.comparator=function(F){return[F.id!==E,F.get("size")]};break;default:this.collection.comparator=function(F){return[F.id!==E,Date(F.get("update_time"))]}}this.collection.sort(D);return this.collection},setOrder:function(C){if(["update","name","size"].indexOf(C)===-1){C="update"}this.order=C;this.trigger("order:change",C,this);this.$(".current-order").text(C);this.sortCollection();return this},create:function(C){return this.collection.create({current:true})},createColumns:function r(D){D=D||{};var C=this;this.columnMap={};C.collection.each(function(E,F){var G=C.createColumn(E,D);C.columnMap[E.id]=G})},createColumn:function t(E,C){C=_.extend({},C,{model:E});var D=new B(C);if(E.id===this.currentHistoryId){D.currentHistory=true}this.setUpColumnListeners(D);return D},sortedFilteredColumns:function(C){C=C||this.filters;if(!C||!C.length){return this.sortedColumns()}var D=this;return D.sortedColumns().filter(function(G,F){var E=G.currentHistory||_.every(C.map(function(H){return H.call(G)}));return E})},sortedColumns:function(){var D=this;var C=this.collection.map(function(F,E){return D.columnMap[F.id]});return C},addColumn:function o(E,C){C=C!==undefined?C:true;var D=this.createColumn(E);this.columnMap[E.id]=D;if(C){this.renderColumns()}return D},addAsCurrentColumn:function o(E){var D=this,C=this.addColumn(E,false);this.setCurrentHistory(E);C.once("rendered",function(){D.queueHdaFetch(C)});return C},removeColumn:function x(E,D){D=D!==undefined?D:true;this.log("removeColumn",E);if(!E){return}var F=this,C=this.options.columnWidth+this.options.columnGap;E.$el.fadeOut("fast",function(){if(D){$(this).remove();F.$(".middle").width(F.$(".middle").width()-C);F.checkColumnsInView();F._recalcFirstColumnHeight()}F.stopListening(E.panel);F.stopListening(E);delete F.columnMap[E.model.id];E.remove()})},setUpColumnListeners:function n(C){var D=this;D.listenTo(C,{"in-view":D.queueHdaFetch});D.listenTo(C.panel,{"view:draggable:dragstart":function(H,F,E,G){D._dropData=JSON.parse(H.dataTransfer.getData("text"));D.currentColumnDropTargetOn()},"view:draggable:dragend":function(H,F,E,G){D._dropData=null;D.currentColumnDropTargetOff()},"droptarget:drop":function(G,H,F){var I=D._dropData.filter(function(J){return(_.isObject(J)&&J.id&&J.model_class==="HistoryDatasetAssociation")});D._dropData=null;var E=new a.NamedAjaxQueue();I.forEach(function(J){E.add({name:"copy-"+J.id,fn:function(){return F.model.contents.copy(J.id)}})});E.start();E.done(function(J){F.model.fetch()})}})},columnMapLength:function(){return Object.keys(this.columnMap).length},render:function A(D){D=D!==undefined?D:this.fxSpeed;var C=this;C.log(C+".render");C.$el.html(C.mainTemplate(C));C.renderColumns(D);C.setUpBehaviors();C.trigger("rendered",C);return C},renderColumns:function j(F){F=F!==undefined?F:this.fxSpeed;var E=this,C=E.sortedFilteredColumns();E.$(".middle").width(C.length*(this.options.columnWidth+this.options.columnGap)+this.options.columnGap+16);var D=E.$(".middle");D.empty();C.forEach(function(H,G){H.$el.appendTo(D);H.delegateEvents();E.renderColumn(H,F)});if(this.searchFor&&C.length<=1){}else{E.checkColumnsInView();this._recalcFirstColumnHeight()}return C},renderColumn:function(C,D){D=D!==undefined?D:this.fxSpeed;return C.render(D)},queueHdaFetch:function i(E){if(E.model.contents.length===0&&!E.model.get("empty")){var C={},D=_.values(E.panel.storage.get("expandedIds")).join();if(D){C.dataset_details=D}this.hdaQueue.add({name:E.model.id,fn:function(){var F=E.model.contents.fetch({data:C,silent:true});return F.done(function(G){E.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},queueHdaFetchDetails:function(C){if((C.model.contents.length===0&&!C.model.get("empty"))||(!C.model.contents.haveDetails())){this.hdaQueue.add({name:C.model.id,fn:function(){var D=C.model.contents.fetch({data:{details:"all"},silent:true});return D.done(function(E){C.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},renderInfo:function(C){this.$(".header .header-info").text(C)},events:{"click .done.btn":"close","click .create-new.btn":"create","click #include-deleted":"_clickToggleDeletedHistories","click .order .order-update":function(C){this.setOrder("update")},"click .order .order-name":function(C){this.setOrder("name")},"click .order .order-size":function(C){this.setOrder("size")},"click #toggle-deleted":"_clickToggleDeletedDatasets","click #toggle-hidden":"_clickToggleHiddenDatasets"},close:function(D){var C="/";if(Galaxy&&Galaxy.options&&Galaxy.options.root){C=Galaxy.options.root}else{if(galaxy_config&&galaxy_config.root){C=galaxy_config.root}}window.location=C},_clickToggleDeletedHistories:function(C){return this.toggleDeletedHistories($(C.currentTarget).is(":checked"))},toggleDeletedHistories:function(C){if(C){window.location=Galaxy.options.root+"history/view_multiple?include_deleted_histories=True"}else{window.location=Galaxy.options.root+"history/view_multiple"}},_clickToggleDeletedDatasets:function(C){return this.toggleDeletedDatasets($(C.currentTarget).is(":checked"))},toggleDeletedDatasets:function(C){C=C!==undefined?C:false;var D=this;D.sortedFilteredColumns().forEach(function(F,E){_.delay(function(){F.panel.toggleShowDeleted(C,false)},E*200)})},_clickToggleHiddenDatasets:function(C){return this.toggleHiddenDatasets($(C.currentTarget).is(":checked"))},toggleHiddenDatasets:function(C){C=C!==undefined?C:false;var D=this;D.sortedFilteredColumns().forEach(function(F,E){_.delay(function(){F.panel.toggleShowHidden(C,false)},E*200)})},setUpBehaviors:function(){var D=this;D._moreOptionsPopover();D.$("#search-histories").searchInput({name:"search-histories",placeholder:_l("search histories"),onsearch:function(E){D.searchFor=E;D.filters=[function(){return this.model.matchesAll(D.searchFor)}];D.renderColumns(0)},onclear:function(E){D.searchFor=null;D.filters=[];D.renderColumns(0)}});D.$("#search-datasets").searchInput({name:"search-datasets",placeholder:_l("search all datasets"),onfirstsearch:function(E){D.hdaQueue.clear();D.$("#search-datasets").searchInput("toggle-loading");D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E);D.queueHdaFetchDetails(F)});D.hdaQueue.progress(function(F){D.renderInfo([_l("searching"),(F.curr+1),_l("of"),F.total].join(" "))});D.hdaQueue.deferred.done(function(){D.renderInfo("");D.$("#search-datasets").searchInput("toggle-loading")})},onsearch:function(E){D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E)})},onclear:function(E){D.searchFor=null;D.sortedFilteredColumns().forEach(function(F){F.panel.clearSearch()})}});$(window).resize(function(){D._recalcFirstColumnHeight()});var C=_.debounce(_.bind(this.checkColumnsInView,this),100);this.$(".middle").parent().scroll(C)},_moreOptionsPopover:function(){return this.$(".open-more-options.btn").popover({container:".header",placement:"bottom",html:true,content:$(this.optionsPopoverTemplate(this))})},_recalcFirstColumnHeight:function(){var C=this.$(".history-column").first(),E=this.$(".middle").height(),D=C.find(".panel-controls").height();C.height(E).find(".inner").height(E-D)},_viewport:function(){var C=this.$(".middle").parent().offset().left;return{left:C,right:C+this.$(".middle").parent().width()}},columnsInView:function(){var C=this._viewport();return this.sortedFilteredColumns().filter(function(D){return D.currentHistory||D.inView(C.left,C.right)})},checkColumnsInView:function(){this.columnsInView().forEach(function(C){C.trigger("in-view",C)})},currentColumnDropTargetOn:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=function(D){};C.panel.dropTargetOn()},currentColumnDropTargetOff:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=l.HistoryPanelEdit.prototype.dataDrop;C.panel.dropTargetOff()},toString:function(){return"MultiPanelColumns("+(this.columns?this.columns.length:0)+")"},mainTemplate:_.template(['<div class="header flex-column-container">','<div class="control-column control-column-left flex-column">','<button class="create-new btn btn-default">',_l("Create new"),"</button> ",'<div id="search-histories" class="search-control"></div>','<div id="search-datasets" class="search-control"></div>','<button class="open-more-options btn btn-default">','<span class="fa fa-ellipsis-h" title="More options"></span>',"</button>","</div>",'<div class="control-column control-column-center flex-column">','<div class="header-info">',"</div>","</div>",'<div class="control-column control-column-right flex-column">','<button class="done btn btn-default">',_l("Done"),"</button>","</div>","</div>",'<div class="outer-middle flex-row flex-row-container">','<div class="middle flex-column-container flex-row"></div>',"</div>",'<div class="footer flex-column-container">',"</div>"].join(""),{variable:"view"}),optionsPopoverTemplate:_.template(['<div class="more-options">','<div class="checkbox"><label><input id="include-deleted" type="checkbox"','<%= view.collection.includeDeleted? " checked" : "" %>>',_l("Include deleted histories"),"</label></div>",'<div class="order btn-group">','<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">',_l("Order histories by")+" ",'<span class="current-order"><%= view.order %></span> ','<span class="caret"></span>',"</button>",'<ul class="dropdown-menu" role="menu">','<li><a href="javascript:void(0);" class="order-update">',_l("Time of last update"),"</a></li>",'<li><a href="javascript:void(0);" class="order-name">',_l("Name"),"</a></li>",'<li><a href="javascript:void(0);" class="order-size">',_l("Size"),"</a></li>","</ul>","</div>",'<div class="checkbox"><label><input id="toggle-deleted" type="checkbox">',_l("Include deleted datasets"),"</label></div>",'<div class="checkbox"><label><input id="toggle-hidden" type="checkbox">',_l("Include hidden datasets"),"</label></div>","</div>"].join(""),{variable:"view"}),});return{MultiPanelColumns:m}}); \ No newline at end of file diff -r 5b2f1eab281920eca6c9d1b84e6cebf02866ea09 -r a99aafec2a89d95795cf1296ddf43c9fd6a38c8e templates/webapps/galaxy/history/view_multiple.mako --- a/templates/webapps/galaxy/history/view_multiple.mako +++ b/templates/webapps/galaxy/history/view_multiple.mako @@ -75,29 +75,36 @@ .flex-column { } + /* ---------------------- header & footer */ .header, .footer { width: 100%; } + /* ---------------------- header */ .header { background-color: lightgrey; min-height: ${header_height}px; max-height: ${header_height}px; } - .header-control { + .control-column { margin-top: 4px; } - .header-control-right, - .header-control-left { + + .control-column-right, + .control-column-left { margin-right: 8px; margin-left: 8px; + /*background-color: green;*/ } - .header-control-left > * { + .control-column-left > * { margin: 0px 4px 4px 0px; } + .more-options input[type=checkbox] { + margin-top: 3px; + } - .header-control-center { + .control-column-center { text-align: center; max-height: 22px; -webkit-flex: 0 1 auto; @@ -117,26 +124,35 @@ } - .header-control-right { + .control-column-right { text-align: right; } - .header-control-right > * { + .control-column-right > * { margin: 0px 0px 4px 4px; } - .header-search { + .search-control { display: inline-block; + width: 40%; } - .header-search .search-clear, - .header-search .search-loading { + .search-control .search-clear, + .search-control .search-loading { margin-top: -22px; } + .footer input.search-query, .header input.search-query { font-size: 90%; height: 21px; - padding: 2px; + line-height: normal; + padding: 2px 2px 1px 2px; } + .open-more-options { + padding: 0 4px 0 4px; + font-size: 150%; + } + + /* ---------------------- middle */ .outer-middle { overflow: auto; } @@ -169,13 +185,18 @@ align-self: auto; } .header .btn, + .footer .btn, .panel-controls .btn { - height: ${controls_height}px; - line-height: ${controls_height - 2}px; + height: 20px; + /*line-height: ${controls_height - 2}px;*/ + line-height: normal; font-size: 90%; padding-top: 0px; padding-bottom: 0px; } + .header .btn { + height: 21px; + } .panel-controls .pull-left .btn { margin-right: 4px; } @@ -183,13 +204,14 @@ margin-left: 4px; } + /* ---------------------- footer */ .footer { min-height: ${footer_height}px; max-height: ${footer_height}px; - background-color: grey; + background-color: lightgrey; } - /* ---- columns */ + /* ---------------------- columns */ .history-panel { width: 100%; margin-top: 4px; @@ -230,6 +252,12 @@ vertical-align: middle; } + .current-label { + display: inline-block; + color: grey; + padding-left: 2px; + margin-top: 2px; + } </style></%def> 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