commit/galaxy-central: carlfeberhard: History panels: persist search visibility, improve model switching, consistently apply icon button groups, fix style on only-child buttons, improve docs and private function names
1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/707ba3eee2ab/ Changeset: 707ba3eee2ab User: carlfeberhard Date: 2014-03-14 17:26:23 Summary: History panels: persist search visibility, improve model switching, consistently apply icon button groups, fix style on only-child buttons, improve docs and private function names Affected #: 11 files diff -r 1ed94d89769ed2b51232c6c714c3bb21d93cc3ce -r 707ba3eee2abb828ac262e76c5b9fbbc6c85cd03 static/scripts/mvc/history/annotated-history-panel.js --- a/static/scripts/mvc/history/annotated-history-panel.js +++ b/static/scripts/mvc/history/annotated-history-panel.js @@ -33,16 +33,23 @@ HDAViewClass : hdaBase.HDABaseView, // ------------------------------------------------------------------------ panel rendering + /** render with history data + * In this override: + * replace the datasets list with a table, + * add the history annotation, + * and move the search controls + * @returns {jQuery} dom fragment as temporary container to be swapped out later + */ renderModel : function( ){ - // huh? + // why do we need this here? why isn't className being applied? this.$el.addClass( this.className ); var $newRender = readonlyPanel.ReadOnlyHistoryPanel.prototype.renderModel.call( this ), // move datasets from div to table - $datasetsList = $newRender.find( this.datasetsSelector ), + $datasetsList = this.$datasetsList( $newRender ), $datasetsTable = $( '<table/>' ).addClass( 'datasets-list datasets-table' ); $datasetsTable.append( $datasetsList.children() ); - $newRender.find( this.datasetsSelector ).replaceWith( $datasetsTable ); - //TODO: it's possible to do this with css only, right? + $datasetsList.replaceWith( $datasetsTable ); + //TODO: it's possible to do this with css only, right? display: table-cell, etc.? // add history annotation under subtitle $newRender.find( '.history-subtitle' ).after( this.renderHistoryAnnotation() ); @@ -50,11 +57,11 @@ // hide search button, move search bar beneath controls (instead of above title), show, and set up $newRender.find( '.history-search-btn' ).hide(); $newRender.find( '.history-controls' ).after( $newRender.find( '.history-search-controls' ).show() ); - this.setUpSearchInput( $newRender.find( '.history-search-input' ) ); return $newRender; }, + /** render the history's annotation as it's own field */ renderHistoryAnnotation : function(){ var annotation = this.model.get( 'annotation' ); if( !annotation ){ return null; } @@ -63,10 +70,13 @@ ].join( '' )); }, + /** Set up/render a view for each HDA to be shown, init with model and listeners. + * In this override, add table header cells to indicate the dataset, annotation columns + */ renderHdas : function( $whereTo ){ $whereTo = $whereTo || this.$el; var hdaViews = readonlyPanel.ReadOnlyHistoryPanel.prototype.renderHdas.call( this, $whereTo ); - $whereTo.find( this.datasetsSelector ).prepend( $( '<tr/>' ).addClass( 'headers' ).append([ + this.$datasetsList( $whereTo ).prepend( $( '<tr/>' ).addClass( 'headers' ).append([ $( '<th/>' ).text( _l( 'Dataset' ) ), $( '<th/>' ).text( _l( 'Annotation' ) ) ])); @@ -74,6 +84,9 @@ }, // ------------------------------------------------------------------------ hda sub-views + /** attach an hdaView to the panel + * In this override, wrap the hdaView in a table row and cell, adding a 2nd cell for the hda annotation + */ attachHdaView : function( hdaView, $whereTo ){ $whereTo = $whereTo || this.$el; // build a row around the dataset with the std hdaView in the first cell and the annotation in the next @@ -87,7 +100,7 @@ .addClass( stateClass? stateClass.replace( '-', '-color-' ): '' ), $( '<td/>' ).addClass( 'additional-info' ).text( annotation ) ]); - $whereTo.find( this.datasetsSelector ).append( $tr ); + this.$datasetsList( $whereTo ).append( $tr ); }, // ------------------------------------------------------------------------ panel events diff -r 1ed94d89769ed2b51232c6c714c3bb21d93cc3ce -r 707ba3eee2abb828ac262e76c5b9fbbc6c85cd03 static/scripts/mvc/history/current-history-panel.js --- a/static/scripts/mvc/history/current-history-panel.js +++ b/static/scripts/mvc/history/current-history-panel.js @@ -53,10 +53,6 @@ // ......................................................................... SET UP /** Set up the view, set up storage, bind listeners to HDACollection events * @param {Object} attributes - * @config {Object} urlTemplates.hda nested object containing url templates for HDAViews - * @throws 'needs urlTemplates' if urlTemplates.history or urlTemplates.hda aren't present - * @see PersistentStorage - * @see Backbone.View#initialize */ initialize : function( attributes ){ attributes = attributes || {}; @@ -68,10 +64,6 @@ }, _.pick( attributes, _.keys( HistoryPanelPrefs.prototype.defaults ) ))); hpanel.HistoryPanel.prototype.initialize.call( this, attributes ); - if( this.model ){ - console.debug( this.model ); - this.model.checkForUpdates(); - } }, // ------------------------------------------------------------------------ loading history/hda models @@ -119,10 +111,12 @@ }, /** release/free/shutdown old models and set up panel for new models */ - setModel : function( newHistoryJSON, newHdaJSON, attributes ){ - attributes = attributes || {}; - hpanel.HistoryPanel.prototype.setModel.call( this, newHistoryJSON, newHdaJSON, attributes ); - this.model.checkForUpdates(); + setModel : function( model, attributes, render ){ + hpanel.HistoryPanel.prototype.setModel.call( this, model, attributes, render ); + if( this.model ){ + this.log( 'checking for updates' ); + this.model.checkForUpdates(); + } return this; }, @@ -148,7 +142,6 @@ this.removeHdaView( this.hdaViews[ hda.id ] ); } }, this ); - }, // ------------------------------------------------------------------------ panel rendering @@ -172,7 +165,6 @@ // fade out existing, swap with the new, fade in, set up behaviours $( panel ).queue( 'fx', [ function( next ){ - //panel.$el.fadeTo( panel.fxSpeed, 0.0001, next ); if( speed && panel.$el.is( ':visible' ) ){ panel.$el.fadeOut( speed, next ); } else { @@ -208,10 +200,19 @@ /** perform additional rendering based on preferences */ renderBasedOnPrefs : function(){ if( this.preferences.get( 'searching' ) ){ - this.showSearchControls( 0 ); + this.toggleSearchControls( 0, true ); } }, + /** In this override, save the search control visibility state to preferences */ + toggleSearchControls : function( eventOrSpeed, show ){ + var visible = hpanel.HistoryPanel.prototype.toggleSearchControls.call( this, eventOrSpeed, show ); + this.preferences.set( 'searching', visible ); + }, + + /** render the tag sub-view controller + * In this override, get and set current panel preferences when editor is used + */ _renderTags : function( $where ){ var panel = this; // render tags and show/hide based on preferences @@ -225,6 +226,9 @@ panel.preferences.set( 'tagsEditorShown', tagsEditor.hidden ); }); }, + /** render the annotation sub-view controller + * In this override, get and set current panel preferences when editor is used + */ _renderAnnotation : function( $where ){ var panel = this; // render annotation and show/hide based on preferences @@ -300,6 +304,7 @@ } }); + //============================================================================== return { CurrentHistoryPanel : CurrentHistoryPanel diff -r 1ed94d89769ed2b51232c6c714c3bb21d93cc3ce -r 707ba3eee2abb828ac262e76c5b9fbbc6c85cd03 static/scripts/mvc/history/history-panel.js --- a/static/scripts/mvc/history/history-panel.js +++ b/static/scripts/mvc/history/history-panel.js @@ -7,7 +7,7 @@ TODO: ============================================================================= */ -/** @class View/Controller for the history model. +/** @class Editable View/Controller for the history model. * @name HistoryPanel * * Allows: @@ -15,8 +15,6 @@ * changing the name * displaying and editing tags and annotations * multi-selection and operations on mulitple hdas - * Does not allow: - * changing the name * * @augments Backbone.View * @borrows LoggableMixin#logger as #logger @@ -65,8 +63,8 @@ this.model.on( 'change:nice_size', this.updateHistoryDiskSize, this ); - this.model.hdas.on( 'change:deleted', this.handleHdaDeletionChange, this ); - this.model.hdas.on( 'change:visible', this.handleHdaVisibleChange, this ); + this.model.hdas.on( 'change:deleted', this._handleHdaDeletionChange, this ); + this.model.hdas.on( 'change:visible', this._handleHdaVisibleChange, this ); this.model.hdas.on( 'change:purged', function( hda ){ // hafta get the new nice-size w/o the purged hda this.model.fetch(); @@ -74,8 +72,12 @@ }, // ------------------------------------------------------------------------ panel rendering - /** render with history data */ + /** render with history data + * In this override, add tags, annotations, and multi select + * @returns {jQuery} dom fragment as temporary container to be swapped out later + */ renderModel : function( ){ +//TODO: can't call ReadOnlyHistoryPanel? var $newRender = $( '<div/>' ); $newRender.append( HistoryPanel.templates.historyPanel( this.model.toJSON() ) ); @@ -95,6 +97,7 @@ return $newRender; }, + /** render the tags sub-view controller */ _renderTags : function( $where ){ var panel = this; this.tagsEditor = new TagsEditor({ @@ -115,6 +118,7 @@ }).appendTo( $where.find( '.history-secondary-actions' ) ) }); }, + /** render the annotation sub-view controller */ _renderAnnotation : function( $where ){ var panel = this; this.annotationEditor = new AnnotationEditor({ @@ -145,11 +149,12 @@ }); }, - /** Set up HistoryPanel js/widget behaviours */ + /** Set up HistoryPanel js/widget behaviours + * In this override, add the multi select popup menu and make the name editable + */ _setUpBehaviours : function( $where ){ - //TODO: these should be either sub-MVs, or handled by events $where = $where || this.$el; - $where.find( '[title]' ).tooltip({ placement: 'bottom' }); + readonlyPanel.ReadOnlyHistoryPanel.prototype._setUpBehaviours.call( this, $where ); // anon users shouldn't have access to any of the following if( !this.model ){ @@ -184,6 +189,9 @@ }); }, + /** return a new popup menu for choosing a multi selection action + * ajax calls made for multiple datasets are queued + */ _setUpDatasetActionsPopup : function( $where ){ var panel = this; ( new PopupMenu( $where.find( '.history-dataset-action-popup-btn' ), [ @@ -226,7 +234,7 @@ /** If this hda is deleted and we're not showing deleted hdas, remove the view * @param {HistoryDataAssociation} the hda to check */ - handleHdaDeletionChange : function( hda ){ + _handleHdaDeletionChange : function( hda ){ if( hda.get( 'deleted' ) && !this.storage.get( 'show_deleted' ) ){ this.removeHdaView( this.hdaViews[ hda.id ] ); } // otherwise, the hdaView rendering should handle it @@ -236,7 +244,7 @@ /** If this hda is hidden and we're not showing hidden hdas, remove the view * @param {HistoryDataAssociation} the hda to check */ - handleHdaVisibleChange : function( hda ){ + _handleHdaVisibleChange : function( hda ){ if( hda.hidden() && !this.storage.get( 'show_hidden' ) ){ this.removeHdaView( this.hdaViews[ hda.id ] ); } // otherwise, the hdaView rendering should handle it @@ -245,7 +253,7 @@ /** Create an HDA view for the given HDA (but leave attachment for addHdaView above) * @param {HistoryDatasetAssociation} hda */ - createHdaView : function( hda ){ + _createHdaView : function( hda ){ var hdaId = hda.get( 'id' ), hdaView = new this.HDAViewClass({ model : hda, @@ -315,7 +323,7 @@ hdaView.remove(); delete panel.hdaViews[ hdaView.model.id ]; if( _.isEmpty( panel.hdaViews ) ){ - panel.$el.find( panel.emptyMsgSelector ).fadeIn( panel.fxSpeed, function(){ + panel.$emptyMessage().fadeIn( panel.fxSpeed, function(){ panel.trigger( 'empty-history', panel ); }); } @@ -337,6 +345,7 @@ }, // ........................................................................ multi-select of hdas +//TODO: to toggle showOrHide pattern /** show selectors on all visible hdas and associated controls */ showSelectors : function( speed ){ this.selecting = true; diff -r 1ed94d89769ed2b51232c6c714c3bb21d93cc3ce -r 707ba3eee2abb828ac262e76c5b9fbbc6c85cd03 static/scripts/mvc/history/readonly-history-panel.js --- a/static/scripts/mvc/history/readonly-history-panel.js +++ b/static/scripts/mvc/history/readonly-history-panel.js @@ -15,26 +15,43 @@ }, /** add an hda id to the hash of expanded hdas */ addExpandedHda : function( id ){ - this.save( 'expandedHdas', _.extend( this.get( 'expandedHdas' ), _.object([ id ], [ true ]) ) ); + var key = 'expandedHdas'; + this.save( key, _.extend( this.get( key ), _.object([ id ], [ true ]) ) ); }, /** remove an hda id from the hash of expanded hdas */ removeExpandedHda : function( id ){ - this.save( 'expandedHdas', _.omit( this.get( 'expandedHdas' ), id ) ); + var key = 'expandedHdas'; + this.save( key, _.omit( this.get( key ), id ) ); }, toString : function(){ return 'HistoryPrefs(' + this.id + ')'; } }); +// class lvl for access w/o instantiation +HistoryPrefs.storageKeyPrefix = 'history:'; /** key string to store each histories settings under */ HistoryPrefs.historyStorageKey = function historyStorageKey( historyId ){ - // class lvl for access w/o instantiation if( !historyId ){ throw new Error( 'HistoryPrefs.historyStorageKey needs valid id: ' + historyId ); } // single point of change - return ( 'history:' + historyId ); + return ( HistoryPrefs.storageKeyPrefix + historyId ); }; +/** return the existing storage for the history with the given id (or create one if it doesn't exist) */ +HistoryPrefs.get = function get( historyId ){ + return new HistoryPrefs({ id: HistoryPrefs.historyStorageKey( historyId ) }); +}; +/** clear all history related items in sessionStorage */ +HistoryPrefs.clearAll = function clearAll( historyId ){ + for( var key in sessionStorage ){ + if( key.indexOf( HistoryPrefs.storageKeyPrefix ) === 0 ){ + sessionStorage.removeItem( key ); + } + } +}; + + /* ============================================================================= TODO: @@ -44,6 +61,8 @@ * * Allows: * changing the loaded history + * searching hdas + * displaying data, info, and download * Does not allow: * changing the name * @@ -55,8 +74,8 @@ var ReadOnlyHistoryPanel = Backbone.View.extend( LoggableMixin ).extend( /** @lends ReadOnlyHistoryPanel.prototype */{ - ///** logger used to record this.log messages, commonly set to console */ - //// comment this out to suppress log output + /** logger used to record this.log messages, commonly set to console */ + // comment this out to suppress log output //logger : console, /** class to use for constructing the HDA views */ @@ -68,15 +87,14 @@ /** (in ms) that jquery effects will use */ fxSpeed : 'fast', - datasetsSelector : '.datasets-list', - msgsSelector : '.message-container', - emptyMsgSelector : '.empty-history-message', + /** string to display when the model has no hdas */ emptyMsg : _l( 'This history is empty' ), + /** string to no hdas match the search terms */ noneFoundMsg : _l( 'No matching datasets found' ), // ......................................................................... SET UP /** Set up the view, set up storage, bind listeners to HDACollection events - * @param {Object} attributes + * @param {Object} attributes optional settings for the panel */ initialize : function( attributes ){ attributes = attributes || {}; @@ -86,35 +104,34 @@ } this.log( this + '.initialize:', attributes ); - // ---- set up instance vars + // ---- instance vars // control contents/behavior based on where (and in what context) the panel is being used /** where should pages from links be displayed? (default to new tab/window) */ this.linkTarget = attributes.linkTarget || '_blank'; /** how quickly should jquery fx run? */ this.fxSpeed = _.has( attributes, 'fxSpeed' )?( attributes.fxSpeed ):( this.fxSpeed ); + /** filters for displaying hdas */ + this.filters = []; + this.searchFor = ''; + + /** a function to locate the container to scroll to effectively scroll the panel */ +//TODO: rename + this.findContainerFn = attributes.findContainerFn; + // generally this is $el.parent() - but may be $el or $el.parent().parent() depending on the context + // ---- sub views and saved elements /** map of hda model ids to hda views */ this.hdaViews = {}; /** loading indicator */ this.indicator = new LoadingIndicator( this.$el ); - /** filters for displaying hdas */ - this.filters = []; - this.searchFor = ''; - + // ----- set up panel listeners, handle models passed on init, and call any ready functions this._setUpListeners(); - - // ---- handle models passed on init - if( this.model ){ - if( this.logger ){ - this.model.logger = this.logger; - } - this._setUpWebStorage( attributes.initiallyExpanded, attributes.show_deleted, attributes.show_hidden ); - this._setUpModelEventHandlers(); - } + // don't render when setting the first time + var modelOptions = _.pick( attributes, 'initiallyExpanded', 'show_deleted', 'show_hidden' ); + this.setModel( this.model, modelOptions, false ); //TODO: remove? - // ---- and any run functions if( attributes.onready ){ attributes.onready.call( this ); } @@ -125,25 +142,23 @@ * @fires: empty-history when switching to a history with no HDAs or creating a new history */ _setUpListeners : function(){ - // ---- event handlers for self this.on( 'error', function( model, xhr, options, msg, details ){ this.errorHandler( model, xhr, options, msg, details ); }); this.on( 'loading-history', function(){ // show the loading indicator when loading a new history starts... - this.showLoadingIndicator( 'loading history...', 40 ); + this._showLoadingIndicator( 'loading history...', 40 ); }); this.on( 'loading-done', function(){ // ...hiding it again when loading is done (or there's been an error) - this.hideLoadingIndicator( 40 ); + this._hideLoadingIndicator( 40 ); if( _.isEmpty( this.hdaViews ) ){ this.trigger( 'empty-history', this ); } }); - // throw the first render up as a diff namespace using once - // (for outside consumption) + // throw the first render up as a diff namespace using once (for outside consumption) this.once( 'rendered', function(){ this.trigger( 'rendered:initial', this ); return false; @@ -155,6 +170,7 @@ this.log( this + '', arguments ); }, this ); } + return this; }, //TODO: see base-mvc @@ -166,7 +182,7 @@ //}, // ........................................................................ error handling - /** Event listener to handle errors (from the panel, the history, or the history's HDAs) + /** Event handler for errors (from the panel, the history, or the history's HDAs) * @param {Model or View} model the (Backbone) source of the error * @param {XMLHTTPRequest} xhr any ajax obj. assoc. with the error * @param {Object} options the options map commonly used with bbone ajax @@ -174,7 +190,8 @@ * @param {Object} msg optional object containing error details */ errorHandler : function( model, xhr, options, msg, details ){ - var parsed = this._parseErrorMessage( model, xhr, options, msg, details ); + console.error( model, xhr, options, msg, details ); +//TODO: getting JSON parse errors from jq migrate // interrupted ajax if( xhr && xhr.status === 0 && xhr.readyState === 0 ){ @@ -185,8 +202,9 @@ // otherwise, show an error message inside the panel } else { + var parsed = this._parseErrorMessage( model, xhr, options, msg, details ); // it's possible to have a triggered error before the message container is rendered - wait for it to show - if( !this.$el.find( this.msgsSelector ).is( ':visible' ) ){ + if( !this.$messages().is( ':visible' ) ){ this.once( 'rendered', function(){ this.displayMessage( 'error', parsed.message, parsed.details ); }); @@ -239,34 +257,35 @@ /** loads a history & hdas w/ details (but does not make them the current history) */ loadHistoryWithHDADetails : function( historyId, attributes, historyFn, hdaFn ){ //console.info( 'loadHistoryWithHDADetails:', historyId, attributes, historyFn, hdaFn ); - var panel = this, - // will be called to get hda ids that need details from the api - hdaDetailIds = function( historyData ){ + var hdaDetailIds = function( historyData ){ + // will be called to get hda ids that need details from the api //TODO: non-visible HDAs are getting details loaded... either stop loading them at all or filter ids thru isVisible - return panel.getExpandedHdaIds( historyData.id ); + return _.keys( HistoryPrefs.get( historyData.id ).get( 'expandedHdas' ) ); }; return this.loadHistory( historyId, attributes, historyFn, hdaFn, hdaDetailIds ); }, /** loads a history & hdas w/ NO details (but does not make them the current history) */ loadHistory : function( historyId, attributes, historyFn, hdaFn, hdaDetailIds ){ - this.trigger( 'loading-history', this ); + var panel = this; attributes = attributes || {}; - var panel = this; + + panel.trigger( 'loading-history', panel ); //console.info( 'loadHistory:', historyId, attributes, historyFn, hdaFn, hdaDetailIds ); var xhr = historyModel.History.getHistoryData( historyId, { historyFn : historyFn, hdaFn : hdaFn, hdaDetailIds : attributes.initiallyExpanded || hdaDetailIds }); - return this._loadHistoryFromXHR( xhr, attributes ) + + return panel._loadHistoryFromXHR( xhr, attributes ) .fail( function( xhr, where, history ){ // throw an error up for the error handler panel.trigger( 'error', panel, xhr, attributes, _l( 'An error was encountered while ' + where ), { historyId: historyId, history: history || {} }); }) .always( function(){ - // bc hideLoadingIndicator relies on this firing + // bc _hideLoadingIndicator relies on this firing panel.trigger( 'loading-done', panel ); }); }, @@ -275,7 +294,7 @@ _loadHistoryFromXHR : function( xhr, attributes ){ var panel = this; xhr.then( function( historyJSON, hdaJSON ){ - panel.setModel( historyJSON, hdaJSON, attributes ); + panel.JSONToModel( historyJSON, hdaJSON, attributes ); }); xhr.fail( function( xhr, where ){ // always render - whether we get a model or not @@ -284,11 +303,59 @@ return xhr; }, - /** release/free/shutdown old models and set up panel for new models */ - setModel : function( newHistoryJSON, newHdaJSON, attributes ){ + + /** create a new history model from JSON and call setModel on it */ + JSONToModel : function( newHistoryJSON, newHdaJSON, attributes ){ + this.log( 'JSONToModel:', newHistoryJSON, newHdaJSON, attributes ); +//TODO: Maybe better in History? attributes = attributes || {}; - //console.info( 'setModel:', newHistoryJSON, newHdaJSON.length, attributes ); + //this.log( 'JSONToModel:', newHistoryJSON, newHdaJSON.length, attributes ); + // set up the new model and render + if( Galaxy && Galaxy.currUser ){ +//TODO: global + newHistoryJSON.user = Galaxy.currUser.toJSON(); + } + var model = new historyModel.History( newHistoryJSON, newHdaJSON, attributes ); + this.setModel( model ); + return this; + }, + + /** release/free/shutdown old models and set up panel for new models + * @fires new-model with the panel as parameter + */ + setModel : function( model, attributes, render ){ + attributes = attributes || {}; + render = ( render !== undefined )?( render ):( true ); + this.log( 'setModel:', model, attributes, render ); + + this.freeModel(); + this.selectedHdaIds = []; + + if( model ){ + // set up the new model with user, logger, storage, events + if( Galaxy && Galaxy.currUser ){ +//TODO: global + model.user = Galaxy.currUser.toJSON(); + } + this.model = model; + if( this.logger ){ + this.model.logger = this.logger; + } + this._setUpWebStorage( attributes.initiallyExpanded, attributes.show_deleted, attributes.show_hidden ); + this._setUpModelEventHandlers(); + this.trigger( 'new-model', this ); + } + + if( render ){ +//TODO: remove? + this.render(); + } + return this; + }, + + /** free the current model and all listeners for it, free any hdaViews for the model */ + freeModel : function(){ // stop/release the previous model, and clear cache to hda sub-views if( this.model ){ this.model.clearUpdateTimeout(); @@ -297,22 +364,13 @@ //TODO: see base-mvc //this.model.free(); } + this.freeHdaViews(); + return this; + }, + + /** free any hdaViews the panel has */ + freeHdaViews : function(){ this.hdaViews = {}; - - // set up the new model and render - if( Galaxy && Galaxy.currUser ){ -//TODO: global - newHistoryJSON.user = Galaxy.currUser.toJSON(); - } - this.model = new historyModel.History( newHistoryJSON, newHdaJSON, attributes ); - if( this.logger ){ - this.model.logger = this.logger; - } - this._setUpWebStorage( attributes.initiallyExpanded, attributes.show_deleted, attributes.show_hidden ); - this._setUpModelEventHandlers(); - this.selectedHdaIds = []; - this.trigger( 'new-model', this ); - this.render(); return this; }, @@ -324,12 +382,11 @@ * @see PersistentStorage */ _setUpWebStorage : function( initiallyExpanded, show_deleted, show_hidden ){ - //console.debug( '_setUpWebStorage', initiallyExpanded, show_deleted, show_hidden ); + //this.log( '_setUpWebStorage', initiallyExpanded, show_deleted, show_hidden ); this.storage = new HistoryPrefs({ id: HistoryPrefs.historyStorageKey( this.model.get( 'id' ) ) }); -//TODO: move into HistoryPrefs // expanded Hdas is a map of hda.ids -> a boolean repr'ing whether this hda's body is already expanded // store any pre-expanded ids passed in if( _.isObject( initiallyExpanded ) ){ @@ -348,34 +405,7 @@ this.trigger( 'new-storage', this.storage, this ); this.log( this + ' (init\'d) storage:', this.storage.get() ); - }, - -//TODO: move into HistoryPrefs - /** clear all stored history panel data */ - clearWebStorage : function(){ - for( var key in sessionStorage ){ - if( key.indexOf( 'history:' ) === 0 ){ - sessionStorage.removeItem( key ); - } - } - }, - -//TODO: move into HistoryPrefs - /** get all stored data as an Object for a history with the given id */ - getStoredOptions : function( historyId ){ - if( !historyId || historyId === 'current' ){ - return ( this.storage )?( this.storage.get() ):( {} ); - } - //TODO: make storage engine generic - var item = sessionStorage.getItem( HistoryPrefs.historyStorageKey( historyId ) ); - return ( item === null )?( {} ):( JSON.parse( item ) ); - }, - -//TODO: move into HistoryPrefs - /** get an array of expanded hda ids for the given history id */ - getExpandedHdaIds : function( historyId ){ - var expandedHdas = this.getStoredOptions( historyId ).expandedHdas; - return (( _.isEmpty( expandedHdas ) )?( [] ):( _.keys( expandedHdas ) )); + return this; }, // ------------------------------------------------------------------------ history/hda event listening @@ -391,6 +421,7 @@ this.model.on( 'error error:hdas', function( model, xhr, options, msg ){ this.errorHandler( model, xhr, options, msg ); }, this ); + return this; }, // ------------------------------------------------------------------------ panel rendering @@ -399,6 +430,7 @@ * @see Backbone.View#render */ render : function( speed, callback ){ + this.log( 'render:', speed, callback ); // send a speed of 0 to have no fade in/out performed speed = ( speed === undefined )?( this.fxSpeed ):( speed ); var panel = this, @@ -414,7 +446,6 @@ // fade out existing, swap with the new, fade in, set up behaviours $( panel ).queue( 'fx', [ function( next ){ - //panel.$el.fadeTo( panel.fxSpeed, 0.0001, next ); if( speed && panel.$el.is( ':visible' ) ){ panel.$el.fadeOut( speed, next ); } else { @@ -446,7 +477,9 @@ return this; }, - /** render with no history data */ + /** render without history data + * @returns {jQuery} dom fragment with message container only + */ renderWithoutModel : function( ){ // we'll always need the message container var $newRender = $( '<div/>' ), @@ -455,8 +488,11 @@ return $newRender.append( $msgContainer ); }, - /** render with history data */ + /** render with history data + * @returns {jQuery} dom fragment as temporary container to be swapped out later + */ renderModel : function( ){ + // tmp div for final swap in render var $newRender = $( '<div/>' ); // render based on anonymity, set up behaviors @@ -485,13 +521,77 @@ //TODO: these should be either sub-MVs, or handled by events $where = $where || this.$el; $where.find( '[title]' ).tooltip({ placement: 'bottom' }); + this._setUpSearchInput( $where.find( '.history-search-controls .history-search-input' ) ); + return this; + }, + + // ------------------------------------------------------------------------ sub-$element shortcuts + /** the scroll container for this panel - can be $el, $el.parent(), or grandparent depending on context */ + $container : function(){ + return ( this.findContainerFn )?( this.findContainerFn.call( this ) ):( this.$el.parent() ); + }, + /** where hdaViews are attached */ + $datasetsList : function( $where ){ + return ( $where || this.$el ).find( '.datasets-list' ); + }, + /** container where panel messages are attached */ + $messages : function( $where ){ + return ( $where || this.$el ).find( '.message-container' ); + }, + /** the message displayed when no hdaViews can be shown (no hdas, none matching search) */ + $emptyMessage : function( $where ){ + return ( $where || this.$el ).find( '.empty-history-message' ); }, // ------------------------------------------------------------------------ hda sub-views - /** Create an HDA view for the given HDA (but leave attachment for addHdaView above) + /** Set up/render a view for each HDA to be shown, init with model and listeners. + * HDA views are cached to the map this.hdaViews (using the model.id as key). + * @param {jQuery} $whereTo what dom element to prepend the HDA views to + * @returns the number of visible hda views + */ + renderHdas : function( $whereTo ){ + $whereTo = $whereTo || this.$el; + var panel = this, + newHdaViews = {}, + // only render the shown hdas + //TODO: switch to more general filtered pattern + visibleHdas = this.model.hdas.getVisible( + this.storage.get( 'show_deleted' ), + this.storage.get( 'show_hidden' ), + this.filters + ); + //this.log( 'renderHdas, visibleHdas:', visibleHdas, $whereTo ); +//TODO: prepend to sep div, add as one + + this.$datasetsList( $whereTo ).empty(); + + if( visibleHdas.length ){ + visibleHdas.each( function( hda ){ + // render it (NOTE: reverse order, newest on top (prepend)) + var hdaId = hda.get( 'id' ), + hdaView = panel._createHdaView( hda ); + newHdaViews[ hdaId ] = hdaView; + if( _.contains( panel.selectedHdaIds, hdaId ) ){ + hdaView.selected = true; + } + panel.attachHdaView( hdaView.render(), $whereTo ); + }); + panel.$emptyMessage( $whereTo ).hide(); + + } else { + //this.log( 'emptyMsg:', panel.$emptyMessage( $whereTo ) ) + panel.$emptyMessage( $whereTo ) + .text( ( this.model.hdas.length && this.searchFor )?( this.noneFoundMsg ):( this.emptyMsg ) ) + .show(); + } + this.hdaViews = newHdaViews; + return this.hdaViews; + }, + + /** Create an HDA view for the given HDA and set up listeners (but leave attachment for addHdaView) * @param {HistoryDatasetAssociation} hda */ - createHdaView : function( hda ){ + _createHdaView : function( hda ){ var hdaId = hda.get( 'id' ), hdaView = new this.HDAViewClass({ model : hda, @@ -510,66 +610,26 @@ * @param {HDAView} hdaView HDAView (base or edit) to listen to */ _setUpHdaListeners : function( hdaView ){ - var historyView = this; + var panel = this; + hdaView.on( 'error', function( model, xhr, options, msg ){ + panel.errorHandler( model, xhr, options, msg ); + }); // maintain a list of hdas whose bodies are expanded hdaView.on( 'body-expanded', function( id ){ - historyView.storage.addExpandedHda( id ); + panel.storage.addExpandedHda( id ); }); hdaView.on( 'body-collapsed', function( id ){ - historyView.storage.removeExpandedHda( id ); + panel.storage.removeExpandedHda( id ); }); - hdaView.on( 'error', function( model, xhr, options, msg ){ - historyView.errorHandler( model, xhr, options, msg ); - }); + return this; }, - /** Set up/render a view for each HDA to be shown, init with model and listeners. - * HDA views are cached to the map this.hdaViews (using the model.id as key). - * @param {jQuery} $whereTo what dom element to prepend the HDA views to - * @returns the number of visible hda views - */ - renderHdas : function( $whereTo ){ - $whereTo = $whereTo || this.$el; - var historyView = this, - newHdaViews = {}, - // only render the shown hdas - //TODO: switch to more general filtered pattern - visibleHdas = this.model.hdas.getVisible( - this.storage.get( 'show_deleted' ), - this.storage.get( 'show_hidden' ), - this.filters - ); - //console.debug( 'renderHdas, visibleHdas:', visibleHdas, $whereTo ); -//TODO: prepend to sep div, add as one - $whereTo.find( this.datasetsSelector ).empty(); - - if( visibleHdas.length ){ - visibleHdas.each( function( hda ){ - // render it (NOTE: reverse order, newest on top (prepend)) - var hdaId = hda.get( 'id' ), - hdaView = historyView.createHdaView( hda ); - newHdaViews[ hdaId ] = hdaView; - if( _.contains( historyView.selectedHdaIds, hdaId ) ){ - hdaView.selected = true; - } - historyView.attachHdaView( hdaView.render(), $whereTo ); - }); - $whereTo.find( this.emptyMsgSelector ).hide(); - - } else { - //console.debug( 'emptyMsg:', $whereTo.find( this.emptyMsgSelector ) ) - $whereTo.find( this.emptyMsgSelector ) - .text( ( this.model.hdas.length && this.searchFor )?( this.noneFoundMsg ):( this.emptyMsg ) ) - .show(); - } - this.hdaViews = newHdaViews; - return this.hdaViews; - }, - + /** attach an hdaView to the panel */ attachHdaView : function( hdaView, $whereTo ){ $whereTo = $whereTo || this.$el; - var $datasetsList = $whereTo.find( this.datasetsSelector ); + var $datasetsList = this.$datasetsList( $whereTo ); $datasetsList.prepend( hdaView.$el ); + return this; }, /** Add an hda view to the panel for the given hda @@ -581,13 +641,13 @@ // don't add the view if it wouldn't be visible accrd. to current settings if( !hda.isVisible( this.storage.get( 'show_deleted' ), this.storage.get( 'show_hidden' ) ) ){ - return; + return panel; } // create and prepend to current el, if it was empty fadeout the emptyMsg first $({}).queue([ function fadeOutEmptyMsg( next ){ - var $emptyMsg = panel.$el.find( panel.emptyMsgSelector ); + var $emptyMsg = panel.$emptyMessage(); if( $emptyMsg.is( ':visible' ) ){ $emptyMsg.fadeOut( panel.fxSpeed, next ); } else { @@ -595,16 +655,20 @@ } }, function createAndPrepend( next ){ + var hdaView = panel._createHdaView( hda ); + panel.hdaViews[ hda.id ] = hdaView; + hdaView.render().$el.hide(); panel.scrollToTop(); - var $whereTo = panel.$el.find( panel.datasetsSelector ), - hdaView = panel.createHdaView( hda ); - panel.hdaViews[ hda.id ] = hdaView; - hdaView.render().$el.hide().prependTo( $whereTo ).slideDown( panel.fxSpeed ); + panel.attachHdaView( hdaView ); + hdaView.$el.slideDown( panel.fxSpeed ); } ]); + return panel; }, - /** alias to the model. Updates the hda list only (not the history) */ + //TODO: removeHdaView? + + /** convenience alias to the model. Updates the hda list only (not the history) */ refreshHdas : function( detailIds, options ){ if( this.model ){ return this.model.refresh( detailIds, options ); @@ -613,11 +677,20 @@ return $.when(); }, + ///** use underscore's findWhere to find a view where the model matches the terms + // * note: finds and returns the _first_ matching + // */ + //findHdaView : function( terms ){ + // if( !this.model || !this.model.hdas.length ){ return undefined; } + // var model = this.model.hdas.findWhere( terms ); + // return ( model )?( this.hdaViews[ model.id ] ):( undefined ); + //}, + // ------------------------------------------------------------------------ panel events /** event map */ events : { // allow (error) messages to be clicked away - //TODO: switch to common close (X) idiom +//TODO: switch to common close (X) idiom 'click .message-container' : 'clearMessages', 'click .history-search-btn' : 'toggleSearchControls' }, @@ -628,6 +701,7 @@ item.toggleBodyVisibility( null, false ); }); this.storage.set( 'expandedHdas', {} ); + return this; }, /** Handle the user toggling the deleted visibility by: @@ -654,7 +728,7 @@ return this.storage.get( 'show_hidden' ); }, - // ........................................................................ filters + // ........................................................................ hda search & filters /** render a search input for filtering datasets shown * (see the search section in the HDA model for implementation of the actual searching) * return will start the search @@ -662,12 +736,12 @@ * clicking the clear button will clear the search * uses searchInput in ui.js */ - setUpSearchInput : function( $where ){ + _setUpSearchInput : function( $where ){ var panel = this, inputSelector = '.history-search-input'; function onFirstSearch( searchFor ){ - //console.debug( 'onFirstSearch', searchFor, panel ); + //this.log( 'onFirstSearch', searchFor, panel ); if( panel.model.hdas.haveDetails() ){ panel.searchHdas( searchFor ); return; @@ -692,33 +766,22 @@ }); return $where; }, -//TODO: to hidden/shown plugin - showSearchControls : function( speed ){ - speed = ( speed === undefined )?( this.fxSpeed ):( speed ); + /** toggle showing/hiding the search controls (rendering first on the initial show) + * @param {Event or Number} eventOrSpeed variadic - if number the speed of the show/hide effect + * @param {boolean} show force show/hide with T/F + */ + toggleSearchControls : function( eventOrSpeed, show ){ var $searchControls = this.$el.find( '.history-search-controls' ), - $input = $searchControls.find( '.history-search-input' ); - // if it hasn't been rendered - do it now - if( !$input.children().size() ){ - this.setUpSearchInput( $input ); + speed = ( jQuery.type( eventOrSpeed ) === 'number' )?( eventOrSpeed ):( this.fxSpeed ); + show = ( show !== undefined )?( show ):( !$searchControls.is( ':visible' ) ); + if( show ){ + $searchControls.slideDown( speed, function(){ + $( this ).find( 'input' ).focus(); + }); + } else { + $searchControls.slideUp( speed ); } - // then slide open, focusing on the input, and persisting the setting when it's done - $searchControls.slideDown( speed, function(){ - $( this ).find( 'input' ).focus(); - }); - }, - hideSearchControls : function( speed ){ - speed = ( speed === undefined )?( this.fxSpeed ):( speed ); - this.$el.find( '.history-search-controls' ).slideUp( speed ); - }, - - /** toggle showing/hiding the search controls (rendering first on the initial show) */ - toggleSearchControls : function( eventOrSpeed ){ - speed = ( jQuery.type( eventOrSpeed ) === 'number' )?( eventOrSpeed ):( this.fxSpeed ); - if( this.$el.find( '.history-search-controls' ).is( ':visible' ) ){ - this.hideSearchControls( speed ); - } else { - this.showSearchControls( speed ); - } + return show; }, /** filter hda view list to those that contain the searchFor terms @@ -726,26 +789,28 @@ */ searchHdas : function( searchFor ){ //note: assumes hda details are loaded - //console.debug( 'onSearch', searchFor, this ); + //this.log( 'onSearch', searchFor, this ); var panel = this; this.searchFor = searchFor; this.filters = [ function( hda ){ return hda.matchesAll( panel.searchFor ); } ]; this.trigger( 'search:searching', searchFor, this ); this.renderHdas(); + return this; }, /** clear the search filters and show all views that are normally shown */ clearHdaSearch : function( searchFor ){ - //console.debug( 'onSearchClear', this ); + //this.log( 'onSearchClear', this ); this.searchFor = ''; this.filters = []; this.trigger( 'search:clear', this ); this.renderHdas(); + return this; }, // ........................................................................ loading indicator /** hide the panel and display a loading indicator (in the panel's parent) when history model's are switched */ - showLoadingIndicator : function( msg, speed, callback ){ + _showLoadingIndicator : function( msg, speed, callback ){ speed = ( speed !== undefined )?( speed ):( this.fxSpeed ); if( !this.indicator ){ this.indicator = new LoadingIndicator( this.$el, this.$el.parent() ); @@ -759,7 +824,7 @@ }, /** hide the loading indicator */ - hideLoadingIndicator : function( speed, callback ){ + _hideLoadingIndicator : function( speed, callback ){ speed = ( speed !== undefined )?( speed ):( this.fxSpeed ); if( this.indicator ){ this.indicator.hide( speed, callback ); @@ -770,16 +835,17 @@ /** Display a message in the top of the panel. * @param {String} type type of message ('done', 'error', 'warning') * @param {String} msg the message to display + * @param {Object or HTML} modal contents displayed when the user clicks 'details' in the message */ displayMessage : function( type, msg, details ){ //precondition: msgContainer must have been rendered even if there's no model var panel = this; - //console.debug( 'displayMessage', type, msg, details ); + //this.log( 'displayMessage', type, msg, details ); this.scrollToTop(); - var $msgContainer = this.$el.find( this.msgsSelector ), + var $msgContainer = this.$messages(), $msg = $( '<div/>' ).addClass( type + 'message' ).html( msg ); - //console.debug( ' ', $msgContainer ); + //this.log( ' ', $msgContainer ); if( !_.isEmpty( details ) ){ var $detailsLink = $( '<a href="javascript:void(0)">Details</a>' ) @@ -832,47 +898,25 @@ /** Remove all messages from the panel. */ clearMessages : function(){ - var $msgContainer = this.$el.find( this.msgsSelector ); - $msgContainer.empty(); + this.$messages().empty(); + return this; }, // ........................................................................ scrolling /** get the current scroll position of the panel in its parent */ scrollPosition : function(){ - return this.$el.scrollTop(); + return this.$container().scrollTop(); }, /** set the current scroll position of the panel in its parent */ scrollTo : function( pos ){ - this.$el.scrollTop( pos ); + this.$container().scrollTop( pos ); return this; }, /** Scrolls the panel to the top. */ scrollToTop : function(){ - this.$el.scrollTop( 0 ); - return this; - }, - - /** Scrolls the panel (the enclosing container - in gen., the page) so that some object - * is displayed in the vertical middle. - * NOTE: if no height is given the panel will scroll to objTop (putting it at the top). - * @param {Number} top top offset of the object to view - * @param {Number} height height of the object to view - * @returns {HistoryPanel} the panel - */ - scrollIntoView : function( top, height ){ - if( height === undefined ){ - this.scrollTo( top ); - return this; - } - // otherwise, place the object in the vertical middle - var viewport = window, - $panelContainer = this.$el.parent(), - containerHeight = $( viewport ).innerHeight(), - middleOffset = ( containerHeight / 2 ) - ( height / 2 ); - - this.scrollTo( top - middleOffset ); + this.$container().scrollTop( 0 ); return this; }, @@ -885,8 +929,9 @@ if( ( !id ) || ( !this.hdaViews[ id ] ) ){ return this; } - var $viewEl = this.hdaViews[ id ].$el; - this.scrollIntoView( $viewEl.offset().top, $viewEl.outerHeight() ); + var view = this.hdaViews[ id ]; + //this.scrollIntoView( $viewEl.offset().top ); + this.scrollTo( view.el.offsetTop ); return this; }, @@ -901,18 +946,18 @@ return this.scrollToId( hda.id ); }, + // ........................................................................ misc /** Return a string rep of the history */ toString : function(){ return 'ReadOnlyHistoryPanel(' + (( this.model )?( this.model.get( 'name' )):( '' )) + ')'; } }); - - //------------------------------------------------------------------------------ TEMPLATES ReadOnlyHistoryPanel.templates = { historyPanel : Handlebars.templates[ 'template-history-historyPanel' ] }; + //============================================================================== return { ReadOnlyHistoryPanel: ReadOnlyHistoryPanel diff -r 1ed94d89769ed2b51232c6c714c3bb21d93cc3ce -r 707ba3eee2abb828ac262e76c5b9fbbc6c85cd03 static/scripts/mvc/visualization/scatterplotControlForm.js --- a/static/scripts/mvc/visualization/scatterplotControlForm.js +++ /dev/null @@ -1,631 +0,0 @@ -/* ============================================================================= -todo: - I'd like to move the svg creation out of the splot constr. to: - allow adding splots to an existing canvas - allow mult. splots sharing a canvas - - - outside this: - BUG: setting width, height in plot controls doesn't re-interpolate data locations!! - BUG?: get metadata_column_names (from datatype if necessary) - BUG: single vis in popupmenu should have tooltip with that name NOT 'Visualizations' - - wire label setters, anim setter - - TwoVarScatterplot: - ??: maybe better to do this with a canvas... - save as visualization - to seperate file? - remove underscore dependencies - add interface to change values (seperate)? - download svg -> base64 encode - incorporate glyphs, glyph state renderers - - ScatterplotSettingsForm: - some css bug that lowers the width of settings form when plot-controls tab is open - causes chart to shift - what can be abstracted/reused for other graphs? - avoid direct manipulation of this.plot - allow option to put plot into seperate tab of interface (for small multiples) - - provide callback in view to load data incrementally - for large sets - paginate - handle rerender - use endpoint (here and on the server (fileptr)) - fetch (new?) data - handle rerender - use d3.TSV? - render warning on long data (> maxDataPoints) - adjust endpoint - - selectable list of preset column comparisons (rnaseq etc.) - how to know what sort of Tabular the data is? - smarter about headers - validate columns selection (here or server) - - set stats column names by selected columns - move chart into tabbed area... - - Scatterplot.mako: - multiple plots on one page (small multiples) - ?? ensure svg styles thru d3 or css? - d3: configable (easily) - css: standard - better maintenance - ? override at config - -============================================================================= */ -/** - * Scatterplot control UI as a backbone view - * handles: - * getting the desired data - * configuring the plot display - * showing (general) statistics - * - * initialize attributes REQUIRES a dataset and an apiDatasetsURL - */ -var ScatterplotControlForm = Backbone.View.extend( LoggableMixin ).extend({ - //logger : console, - className : 'scatterplot-control-form', - - //NOTE: should include time needed to render - dataLoadDelay : 4000, - dataLoadSize : 5000, - - loadingIndicatorImage : 'loading_small_white_bg.gif', - fetchMsg : 'Fetching data...', - renderMsg : 'Rendering...', - - initialize : function( attributes ){ - this.log( this + '.initialize, attributes:', attributes ); - - this.dataset = null; - this.chartConfig = null; - this.chart = null; - this.loader = null; - - // set up refs to the four tab areas - this.$dataControl = null; - this.$chartControl = null; - this.$statsDisplay = null; - this.$chartDisplay = null; - - this.dataFetch = null; - - this.initializeFromAttributes( attributes ); - this.initializeChart( attributes ); - this.initializeDataLoader( attributes ); - }, - - initializeFromAttributes : function( attributes ){ - // required settings: ensure certain vars we need are passed in attributes - if( !attributes || !attributes.dataset ){ - throw( "ScatterplotView requires a dataset" ); - } else { - this.dataset = attributes.dataset; - } - if( jQuery.type( this.dataset.metadata_column_types ) === 'string' ){ - this.dataset.metadata_column_types = this.dataset.metadata_column_types.split( ', ' ); - } - this.log( '\t dataset:', this.dataset ); - - // attempt to get possible headers from the data's first line - if( this.dataset.comment_lines && this.dataset.comment_lines.length ){ - //TODO:?? - var firstLine = this.dataset.comment_lines[0], - possibleHeaders = firstLine.split( '\t' ); - if( possibleHeaders.length === this.dataset.metadata_column_types.length ){ - this.possibleHeaders = possibleHeaders; - } - } - - // passed from mako helper - //TODO: integrate to galaxyPaths - //TODO: ?? seems like data loader section would be better - if( !attributes.apiDatasetsURL ){ - throw( "ScatterplotView requires a apiDatasetsURL" ); - } else { - this.dataURL = attributes.apiDatasetsURL + '/' + this.dataset.id + '?'; - } - this.log( '\t dataURL:', this.dataURL ); - }, - - initializeChart : function( attributes ){ - // set up the basic chart infrastructure and config (if any) - this.chartConfig = attributes.chartConfig || {}; - //if( this.logger ){ this.chartConfig.debugging = true; } - this.log( '\t initial chartConfig:', this.chartConfig ); - - this.chart = new TwoVarScatterplot( this.chartConfig ); - //TODO: remove 2nd ref, use this.chart.config - this.chartConfig = this.chart.config; - }, - - initializeDataLoader : function( attributes ){ - // set up data loader - var view = this; - this.loader = new LazyDataLoader({ - //logger : ( this.logger )?( this.logger ):( null ), - // we'll generate this when columns are chosen - url : null, - start : attributes.start || 0, - //NOTE: metadata_data_lines can be null (so we won't know the total) - total : attributes.total || this.dataset.metadata_data_lines, - delay : this.dataLoadDelay, - size : this.dataLoadSize, - - buildUrl : function( start, size ){ - // currently VERY SPECIFIC to using data_providers.py start_val, max_vals params - return this.url + '&' + jQuery.param({ - start_val: start, - max_vals: size - }); - } - }); - $( this.loader ).bind( 'error', function( event, status, error ){ - view.log( 'ERROR:', status, error ); - alert( 'ERROR fetching data:\n' + status + '\n' + error ); - view.hideLoadingIndicator(); - }); - }, - - // ------------------------------------------------------------------------- CONTROLS RENDERING - render : function(){ - this.log( this + '.render' ); - - // render the tab controls, areas and loading indicator - this.$el.append( ScatterplotControlForm.templates.mainLayout({ - loadingIndicatorImagePath : galaxy_config.root + 'static/images/' + this.loadingIndicatorImage, - message : '' - })); - - // render the tab content - this.$dataControl = this._render_dataControl(); - this.$chartControl = this._render_chartControl(); - this.$statsDisplay = this.$el.find( '.tab-pane#stats-display' ); - this.$chartDisplay = this._render_chartDisplay(); - - // auto render if given both x, y column choices in query for page - //TODO:?? add autoRender=1 to query maybe? - if( this.chartConfig.xColumn && this.chartConfig.yColumn ){ - this.renderChart(); - } - - // set up behaviours - this.$el.find( '[title]' ).tooltip(); - - // uncomment any of the following to have that tab show on initial load (for testing) - //this.$el.find( 'ul.nav' ).find( 'a[href="#data-control"]' ).tab( 'show' ); - //this.$el.find( 'ul.nav' ).find( 'a[href="#chart-control"]' ).tab( 'show' ); - //this.$el.find( 'ul.nav' ).find( 'a[href="#stats-display"]' ).tab( 'show' ); - //this.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' ); - return this; - }, - - _render_dataControl : function(){ - // controls for which columns are used to plot datapoints (and ids/additional info to attach if desired) - var view = this, - allColumns = [], - numericColumns = [], - usePossibleHeaders = ( this.possibleHeaders && this.$dataControl )? - ( this.$dataControl.find( '#first-line-header-checkbox' ).is( ':checked' ) ):( false ); - - // gather column indeces (from metadata_column_types) and names (from metadata_columnnames) - _.each( this.dataset.metadata_column_types, function( type, index ){ - // use a 1 based index in names/values within the form (will be dec. when parsed out) - var oneBasedIndex = index + 1, - // default name is 'column <index>'... - name = 'column ' + oneBasedIndex; - - // ...but label with the name if available... - if( view.dataset.metadata_column_names ){ - name = view.dataset.metadata_column_names[ index ]; - - // ...or, use the first line as headers if the user wants - } else if( usePossibleHeaders ){ - name = view.possibleHeaders[ index ]; - } - - // cache all columns here - allColumns.push({ index: oneBasedIndex, name: name }); - - // filter numeric columns to their own list - if( type === 'int' || type === 'float' ){ - numericColumns.push({ index: oneBasedIndex, name: name }); - } - }); - //TODO: other vals: max_vals, start_val, pagination (chart-settings) - - // render the html - var $dataControl = this.$el.find( '.tab-pane#data-control' ); - $dataControl.html( ScatterplotControlForm.templates.dataControl({ - allColumns : allColumns, - numericColumns : numericColumns, - possibleHeaders : ( this.possibleHeaders )?( this.possibleHeaders.join( ', ' ) ):( '' ), - usePossibleHeaders : usePossibleHeaders - })); - - if( !this.dataset.metadata_column_names && this.possibleHeaders ){ - $dataControl.find( '#first-line-header' ).show(); - } - - // preset to column selectors if they were passed in the config in the query string - $dataControl.find( '#X-select' ).val( this.chartConfig.xColumn ); - $dataControl.find( '#Y-select' ).val( this.chartConfig.yColumn ); - if( this.chartConfig.idColumn !== undefined ){ - $dataControl.find( '#include-id-checkbox' ) - .attr( 'checked', true ).trigger( 'change' ); - $dataControl.find( '#ID-select' ).val( this.chartConfig.idColumn ); - } - - return $dataControl; - }, - - _render_chartControl : function(){ - // tab content to control how the chart is rendered (data glyph size, chart size, etc.) - var view = this, - $chartControl = this.$el.find( '.tab-pane#chart-control' ), - // limits for controls (by control/chartConfig id) - //TODO: move into TwoVarScatterplot - controlRanges = { - 'datapointSize' : { min: 2, max: 10, step: 1 }, - 'width' : { min: 200, max: 800, step: 20 }, - 'height' : { min: 200, max: 800, step: 20 } - }; - - // render the html - $chartControl.append( ScatterplotControlForm.templates.chartControl( this.chartConfig ) ); - - // set up behaviours, js on sliders - $chartControl.find( '.numeric-slider-input' ).each( function(){ - var $this = $( this ), - $output = $this.find( '.slider-output' ), - $slider = $this.find( '.slider' ), - id = $this.attr( 'id' ); - //chartControl.log( 'slider set up', 'this:', $this, 'slider:', $slider, 'id', id ); - - // what to do when the slider changes: update display and update chartConfig - //TODO: move out of loop - function onSliderChange(){ - var $this = $( this ), - newValue = $this.slider( 'value' ); - //chartControl.log( 'slider change', 'this:', $this, 'output:', $output, 'value', newValue ); - $output.text( newValue ); - //chartControl.chartConfig[ id ] = newValue; - } - - $slider.slider( _.extend( controlRanges[ id ], { - value : view.chartConfig[ id ], - change : onSliderChange, - slide : onSliderChange - })); - }); - - return $chartControl; - }, - - _render_chartDisplay : function(){ - // render the tab content where the chart is displayed (but not the chart itself) - var $chartDisplay = this.$el.find( '.tab-pane#chart-display' ); - $chartDisplay.append( ScatterplotControlForm.templates.chartDisplay( this.chartConfig ) ); - return $chartDisplay; - }, - - // ------------------------------------------------------------------------- EVENTS - events : { - 'change #include-id-checkbox' : 'toggleThirdColumnSelector', - 'change #first-line-header-checkbox' : 'rerenderDataControl', - 'click #data-control #render-button' : 'renderChart', - 'click #chart-control #render-button' : 'changeChartSettings' - }, - - toggleThirdColumnSelector : function(){ - // show/hide the id selector on the data settings panel - this.$el.find( 'select[name="ID"]' ).parent().toggle(); - }, - - rerenderDataControl : function(){ - this.$dataControl = this._render_dataControl(); - }, - - showLoadingIndicator : function( message, callback ){ - // display the loading indicator over the tab panels if hidden, update message (if passed) - message = message || ''; - var indicator = this.$el.find( 'div#loading-indicator' ); - messageBox = indicator.find( '.loading-message' ); - - if( indicator.is( ':visible' ) ){ - if( message ){ - messageBox.fadeOut( 'fast', function(){ - messageBox.text( message ); - messageBox.fadeIn( 'fast', callback ); - }); - } else { - callback(); - } - - } else { - if( message ){ messageBox.text( message ); } - indicator.fadeIn( 'fast', callback ); - } - }, - - hideLoadingIndicator : function( callback ){ - this.$el.find( 'div#loading-indicator' ).fadeOut( 'fast', callback ); - }, - - // ------------------------------------------------------------------------- CHART/STATS RENDERING - renderChart : function(){ - // fetch the data, (re-)render the chart - this.log( this + '.renderChart' ); - - //TODO: separate data fetch - - // this is a complete re-render, so clear the prev. data - this.data = null; - this.meta = null; - - // update the chartConfig (here and chart) using chart settings - //TODO: separate and improve (used in changeChartSettings too) - _.extend( this.chartConfig, this.getChartSettings() ); - this.log( '\t chartConfig:', this.chartConfig ); - this.chart.updateConfig( this.chartConfig, false ); - - // build the url with the current data settings - this.loader.url = this.dataURL + '&' + jQuery.param( this.getDataSettings() ); - this.log( '\t loader: total lines:', this.loader.total, ' url:', this.loader.url ); - - // bind the new data event to: aggregate data, update the chart and stats with new data - var view = this; - $( this.loader ).bind( 'loaded.new', function( event, response ){ - view.log( view + ' loaded.new', response ); - - // aggregate data and meta - view.postProcessDataFetchResponse( response ); - view.log( '\t postprocessed data:', view.data ); - view.log( '\t postprocessed meta:', view.meta ); - - // update the chart and stats - view.showLoadingIndicator( view.renderMsg, function(){ - view.chart.render( view.data, view.meta ); - view.renderStats( view.data, view.meta ); - view.hideLoadingIndicator(); - }); - }); - // when all data loaded - unbind (or we'll start doubling event handlers) - $( this.loader ).bind( 'complete', function( event, data ){ - view.log( view + ' complete', data ); - $( view.loader ).unbind(); - }); - - // begin loading the data, switch to the chart display tab - view.showLoadingIndicator( view.fetchMsg, function(){ - view.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' ); - view.loader.load(); - }); - }, - - renderStats : function(){ - this.log( this + '.renderStats' ); - // render the stats table in the stats panel - //TODO: there's a better way - this.$statsDisplay.html( ScatterplotControlForm.templates.statsDisplay({ - stats: [ - { name: 'Count', xval: this.meta[0].count, yval: this.meta[1].count }, - { name: 'Min', xval: this.meta[0].min, yval: this.meta[1].min }, - { name: 'Max', xval: this.meta[0].max, yval: this.meta[1].max }, - { name: 'Sum', xval: this.meta[0].sum, yval: this.meta[1].sum }, - { name: 'Mean', xval: this.meta[0].mean, yval: this.meta[1].mean }, - { name: 'Median', xval: this.meta[0].median, yval: this.meta[1].median } - ] - })); - }, - - changeChartSettings : function(){ - // re-render the chart with new chart settings and OLD data - var view = this; - newChartSettings = this.getChartSettings(); - - // update the chart config from the chartSettings panel controls - _.extend( this.chartConfig, newChartSettings ); - this.log( 'this.chartConfig:', this.chartConfig ); - this.chart.updateConfig( this.chartConfig, false ); - - // if there's current data, call chart.render with it (no data fetch) - if( view.data && view.meta ){ - view.showLoadingIndicator( view.renderMsg, function(){ - view.$el.find( 'ul.nav' ).find( 'a[href="#chart-display"]' ).tab( 'show' ); - view.chart.render( view.data, view.meta ); - view.hideLoadingIndicator(); - }); - - // no current data, call renderChart instead (which will fetch data) - } else { - this.renderChart(); - } - }, - - // ------------------------------------------------------------------------- DATA AGGREGATION - postProcessDataFetchResponse : function( response ){ - // the loader only returns new data - it's up to this to munge the fetches together properly - //TODO: we're now storing data in two places: loader and here - // can't we reduce incoming data into loader.data[0]? are there concurrency problems? - this.postProcessData( response.data ); - this.postProcessMeta( response.meta ); - }, - - postProcessData : function( newData ){ - // stack the column data on top of each other into this.data - //this.log( this + '.postProcessData:', newData ); - var view = this; - - // if we already have data: aggregate - if( view.data ){ - _.each( newData, function( newColData, colIndex ){ - //view.log( colIndex + ' data:', newColData ); - //TODO??: time, space efficiency of this? - view.data[ colIndex ] = view.data[ colIndex ].concat( newColData ); - }); - - // otherwise: assign (first load) - } else { - view.data = newData; - } - }, - - postProcessMeta : function( newMeta ){ - // munge the meta data (stats) from the server fetches together - //pre: this.data must be preprocessed (needed for medians) - //this.log( this + '.postProcessMeta:', newMeta ); - var view = this, - colTypes = this.dataset.metadata_column_types; - - // if we already have meta: aggregate - if( view.meta ){ - _.each( newMeta, function( newColMeta, colIndex ){ - var colMeta = view.meta[ colIndex ], - colType = colTypes[ colIndex ]; - //view.log( '\t ' + colIndex + ' postprocessing meta:', newColMeta ); - //view.log( colIndex + ' old meta:', - // 'min:', colMeta.min, - // 'max:', colMeta.max, - // 'sum:', colMeta.sum, - // 'mean:', colMeta.mean, - // 'median:', colMeta.median - //); - - //!TODO: at what point are we getting int/float overflow on these?! - //??: need to be null safe? - colMeta.count += ( newColMeta.count )?( newColMeta.count ):( 0 ); - //view.log( colIndex, 'count:', colMeta.count ); - - if( ( colType === 'int' ) || ( colType === 'float' ) ){ - //view.log( colIndex + ' incoming meta:', - // 'min:', newColMeta.min, - // 'max:', newColMeta.max, - // 'sum:', newColMeta.sum, - // 'mean:', newColMeta.mean, - // 'median:', newColMeta.median - //); - - colMeta.min = Math.min( newColMeta.min, colMeta.min ); - colMeta.max = Math.max( newColMeta.max, colMeta.max ); - colMeta.sum = newColMeta.sum + colMeta.sum; - colMeta.mean = ( colMeta.count )?( colMeta.sum / colMeta.count ):( null ); - - // median's a pain bc of sorting (requires the data as well) - var sortedCol = view.data[ colIndex ].slice().sort(), - middleIndex = Math.floor( sortedCol.length / 2 ); - - if( sortedCol.length % 2 === 0 ){ - colMeta.median = ( ( sortedCol[ middleIndex ] + sortedCol[( middleIndex + 1 )] ) / 2 ); - - } else { - colMeta.median = sortedCol[ middleIndex ]; - } - - //view.log( colIndex + ' new meta:', - // 'min:', colMeta.min, - // 'max:', colMeta.max, - // 'sum:', colMeta.sum, - // 'mean:', colMeta.mean, - // 'median:', colMeta.median - //); - } - }); - - // otherwise: assign (first load) - } else { - view.meta = newMeta; - //view.log( '\t meta (first load):', view.meta ); - } - }, - - // ------------------------------------------------------------------------- GET DATA/CHART SETTINGS - getDataSettings : function(){ - // parse the column values for both indeces (for the data fetch) and names (for the chart) - var columnSelections = this.getColumnSelections(), - columns = []; - this.log( '\t columnSelections:', columnSelections ); - - //TODO: validate columns - minimally: we can assume either set by selectors or via a good query string - - // get column indices for params, include the desired ID column (if any) - //NOTE: these are presented in human-readable 1 base index (to match the data.peek) - adjust - columns = [ - columnSelections.X.colIndex - 1, - columnSelections.Y.colIndex - 1 - ]; - if( this.$dataControl.find( '#include-id-checkbox' ).attr( 'checked' ) ){ - columns.push( columnSelections.ID.colIndex - 1 ); - } - //TODO: other vals: max, start, page - - var params = { - data_type : 'raw_data', - provider : 'column_with_stats', - columns : '[' + columns + ']' - }; - this.log( '\t data settings (url params):', params ); - return params; - }, - - getColumnSelections : function(){ - // gets the current user-selected values for which columns to fetch from the data settings panel - // returns a map: { column-select name (eg. X) : { colIndex : column-selector val, - // colName : selected option text }, ... } - var selections = {}; - this.$dataControl.find( 'div.column-select select' ).each( function(){ - var $this = $( this ), - val = $this.val(); - selections[ $this.attr( 'name' ) ] = { - colIndex : val, - colName : $this.children( '[value="' + val + '"]' ).text() - }; - }); - return selections; - }, - - getChartSettings : function(){ - // gets the user-selected chartConfig from the chart settings panel - var settings = {}, - colSelections = this.getColumnSelections(); - //this.log( 'colSelections:', colSelections ); - - //TODO: simplify with keys and loop - settings.datapointSize = this.$chartControl.find( '#datapointSize.numeric-slider-input' ) - .find( '.slider' ).slider( 'value' ); - settings.width = this.$chartControl.find( '#width.numeric-slider-input' ) - .find( '.slider' ).slider( 'value' ); - settings.height = this.$chartControl.find( '#height.numeric-slider-input' ) - .find( '.slider' ).slider( 'value' ); - - // update axes labels using chartSettings inputs (if not at defaults), otherwise the selects' colName - //TODO: a little confusing - var chartSettingsXLabel = this.$chartControl.find( 'input#X-axis-label' ).val(), - chartSettingsYLabel = this.$chartControl.find( 'input#Y-axis-label' ).val(); - settings.xLabel = ( chartSettingsXLabel === 'X' )? - ( colSelections.X.colName ):( chartSettingsXLabel ); - settings.yLabel = ( chartSettingsYLabel === 'Y' )? - ( colSelections.Y.colName ):( chartSettingsYLabel ); - - settings.animDuration = ( this.$chartControl.find( '#animate-chart' ).is( ':checked' ) )? - ( this.chart.defaults.animDuration ):( 0 ); - - this.log( '\t chartSettings:', settings ); - return settings; - }, - - toString : function(){ - return 'ScatterplotControlForm(' + (( this.dataset )?( this.dataset.id ):( '' )) + ')'; - } -}); - -ScatterplotControlForm.templates = { - mainLayout : Handlebars.templates[ 'template-visualization-scatterplotControlForm' ], - dataControl : Handlebars.templates[ 'template-visualization-dataControl' ], - chartControl : Handlebars.templates[ 'template-visualization-chartControl' ], - statsDisplay : Handlebars.templates[ 'template-visualization-statsDisplay' ], - chartDisplay : Handlebars.templates[ 'template-visualization-chartDisplay' ] -}; - -//============================================================================== diff -r 1ed94d89769ed2b51232c6c714c3bb21d93cc3ce -r 707ba3eee2abb828ac262e76c5b9fbbc6c85cd03 static/scripts/packed/mvc/history/annotated-history-panel.js --- a/static/scripts/packed/mvc/history/annotated-history-panel.js +++ b/static/scripts/packed/mvc/history/annotated-history-panel.js @@ -1,1 +1,1 @@ -define(["mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/history/readonly-history-panel"],function(c,a,b){var d=b.ReadOnlyHistoryPanel.extend({className:"annotated-history-panel",HDAViewClass:a.HDABaseView,renderModel:function(){this.$el.addClass(this.className);var g=b.ReadOnlyHistoryPanel.prototype.renderModel.call(this),e=g.find(this.datasetsSelector),f=$("<table/>").addClass("datasets-list datasets-table");f.append(e.children());g.find(this.datasetsSelector).replaceWith(f);g.find(".history-subtitle").after(this.renderHistoryAnnotation());g.find(".history-search-btn").hide();g.find(".history-controls").after(g.find(".history-search-controls").show());this.setUpSearchInput(g.find(".history-search-input"));return g},renderHistoryAnnotation:function(){var e=this.model.get("annotation");if(!e){return null}return $(['<div class="history-annotation">',e,"</div>"].join(""))},renderHdas:function(f){f=f||this.$el;var e=b.ReadOnlyHistoryPanel.prototype.renderHdas.call(this,f);f.find(this.datasetsSelector).prepend($("<tr/>").addClass("headers").append([$("<th/>").text(_l("Dataset")),$("<th/>").text(_l("Annotation"))]));return e},attachHdaView:function(h,f){f=f||this.$el;var i=_.find(h.el.classList,function(j){return(/^state\-/).test(j)}),e=h.model.get("annotation")||"",g=$("<tr/>").addClass("dataset-row").append([$("<td/>").addClass("dataset-container").append(h.$el).addClass(i?i.replace("-","-color-"):""),$("<td/>").addClass("additional-info").text(e)]);f.find(this.datasetsSelector).append(g)},events:_.extend(_.clone(b.ReadOnlyHistoryPanel.prototype.events),{"click tr":function(e){$(e.currentTarget).find(".dataset-title-bar").click()},"click .icon-btn":function(e){e.stopPropagation()}}),toString:function(){return"AnnotatedHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{AnnotatedHistoryPanel:d}}); \ No newline at end of file +define(["mvc/dataset/hda-model","mvc/dataset/hda-base","mvc/history/readonly-history-panel"],function(c,a,b){var d=b.ReadOnlyHistoryPanel.extend({className:"annotated-history-panel",HDAViewClass:a.HDABaseView,renderModel:function(){this.$el.addClass(this.className);var g=b.ReadOnlyHistoryPanel.prototype.renderModel.call(this),e=this.$datasetsList(g),f=$("<table/>").addClass("datasets-list datasets-table");f.append(e.children());e.replaceWith(f);g.find(".history-subtitle").after(this.renderHistoryAnnotation());g.find(".history-search-btn").hide();g.find(".history-controls").after(g.find(".history-search-controls").show());return g},renderHistoryAnnotation:function(){var e=this.model.get("annotation");if(!e){return null}return $(['<div class="history-annotation">',e,"</div>"].join(""))},renderHdas:function(f){f=f||this.$el;var e=b.ReadOnlyHistoryPanel.prototype.renderHdas.call(this,f);this.$datasetsList(f).prepend($("<tr/>").addClass("headers").append([$("<th/>").text(_l("Dataset")),$("<th/>").text(_l("Annotation"))]));return e},attachHdaView:function(h,f){f=f||this.$el;var i=_.find(h.el.classList,function(j){return(/^state\-/).test(j)}),e=h.model.get("annotation")||"",g=$("<tr/>").addClass("dataset-row").append([$("<td/>").addClass("dataset-container").append(h.$el).addClass(i?i.replace("-","-color-"):""),$("<td/>").addClass("additional-info").text(e)]);this.$datasetsList(f).append(g)},events:_.extend(_.clone(b.ReadOnlyHistoryPanel.prototype.events),{"click tr":function(e){$(e.currentTarget).find(".dataset-title-bar").click()},"click .icon-btn":function(e){e.stopPropagation()}}),toString:function(){return"AnnotatedHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{AnnotatedHistoryPanel:d}}); \ No newline at end of file diff -r 1ed94d89769ed2b51232c6c714c3bb21d93cc3ce -r 707ba3eee2abb828ac262e76c5b9fbbc6c85cd03 static/scripts/packed/mvc/history/current-history-panel.js --- a/static/scripts/packed/mvc/history/current-history-panel.js +++ b/static/scripts/packed/mvc/history/current-history-panel.js @@ -1,1 +1,1 @@ -define(["mvc/dataset/hda-edit","mvc/history/history-panel"],function(b,e){var c=SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});c.storageKey=function d(){return("history-panel")};var a=e.HistoryPanel.extend({HDAViewClass:b.HDAEditView,emptyMsg:_l("This history is empty. Click 'Get Data' on the left pane to start"),noneFoundMsg:_l("No matching datasets found"),initialize:function(f){f=f||{};this.preferences=new c(_.extend({id:c.storageKey()},_.pick(f,_.keys(c.prototype.defaults))));e.HistoryPanel.prototype.initialize.call(this,f);if(this.model){console.debug(this.model);this.model.checkForUpdates()}},loadCurrentHistory:function(g){var f=this;return this.loadHistoryWithHDADetails("current",g).then(function(i,h){f.trigger("current-history",f)})},switchToHistory:function(i,h){var f=this,g=function(){return jQuery.post(galaxy_config.root+"api/histories/"+i+"/set_as_current")};return this.loadHistoryWithHDADetails(i,h,g).then(function(k,j){f.trigger("switched-history",f)})},createNewHistory:function(h){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var f=this,g=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,h,g).then(function(j,i){f.trigger("new-history",f)})},setModel:function(h,f,g){g=g||{};e.HistoryPanel.prototype.setModel.call(this,h,f,g);this.model.checkForUpdates();return this},_setUpModelEventHandlers:function(){e.HistoryPanel.prototype._setUpModelEventHandlers.call(this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("state:ready",function(g,h,f){if((!g.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[g.id])}},this)},render:function(h,i){h=(h===undefined)?(this.fxSpeed):(h);var f=this,g;if(this.model){g=this.renderModel()}else{g=this.renderWithoutModel()}$(f).queue("fx",[function(j){if(h&&f.$el.is(":visible")){f.$el.fadeOut(h,j)}else{j()}},function(j){f.$el.empty();if(g){f.$el.append(g.children());f.renderBasedOnPrefs()}j()},function(j){if(h&&!f.$el.is(":visible")){f.$el.fadeIn(h,j)}else{j()}},function(j){if(i){i.call(this)}f.trigger("rendered",this);j()}]);return this},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.showSearchControls(0)}},_renderTags:function(f){var g=this;e.HistoryPanel.prototype._renderTags.call(this,f);if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}this.tagsEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(h){g.preferences.set("tagsEditorShown",h.hidden)})},_renderAnnotation:function(f){var g=this;e.HistoryPanel.prototype._renderAnnotation.call(this,f);if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}this.annotationEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(h){g.preferences.set("annotationEditorShown",h.hidden)})},connectToQuotaMeter:function(f){if(!f){return this}this.listenTo(f,"quota:over",this.showQuotaMessage);this.listenTo(f,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(f&&f.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var f=this.$el.find(".quota-message");if(f.is(":hidden")){f.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var f=this.$el.find(".quota-message");if(!f.is(":hidden")){f.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(f){if(!f){return this}this.on("new-storage",function(h,g){if(f&&h){f.findItemByHtml(_l("Include Deleted Datasets")).checked=h.get("show_deleted");f.findItemByHtml(_l("Include Hidden Datasets")).checked=h.get("show_hidden")}});return this},toString:function(){return"CurrentHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{CurrentHistoryPanel:a}}); \ No newline at end of file +define(["mvc/dataset/hda-edit","mvc/history/history-panel"],function(b,e){var c=SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});c.storageKey=function d(){return("history-panel")};var a=e.HistoryPanel.extend({HDAViewClass:b.HDAEditView,emptyMsg:_l("This history is empty. Click 'Get Data' on the left pane to start"),noneFoundMsg:_l("No matching datasets found"),initialize:function(f){f=f||{};this.preferences=new c(_.extend({id:c.storageKey()},_.pick(f,_.keys(c.prototype.defaults))));e.HistoryPanel.prototype.initialize.call(this,f)},loadCurrentHistory:function(g){var f=this;return this.loadHistoryWithHDADetails("current",g).then(function(i,h){f.trigger("current-history",f)})},switchToHistory:function(i,h){var f=this,g=function(){return jQuery.post(galaxy_config.root+"api/histories/"+i+"/set_as_current")};return this.loadHistoryWithHDADetails(i,h,g).then(function(k,j){f.trigger("switched-history",f)})},createNewHistory:function(h){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",_l("You must be logged in to create histories"));return $.when()}var f=this,g=function(){return jQuery.post(galaxy_config.root+"api/histories",{current:true})};return this.loadHistory(undefined,h,g).then(function(j,i){f.trigger("new-history",f)})},setModel:function(g,f,h){e.HistoryPanel.prototype.setModel.call(this,g,f,h);if(this.model){this.log("checking for updates");this.model.checkForUpdates()}return this},_setUpModelEventHandlers:function(){e.HistoryPanel.prototype._setUpModelEventHandlers.call(this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.hdas.on("state:ready",function(g,h,f){if((!g.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[g.id])}},this)},render:function(h,i){h=(h===undefined)?(this.fxSpeed):(h);var f=this,g;if(this.model){g=this.renderModel()}else{g=this.renderWithoutModel()}$(f).queue("fx",[function(j){if(h&&f.$el.is(":visible")){f.$el.fadeOut(h,j)}else{j()}},function(j){f.$el.empty();if(g){f.$el.append(g.children());f.renderBasedOnPrefs()}j()},function(j){if(h&&!f.$el.is(":visible")){f.$el.fadeIn(h,j)}else{j()}},function(j){if(i){i.call(this)}f.trigger("rendered",this);j()}]);return this},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.toggleSearchControls(0,true)}},toggleSearchControls:function(g,f){var h=e.HistoryPanel.prototype.toggleSearchControls.call(this,g,f);this.preferences.set("searching",h)},_renderTags:function(f){var g=this;e.HistoryPanel.prototype._renderTags.call(this,f);if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}this.tagsEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(h){g.preferences.set("tagsEditorShown",h.hidden)})},_renderAnnotation:function(f){var g=this;e.HistoryPanel.prototype._renderAnnotation.call(this,f);if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}this.annotationEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(h){g.preferences.set("annotationEditorShown",h.hidden)})},connectToQuotaMeter:function(f){if(!f){return this}this.listenTo(f,"quota:over",this.showQuotaMessage);this.listenTo(f,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(f&&f.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var f=this.$el.find(".quota-message");if(f.is(":hidden")){f.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var f=this.$el.find(".quota-message");if(!f.is(":hidden")){f.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(f){if(!f){return this}this.on("new-storage",function(h,g){if(f&&h){f.findItemByHtml(_l("Include Deleted Datasets")).checked=h.get("show_deleted");f.findItemByHtml(_l("Include Hidden Datasets")).checked=h.get("show_hidden")}});return this},toString:function(){return"CurrentHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{CurrentHistoryPanel:a}}); \ No newline at end of file diff -r 1ed94d89769ed2b51232c6c714c3bb21d93cc3ce -r 707ba3eee2abb828ac262e76c5b9fbbc6c85cd03 static/scripts/packed/mvc/history/history-panel.js --- a/static/scripts/packed/mvc/history/history-panel.js +++ b/static/scripts/packed/mvc/history/history-panel.js @@ -1,1 +1,1 @@ -define(["mvc/dataset/hda-model","mvc/dataset/hda-edit","mvc/history/readonly-history-panel"],function(c,a,b){var d=b.ReadOnlyHistoryPanel.extend({HDAViewClass:a.HDAEditView,initialize:function(e){e=e||{};this.selectedHdaIds=[];this.tagsEditor=null;this.annotationEditor=null;this.selecting=e.selecting||false;this.annotationEditorShown=e.annotationEditorShown||false;this.tagsEditorShown=e.tagsEditorShown||false;b.ReadOnlyHistoryPanel.prototype.initialize.call(this,e)},_setUpModelEventHandlers:function(){b.ReadOnlyHistoryPanel.prototype._setUpModelEventHandlers.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);this.model.hdas.on("change:deleted",this.handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this.handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(e){this.model.fetch()},this)},renderModel:function(){var e=$("<div/>");e.append(d.templates.historyPanel(this.model.toJSON()));if(Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(e);this._renderAnnotation(e)}e.find(".history-secondary-actions").prepend(this._renderSelectButton());e.find(".history-dataset-actions").toggle(this.selecting);e.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(e);this.renderHdas(e);return e},_renderTags:function(e){var f=this;this.tagsEditor=new TagsEditor({model:this.model,el:e.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){f.toggleHDATagEditors(true,f.fxSpeed)},onhide:function(){f.toggleHDATagEditors(false,f.fxSpeed)},$activator:faIconButton({title:_l("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(e.find(".history-secondary-actions"))})},_renderAnnotation:function(e){var f=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:e.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){f.toggleHDAAnnotationEditors(true,f.fxSpeed)},onhide:function(){f.toggleHDAAnnotationEditors(false,f.fxSpeed)},$activator:faIconButton({title:_l("Edit history Annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(e.find(".history-secondary-actions"))})},_renderSelectButton:function(e){return faIconButton({title:_l("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(e){e=e||this.$el;e.find("[title]").tooltip({placement:"bottom"});if(!this.model){return}this._setUpDatasetActionsPopup(e);if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var f=this;e.find(".history-name").attr("title",_l("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(g){var h=f.model.get("name");if(g&&g!==h){f.$el.find(".history-name").text(g);f.model.save({name:g}).fail(function(){f.$el.find(".history-name").text(f.model.previous("name"))})}else{f.$el.find(".history-name").text(h)}}})},_setUpDatasetActionsPopup:function(e){var f=this;(new PopupMenu(e.find(".history-dataset-action-popup-btn"),[{html:_l("Hide datasets"),func:function(){var g=c.HistoryDatasetAssociation.prototype.hide;f.getSelectedHdaCollection().ajaxQueue(g)}},{html:_l("Unhide datasets"),func:function(){var g=c.HistoryDatasetAssociation.prototype.unhide;f.getSelectedHdaCollection().ajaxQueue(g)}},{html:_l("Delete datasets"),func:function(){var g=c.HistoryDatasetAssociation.prototype["delete"];f.getSelectedHdaCollection().ajaxQueue(g)}},{html:_l("Undelete datasets"),func:function(){var g=c.HistoryDatasetAssociation.prototype.undelete;f.getSelectedHdaCollection().ajaxQueue(g)}},{html:_l("Permanently delete datasets"),func:function(){if(confirm(_l("This will permanently remove the data in your datasets. Are you sure?"))){var g=c.HistoryDatasetAssociation.prototype.purge;f.getSelectedHdaCollection().ajaxQueue(g)}}}]))},handleHdaDeletionChange:function(e){if(e.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[e.id])}},handleHdaVisibleChange:function(e){if(e.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[e.id])}},createHdaView:function(f){var e=f.get("id"),g=new this.HDAViewClass({model:f,linkTarget:this.linkTarget,expanded:this.storage.get("expandedHdas")[e],selectable:this.selecting,hasUser:this.model.ownedByCurrUser(),logger:this.logger,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});this._setUpHdaListeners(g);return g},_setUpHdaListeners:function(f){var e=this;b.ReadOnlyHistoryPanel.prototype._setUpHdaListeners.call(this,f);f.on("selected",function(g){var h=g.model.get("id");e.selectedHdaIds=_.union(e.selectedHdaIds,[h])});f.on("de-selected",function(g){var h=g.model.get("id");e.selectedHdaIds=_.without(e.selectedHdaIds,h)})},toggleHDATagEditors:function(e){var f=arguments;_.each(this.hdaViews,function(g){if(g.tagsEditor){g.tagsEditor.toggle.apply(g.tagsEditor,f)}})},toggleHDAAnnotationEditors:function(e){var f=arguments;_.each(this.hdaViews,function(g){if(g.annotationEditor){g.annotationEditor.toggle.apply(g.annotationEditor,f)}})},removeHdaView:function(f){if(!f){return}var e=this;f.$el.fadeOut(e.fxSpeed,function(){f.off();f.remove();delete e.hdaViews[f.model.id];if(_.isEmpty(e.hdaViews)){e.$el.find(e.emptyMsgSelector).fadeIn(e.fxSpeed,function(){e.trigger("empty-history",e)})}})},events:_.extend(_.clone(b.ReadOnlyHistoryPanel.prototype.events),{"click .history-select-btn":function(f){this.toggleSelectors(this.fxSpeed)},"click .history-select-all-datasets-btn":"selectAllDatasets","click .history-deselect-all-datasets-btn":"deselectAllDatasets"}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},showSelectors:function(e){this.selecting=true;this.$el.find(".history-dataset-actions").slideDown(e);_.each(this.hdaViews,function(f){f.showSelector(e)});this.selectedHdaIds=[]},hideSelectors:function(e){this.selecting=false;this.$el.find(".history-dataset-actions").slideUp(e);_.each(this.hdaViews,function(f){f.hideSelector(e)});this.selectedHdaIds=[]},toggleSelectors:function(e){if(!this.selecting){this.showSelectors(e)}else{this.hideSelectors(e)}},selectAllDatasets:function(e){_.each(this.hdaViews,function(f){f.select(e)})},deselectAllDatasets:function(e){_.each(this.hdaViews,function(f){f.deselect(e)})},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(e){return e.selected})},getSelectedHdaCollection:function(){return new c.HDACollection(_.map(this.getSelectedHdaViews(),function(e){return e.model}),{historyId:this.model.id})},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanel:d}}); \ No newline at end of file +define(["mvc/dataset/hda-model","mvc/dataset/hda-edit","mvc/history/readonly-history-panel"],function(c,a,b){var d=b.ReadOnlyHistoryPanel.extend({HDAViewClass:a.HDAEditView,initialize:function(e){e=e||{};this.selectedHdaIds=[];this.tagsEditor=null;this.annotationEditor=null;this.selecting=e.selecting||false;this.annotationEditorShown=e.annotationEditorShown||false;this.tagsEditorShown=e.tagsEditorShown||false;b.ReadOnlyHistoryPanel.prototype.initialize.call(this,e)},_setUpModelEventHandlers:function(){b.ReadOnlyHistoryPanel.prototype._setUpModelEventHandlers.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);this.model.hdas.on("change:deleted",this._handleHdaDeletionChange,this);this.model.hdas.on("change:visible",this._handleHdaVisibleChange,this);this.model.hdas.on("change:purged",function(e){this.model.fetch()},this)},renderModel:function(){var e=$("<div/>");e.append(d.templates.historyPanel(this.model.toJSON()));if(Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(e);this._renderAnnotation(e)}e.find(".history-secondary-actions").prepend(this._renderSelectButton());e.find(".history-dataset-actions").toggle(this.selecting);e.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(e);this.renderHdas(e);return e},_renderTags:function(e){var f=this;this.tagsEditor=new TagsEditor({model:this.model,el:e.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){f.toggleHDATagEditors(true,f.fxSpeed)},onhide:function(){f.toggleHDATagEditors(false,f.fxSpeed)},$activator:faIconButton({title:_l("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(e.find(".history-secondary-actions"))})},_renderAnnotation:function(e){var f=this;this.annotationEditor=new AnnotationEditor({model:this.model,el:e.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){f.toggleHDAAnnotationEditors(true,f.fxSpeed)},onhide:function(){f.toggleHDAAnnotationEditors(false,f.fxSpeed)},$activator:faIconButton({title:_l("Edit history Annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(e.find(".history-secondary-actions"))})},_renderSelectButton:function(e){return faIconButton({title:_l("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(e){e=e||this.$el;b.ReadOnlyHistoryPanel.prototype._setUpBehaviours.call(this,e);if(!this.model){return}this._setUpDatasetActionsPopup(e);if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var f=this;e.find(".history-name").attr("title",_l("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(g){var h=f.model.get("name");if(g&&g!==h){f.$el.find(".history-name").text(g);f.model.save({name:g}).fail(function(){f.$el.find(".history-name").text(f.model.previous("name"))})}else{f.$el.find(".history-name").text(h)}}})},_setUpDatasetActionsPopup:function(e){var f=this;(new PopupMenu(e.find(".history-dataset-action-popup-btn"),[{html:_l("Hide datasets"),func:function(){var g=c.HistoryDatasetAssociation.prototype.hide;f.getSelectedHdaCollection().ajaxQueue(g)}},{html:_l("Unhide datasets"),func:function(){var g=c.HistoryDatasetAssociation.prototype.unhide;f.getSelectedHdaCollection().ajaxQueue(g)}},{html:_l("Delete datasets"),func:function(){var g=c.HistoryDatasetAssociation.prototype["delete"];f.getSelectedHdaCollection().ajaxQueue(g)}},{html:_l("Undelete datasets"),func:function(){var g=c.HistoryDatasetAssociation.prototype.undelete;f.getSelectedHdaCollection().ajaxQueue(g)}},{html:_l("Permanently delete datasets"),func:function(){if(confirm(_l("This will permanently remove the data in your datasets. Are you sure?"))){var g=c.HistoryDatasetAssociation.prototype.purge;f.getSelectedHdaCollection().ajaxQueue(g)}}}]))},_handleHdaDeletionChange:function(e){if(e.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[e.id])}},_handleHdaVisibleChange:function(e){if(e.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[e.id])}},_createHdaView:function(f){var e=f.get("id"),g=new this.HDAViewClass({model:f,linkTarget:this.linkTarget,expanded:this.storage.get("expandedHdas")[e],selectable:this.selecting,hasUser:this.model.ownedByCurrUser(),logger:this.logger,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});this._setUpHdaListeners(g);return g},_setUpHdaListeners:function(f){var e=this;b.ReadOnlyHistoryPanel.prototype._setUpHdaListeners.call(this,f);f.on("selected",function(g){var h=g.model.get("id");e.selectedHdaIds=_.union(e.selectedHdaIds,[h])});f.on("de-selected",function(g){var h=g.model.get("id");e.selectedHdaIds=_.without(e.selectedHdaIds,h)})},toggleHDATagEditors:function(e){var f=arguments;_.each(this.hdaViews,function(g){if(g.tagsEditor){g.tagsEditor.toggle.apply(g.tagsEditor,f)}})},toggleHDAAnnotationEditors:function(e){var f=arguments;_.each(this.hdaViews,function(g){if(g.annotationEditor){g.annotationEditor.toggle.apply(g.annotationEditor,f)}})},removeHdaView:function(f){if(!f){return}var e=this;f.$el.fadeOut(e.fxSpeed,function(){f.off();f.remove();delete e.hdaViews[f.model.id];if(_.isEmpty(e.hdaViews)){e.$emptyMessage().fadeIn(e.fxSpeed,function(){e.trigger("empty-history",e)})}})},events:_.extend(_.clone(b.ReadOnlyHistoryPanel.prototype.events),{"click .history-select-btn":function(f){this.toggleSelectors(this.fxSpeed)},"click .history-select-all-datasets-btn":"selectAllDatasets","click .history-deselect-all-datasets-btn":"deselectAllDatasets"}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},showSelectors:function(e){this.selecting=true;this.$el.find(".history-dataset-actions").slideDown(e);_.each(this.hdaViews,function(f){f.showSelector(e)});this.selectedHdaIds=[]},hideSelectors:function(e){this.selecting=false;this.$el.find(".history-dataset-actions").slideUp(e);_.each(this.hdaViews,function(f){f.hideSelector(e)});this.selectedHdaIds=[]},toggleSelectors:function(e){if(!this.selecting){this.showSelectors(e)}else{this.hideSelectors(e)}},selectAllDatasets:function(e){_.each(this.hdaViews,function(f){f.select(e)})},deselectAllDatasets:function(e){_.each(this.hdaViews,function(f){f.deselect(e)})},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(e){return e.selected})},getSelectedHdaCollection:function(){return new c.HDACollection(_.map(this.getSelectedHdaViews(),function(e){return e.model}),{historyId:this.model.id})},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanel:d}}); \ No newline at end of file diff -r 1ed94d89769ed2b51232c6c714c3bb21d93cc3ce -r 707ba3eee2abb828ac262e76c5b9fbbc6c85cd03 static/scripts/packed/mvc/history/readonly-history-panel.js --- a/static/scripts/packed/mvc/history/readonly-history-panel.js +++ b/static/scripts/packed/mvc/history/readonly-history-panel.js @@ -1,1 +1,1 @@ -define(["mvc/history/history-model","mvc/dataset/hda-base"],function(e,a){var d=SessionStorageModel.extend({defaults:{expandedHdas:{},show_deleted:false,show_hidden:false},addExpandedHda:function(f){this.save("expandedHdas",_.extend(this.get("expandedHdas"),_.object([f],[true])))},removeExpandedHda:function(f){this.save("expandedHdas",_.omit(this.get("expandedHdas"),f))},toString:function(){return"HistoryPrefs("+this.id+")"}});d.historyStorageKey=function c(f){if(!f){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+f)}return("history:"+f)};var b=Backbone.View.extend(LoggableMixin).extend({HDAViewClass:a.HDABaseView,tagName:"div",className:"history-panel",fxSpeed:"fast",datasetsSelector:".datasets-list",msgsSelector:".message-container",emptyMsgSelector:".empty-history-message",emptyMsg:_l("This history is empty"),noneFoundMsg:_l("No matching datasets found"),initialize:function(f){f=f||{};if(f.logger){this.logger=f.logger}this.log(this+".initialize:",f);this.linkTarget=f.linkTarget||"_blank";this.fxSpeed=_.has(f,"fxSpeed")?(f.fxSpeed):(this.fxSpeed);this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);this.filters=[];this.searchFor="";this._setUpListeners();if(this.model){if(this.logger){this.model.logger=this.logger}this._setUpWebStorage(f.initiallyExpanded,f.show_deleted,f.show_hidden);this._setUpModelEventHandlers()}if(f.onready){f.onready.call(this)}},_setUpListeners:function(){this.on("error",function(g,j,f,i,h){this.errorHandler(g,j,f,i,h)});this.on("loading-history",function(){this.showLoadingIndicator("loading history...",40)});this.on("loading-done",function(){this.hideLoadingIndicator(40);if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});if(this.logger){this.on("all",function(f){this.log(this+"",arguments)},this)}},errorHandler:function(h,k,g,j,i){var f=this._parseErrorMessage(h,k,g,j,i);if(k&&k.status===0&&k.readyState===0){}else{if(k&&k.status===502){}else{if(!this.$el.find(this.msgsSelector).is(":visible")){this.once("rendered",function(){this.displayMessage("error",f.message,f.details)})}else{this.displayMessage("error",f.message,f.details)}}}},_parseErrorMessage:function(i,m,h,l,k){var g=Galaxy.currUser,f={message:this._bePolite(l),details:{user:(g instanceof User)?(g.toJSON()):(g+""),source:(i instanceof Backbone.Model)?(i.toJSON()):(i+""),xhr:m,options:(m)?(_.omit(h,"xhr")):(h)}};_.extend(f.details,k||{});if(m&&_.isFunction(m.getAllResponseHeaders)){var j=m.getAllResponseHeaders();j=_.compact(j.split("\n"));j=_.map(j,function(n){return n.split(": ")});f.details.xhr.responseHeaders=_.object(j)}return f},_bePolite:function(f){f=f||_l("An error occurred while getting updates from the server");return f+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadHistoryWithHDADetails:function(i,h,g,k){var f=this,j=function(l){return f.getExpandedHdaIds(l.id)};return this.loadHistory(i,h,g,k,j)},loadHistory:function(i,h,g,l,j){this.trigger("loading-history",this);h=h||{};var f=this;var k=e.History.getHistoryData(i,{historyFn:g,hdaFn:l,hdaDetailIds:h.initiallyExpanded||j});return this._loadHistoryFromXHR(k,h).fail(function(o,m,n){f.trigger("error",f,o,h,_l("An error was encountered while "+m),{historyId:i,history:n||{}})}).always(function(){f.trigger("loading-done",f)})},_loadHistoryFromXHR:function(h,g){var f=this;h.then(function(i,j){f.setModel(i,j,g)});h.fail(function(j,i){f.render()});return h},setModel:function(h,f,g){g=g||{};if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.hdaViews={};if(Galaxy&&Galaxy.currUser){h.user=Galaxy.currUser.toJSON()}this.model=new e.History(h,f,g);if(this.logger){this.model.logger=this.logger}this._setUpWebStorage(g.initiallyExpanded,g.show_deleted,g.show_hidden);this._setUpModelEventHandlers();this.selectedHdaIds=[];this.trigger("new-model",this);this.render();return this},_setUpWebStorage:function(g,f,h){this.storage=new d({id:d.historyStorageKey(this.model.get("id"))});if(_.isObject(g)){this.storage.set("exandedHdas",g)}if(_.isBoolean(f)){this.storage.set("show_deleted",f)}if(_.isBoolean(h)){this.storage.set("show_hidden",h)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get())},clearWebStorage:function(){for(var f in sessionStorage){if(f.indexOf("history:")===0){sessionStorage.removeItem(f)}}},getStoredOptions:function(g){if(!g||g==="current"){return(this.storage)?(this.storage.get()):({})}var f=sessionStorage.getItem(d.historyStorageKey(g));return(f===null)?({}):(JSON.parse(f))},getExpandedHdaIds:function(f){var g=this.getStoredOptions(f).expandedHdas;return((_.isEmpty(g))?([]):(_.keys(g)))},_setUpModelEventHandlers:function(){this.model.hdas.on("add",this.addHdaView,this);this.model.on("error error:hdas",function(g,i,f,h){this.errorHandler(g,i,f,h)},this)},render:function(h,i){h=(h===undefined)?(this.fxSpeed):(h);var f=this,g;if(this.model){g=this.renderModel()}else{g=this.renderWithoutModel()}$(f).queue("fx",[function(j){if(h&&f.$el.is(":visible")){f.$el.fadeOut(h,j)}else{j()}},function(j){f.$el.empty();if(g){f.$el.append(g.children())}j()},function(j){if(h&&!f.$el.is(":visible")){f.$el.fadeIn(h,j)}else{j()}},function(j){if(i){i.call(this)}f.trigger("rendered",this);j()}]);return this},renderWithoutModel:function(){var f=$("<div/>"),g=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return f.append(g)},renderModel:function(){var f=$("<div/>");f.append(b.templates.historyPanel(this.model.toJSON()));f.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(f);this.renderHdas(f);return f},_renderSearchButton:function(f){return faIconButton({title:_l("Search datasets"),classes:"history-search-btn",faIcon:"fa-search"})},_setUpBehaviours:function(f){f=f||this.$el;f.find("[title]").tooltip({placement:"bottom"})},createHdaView:function(g){var f=g.get("id"),h=new this.HDAViewClass({model:g,linkTarget:this.linkTarget,expanded:this.storage.get("expandedHdas")[f],hasUser:this.model.ownedByCurrUser(),logger:this.logger});this._setUpHdaListeners(h);return h},_setUpHdaListeners:function(g){var f=this;g.on("body-expanded",function(h){f.storage.addExpandedHda(h)});g.on("body-collapsed",function(h){f.storage.removeExpandedHda(h)});g.on("error",function(i,k,h,j){f.errorHandler(i,k,h,j)})},renderHdas:function(g){g=g||this.$el;var f=this,i={},h=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);g.find(this.datasetsSelector).empty();if(h.length){h.each(function(k){var j=k.get("id"),l=f.createHdaView(k);i[j]=l;if(_.contains(f.selectedHdaIds,j)){l.selected=true}f.attachHdaView(l.render(),g)});g.find(this.emptyMsgSelector).hide()}else{g.find(this.emptyMsgSelector).text((this.model.hdas.length&&this.searchFor)?(this.noneFoundMsg):(this.emptyMsg)).show()}this.hdaViews=i;return this.hdaViews},attachHdaView:function(h,g){g=g||this.$el;var f=g.find(this.datasetsSelector);f.prepend(h.$el)},addHdaView:function(i){this.log("add."+this,i);var g=this;if(!i.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return}$({}).queue([function h(k){var j=g.$el.find(g.emptyMsgSelector);if(j.is(":visible")){j.fadeOut(g.fxSpeed,k)}else{k()}},function f(k){g.scrollToTop();var j=g.$el.find(g.datasetsSelector),l=g.createHdaView(i);g.hdaViews[i.id]=l;l.render().$el.hide().prependTo(j).slideDown(g.fxSpeed)}])},refreshHdas:function(g,f){if(this.model){return this.model.refresh(g,f)}return $.when()},events:{"click .message-container":"clearMessages","click .history-search-btn":"toggleSearchControls"},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(f){f.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{})},toggleShowDeleted:function(f){f=(f!==undefined)?(f):(!this.storage.get("show_deleted"));this.storage.set("show_deleted",f);this.renderHdas();return this.storage.get("show_deleted")},toggleShowHidden:function(f){f=(f!==undefined)?(f):(!this.storage.get("show_hidden"));this.storage.set("show_hidden",f);this.renderHdas();return this.storage.get("show_hidden")},setUpSearchInput:function(g){var h=this,i=".history-search-input";function f(j){if(h.model.hdas.haveDetails()){h.searchHdas(j);return}h.$el.find(i).searchInput("toggle-loading");h.model.hdas.fetchAllDetails({silent:true}).always(function(){h.$el.find(i).searchInput("toggle-loading")}).done(function(){h.searchHdas(j)})}g.searchInput({initialVal:h.searchFor,name:"history-search",placeholder:"search datasets",classes:"history-search",onfirstsearch:f,onsearch:_.bind(this.searchHdas,this),onclear:_.bind(this.clearHdaSearch,this)});return g},showSearchControls:function(g){g=(g===undefined)?(this.fxSpeed):(g);var f=this.$el.find(".history-search-controls"),h=f.find(".history-search-input");if(!h.children().size()){this.setUpSearchInput(h)}f.slideDown(g,function(){$(this).find("input").focus()})},hideSearchControls:function(f){f=(f===undefined)?(this.fxSpeed):(f);this.$el.find(".history-search-controls").slideUp(f)},toggleSearchControls:function(f){speed=(jQuery.type(f)==="number")?(f):(this.fxSpeed);if(this.$el.find(".history-search-controls").is(":visible")){this.hideSearchControls(speed)}else{this.showSearchControls(speed)}},searchHdas:function(f){var g=this;this.searchFor=f;this.filters=[function(h){return h.matchesAll(g.searchFor)}];this.trigger("search:searching",f,this);this.renderHdas()},clearHdaSearch:function(f){this.searchFor="";this.filters=[];this.trigger("search:clear",this);this.renderHdas()},showLoadingIndicator:function(g,f,h){f=(f!==undefined)?(f):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,h)}else{this.$el.fadeOut(f);this.indicator.show(g,f,h)}},hideLoadingIndicator:function(f,g){f=(f!==undefined)?(f):(this.fxSpeed);if(this.indicator){this.indicator.hide(f,g)}},displayMessage:function(k,l,j){var h=this;this.scrollToTop();var i=this.$el.find(this.msgsSelector),f=$("<div/>").addClass(k+"message").html(l);if(!_.isEmpty(j)){var g=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(h._messageToModalOptions(k,l,j));return false});f.append(" ",g)}return i.html(f)},_messageToModalOptions:function(j,l,i){var f=this,k=$("<div/>"),h={title:"Details"};function g(m){m=_.omit(m,_.functions(m));return["<table>",_.map(m,function(o,n){o=(_.isObject(o))?(g(o)):(o);return'<tr><td style="vertical-align: top; color: grey">'+n+'</td><td style="padding-left: 8px">'+o+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(i)){h.body=k.append(g(i))}else{h.body=k.html(i)}h.buttons={Ok:function(){Galaxy.modal.hide();f.clearMessages()}};return h},clearMessages:function(){var f=this.$el.find(this.msgsSelector);f.empty()},scrollPosition:function(){return this.$el.scrollTop()},scrollTo:function(f){this.$el.scrollTop(f);return this},scrollToTop:function(){this.$el.scrollTop(0);return this},scrollIntoView:function(k,g){if(g===undefined){this.scrollTo(k);return this}var f=window,j=this.$el.parent(),i=$(f).innerHeight(),h=(i/2)-(g/2);this.scrollTo(k-h);return this},scrollToId:function(g){if((!g)||(!this.hdaViews[g])){return this}var f=this.hdaViews[g].$el;this.scrollIntoView(f.offset().top,f.outerHeight());return this},scrollToHid:function(f){var g=this.model.hdas.getByHid(f);if(!g){return this}return this.scrollToId(g.id)},toString:function(){return"ReadOnlyHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});b.templates={historyPanel:Handlebars.templates["template-history-historyPanel"]};return{ReadOnlyHistoryPanel:b}}); \ No newline at end of file +define(["mvc/history/history-model","mvc/dataset/hda-base"],function(g,a){var f=SessionStorageModel.extend({defaults:{expandedHdas:{},show_deleted:false,show_hidden:false},addExpandedHda:function(i){var h="expandedHdas";this.save(h,_.extend(this.get(h),_.object([i],[true])))},removeExpandedHda:function(i){var h="expandedHdas";this.save(h,_.omit(this.get(h),i))},toString:function(){return"HistoryPrefs("+this.id+")"}});f.storageKeyPrefix="history:";f.historyStorageKey=function e(h){if(!h){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+h)}return(f.storageKeyPrefix+h)};f.get=function d(h){return new f({id:f.historyStorageKey(h)})};f.clearAll=function c(i){for(var h in sessionStorage){if(h.indexOf(f.storageKeyPrefix)===0){sessionStorage.removeItem(h)}}};var b=Backbone.View.extend(LoggableMixin).extend({HDAViewClass:a.HDABaseView,tagName:"div",className:"history-panel",fxSpeed:"fast",emptyMsg:_l("This history is empty"),noneFoundMsg:_l("No matching datasets found"),initialize:function(h){h=h||{};if(h.logger){this.logger=h.logger}this.log(this+".initialize:",h);this.linkTarget=h.linkTarget||"_blank";this.fxSpeed=_.has(h,"fxSpeed")?(h.fxSpeed):(this.fxSpeed);this.filters=[];this.searchFor="";this.findContainerFn=h.findContainerFn;this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);this._setUpListeners();var i=_.pick(h,"initiallyExpanded","show_deleted","show_hidden");this.setModel(this.model,i,false);if(h.onready){h.onready.call(this)}},_setUpListeners:function(){this.on("error",function(i,l,h,k,j){this.errorHandler(i,l,h,k,j)});this.on("loading-history",function(){this._showLoadingIndicator("loading history...",40)});this.on("loading-done",function(){this._hideLoadingIndicator(40);if(_.isEmpty(this.hdaViews)){this.trigger("empty-history",this)}});this.once("rendered",function(){this.trigger("rendered:initial",this);return false});if(this.logger){this.on("all",function(h){this.log(this+"",arguments)},this)}return this},errorHandler:function(j,m,i,l,k){console.error(j,m,i,l,k);if(m&&m.status===0&&m.readyState===0){}else{if(m&&m.status===502){}else{var h=this._parseErrorMessage(j,m,i,l,k);if(!this.$messages().is(":visible")){this.once("rendered",function(){this.displayMessage("error",h.message,h.details)})}else{this.displayMessage("error",h.message,h.details)}}}},_parseErrorMessage:function(k,o,j,n,m){var i=Galaxy.currUser,h={message:this._bePolite(n),details:{user:(i instanceof User)?(i.toJSON()):(i+""),source:(k instanceof Backbone.Model)?(k.toJSON()):(k+""),xhr:o,options:(o)?(_.omit(j,"xhr")):(j)}};_.extend(h.details,m||{});if(o&&_.isFunction(o.getAllResponseHeaders)){var l=o.getAllResponseHeaders();l=_.compact(l.split("\n"));l=_.map(l,function(p){return p.split(": ")});h.details.xhr.responseHeaders=_.object(l)}return h},_bePolite:function(h){h=h||_l("An error occurred while getting updates from the server");return h+". "+_l("Please contact a Galaxy administrator if the problem persists.")},loadHistoryWithHDADetails:function(j,i,h,l){var k=function(m){return _.keys(f.get(m.id).get("expandedHdas"))};return this.loadHistory(j,i,h,l,k)},loadHistory:function(k,j,i,n,l){var h=this;j=j||{};h.trigger("loading-history",h);var m=g.History.getHistoryData(k,{historyFn:i,hdaFn:n,hdaDetailIds:j.initiallyExpanded||l});return h._loadHistoryFromXHR(m,j).fail(function(q,o,p){h.trigger("error",h,q,j,_l("An error was encountered while "+o),{historyId:k,history:p||{}})}).always(function(){h.trigger("loading-done",h)})},_loadHistoryFromXHR:function(j,i){var h=this;j.then(function(k,l){h.JSONToModel(k,l,i)});j.fail(function(l,k){h.render()});return j},JSONToModel:function(k,h,i){this.log("JSONToModel:",k,h,i);i=i||{};if(Galaxy&&Galaxy.currUser){k.user=Galaxy.currUser.toJSON()}var j=new g.History(k,h,i);this.setModel(j);return this},setModel:function(i,h,j){h=h||{};j=(j!==undefined)?(j):(true);this.log("setModel:",i,h,j);this.freeModel();this.selectedHdaIds=[];if(i){if(Galaxy&&Galaxy.currUser){i.user=Galaxy.currUser.toJSON()}this.model=i;if(this.logger){this.model.logger=this.logger}this._setUpWebStorage(h.initiallyExpanded,h.show_deleted,h.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this)}if(j){this.render()}return this},freeModel:function(){if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.hdas)}this.freeHdaViews();return this},freeHdaViews:function(){this.hdaViews={};return this},_setUpWebStorage:function(i,h,j){this.storage=new f({id:f.historyStorageKey(this.model.get("id"))});if(_.isObject(i)){this.storage.set("exandedHdas",i)}if(_.isBoolean(h)){this.storage.set("show_deleted",h)}if(_.isBoolean(j)){this.storage.set("show_hidden",j)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get());return this},_setUpModelEventHandlers:function(){this.model.hdas.on("add",this.addHdaView,this);this.model.on("error error:hdas",function(i,k,h,j){this.errorHandler(i,k,h,j)},this);return this},render:function(j,k){this.log("render:",j,k);j=(j===undefined)?(this.fxSpeed):(j);var h=this,i;if(this.model){i=this.renderModel()}else{i=this.renderWithoutModel()}$(h).queue("fx",[function(l){if(j&&h.$el.is(":visible")){h.$el.fadeOut(j,l)}else{l()}},function(l){h.$el.empty();if(i){h.$el.append(i.children())}l()},function(l){if(j&&!h.$el.is(":visible")){h.$el.fadeIn(j,l)}else{l()}},function(l){if(k){k.call(this)}h.trigger("rendered",this);l()}]);return this},renderWithoutModel:function(){var h=$("<div/>"),i=$("<div/>").addClass("message-container").css({"margin-left":"4px","margin-right":"4px"});return h.append(i)},renderModel:function(){var h=$("<div/>");h.append(b.templates.historyPanel(this.model.toJSON()));h.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(h);this.renderHdas(h);return h},_renderSearchButton:function(h){return faIconButton({title:_l("Search datasets"),classes:"history-search-btn",faIcon:"fa-search"})},_setUpBehaviours:function(h){h=h||this.$el;h.find("[title]").tooltip({placement:"bottom"});this._setUpSearchInput(h.find(".history-search-controls .history-search-input"));return this},$container:function(){return(this.findContainerFn)?(this.findContainerFn.call(this)):(this.$el.parent())},$datasetsList:function(h){return(h||this.$el).find(".datasets-list")},$messages:function(h){return(h||this.$el).find(".message-container")},$emptyMessage:function(h){return(h||this.$el).find(".empty-history-message")},renderHdas:function(i){i=i||this.$el;var h=this,k={},j=this.model.hdas.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);this.$datasetsList(i).empty();if(j.length){j.each(function(m){var l=m.get("id"),n=h._createHdaView(m);k[l]=n;if(_.contains(h.selectedHdaIds,l)){n.selected=true}h.attachHdaView(n.render(),i)});h.$emptyMessage(i).hide()}else{h.$emptyMessage(i).text((this.model.hdas.length&&this.searchFor)?(this.noneFoundMsg):(this.emptyMsg)).show()}this.hdaViews=k;return this.hdaViews},_createHdaView:function(i){var h=i.get("id"),j=new this.HDAViewClass({model:i,linkTarget:this.linkTarget,expanded:this.storage.get("expandedHdas")[h],hasUser:this.model.ownedByCurrUser(),logger:this.logger});this._setUpHdaListeners(j);return j},_setUpHdaListeners:function(i){var h=this;i.on("error",function(k,m,j,l){h.errorHandler(k,m,j,l)});i.on("body-expanded",function(j){h.storage.addExpandedHda(j)});i.on("body-collapsed",function(j){h.storage.removeExpandedHda(j)});return this},attachHdaView:function(j,i){i=i||this.$el;var h=this.$datasetsList(i);h.prepend(j.$el);return this},addHdaView:function(k){this.log("add."+this,k);var i=this;if(!k.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return i}$({}).queue([function j(m){var l=i.$emptyMessage();if(l.is(":visible")){l.fadeOut(i.fxSpeed,m)}else{m()}},function h(l){var m=i._createHdaView(k);i.hdaViews[k.id]=m;m.render().$el.hide();i.scrollToTop();i.attachHdaView(m);m.$el.slideDown(i.fxSpeed)}]);return i},refreshHdas:function(i,h){if(this.model){return this.model.refresh(i,h)}return $.when()},events:{"click .message-container":"clearMessages","click .history-search-btn":"toggleSearchControls"},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(h){h.toggleBodyVisibility(null,false)});this.storage.set("expandedHdas",{});return this},toggleShowDeleted:function(h){h=(h!==undefined)?(h):(!this.storage.get("show_deleted"));this.storage.set("show_deleted",h);this.renderHdas();return this.storage.get("show_deleted")},toggleShowHidden:function(h){h=(h!==undefined)?(h):(!this.storage.get("show_hidden"));this.storage.set("show_hidden",h);this.renderHdas();return this.storage.get("show_hidden")},_setUpSearchInput:function(i){var j=this,k=".history-search-input";function h(l){if(j.model.hdas.haveDetails()){j.searchHdas(l);return}j.$el.find(k).searchInput("toggle-loading");j.model.hdas.fetchAllDetails({silent:true}).always(function(){j.$el.find(k).searchInput("toggle-loading")}).done(function(){j.searchHdas(l)})}i.searchInput({initialVal:j.searchFor,name:"history-search",placeholder:"search datasets",classes:"history-search",onfirstsearch:h,onsearch:_.bind(this.searchHdas,this),onclear:_.bind(this.clearHdaSearch,this)});return i},toggleSearchControls:function(j,h){var i=this.$el.find(".history-search-controls"),k=(jQuery.type(j)==="number")?(j):(this.fxSpeed);h=(h!==undefined)?(h):(!i.is(":visible"));if(h){i.slideDown(k,function(){$(this).find("input").focus()})}else{i.slideUp(k)}return h},searchHdas:function(h){var i=this;this.searchFor=h;this.filters=[function(j){return j.matchesAll(i.searchFor)}];this.trigger("search:searching",h,this);this.renderHdas();return this},clearHdaSearch:function(h){this.searchFor="";this.filters=[];this.trigger("search:clear",this);this.renderHdas();return this},_showLoadingIndicator:function(i,h,j){h=(h!==undefined)?(h):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,j)}else{this.$el.fadeOut(h);this.indicator.show(i,h,j)}},_hideLoadingIndicator:function(h,i){h=(h!==undefined)?(h):(this.fxSpeed);if(this.indicator){this.indicator.hide(h,i)}},displayMessage:function(m,n,l){var j=this;this.scrollToTop();var k=this.$messages(),h=$("<div/>").addClass(m+"message").html(n);if(!_.isEmpty(l)){var i=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(j._messageToModalOptions(m,n,l));return false});h.append(" ",i)}return k.html(h)},_messageToModalOptions:function(l,n,k){var h=this,m=$("<div/>"),j={title:"Details"};function i(o){o=_.omit(o,_.functions(o));return["<table>",_.map(o,function(q,p){q=(_.isObject(q))?(i(q)):(q);return'<tr><td style="vertical-align: top; color: grey">'+p+'</td><td style="padding-left: 8px">'+q+"</td></tr>"}).join(""),"</table>"].join("")}if(_.isObject(k)){j.body=m.append(i(k))}else{j.body=m.html(k)}j.buttons={Ok:function(){Galaxy.modal.hide();h.clearMessages()}};return j},clearMessages:function(){this.$messages().empty();return this},scrollPosition:function(){return this.$container().scrollTop()},scrollTo:function(h){this.$container().scrollTop(h);return this},scrollToTop:function(){this.$container().scrollTop(0);return this},scrollToId:function(i){if((!i)||(!this.hdaViews[i])){return this}var h=this.hdaViews[i];this.scrollTo(h.el.offsetTop);return this},scrollToHid:function(h){var i=this.model.hdas.getByHid(h);if(!i){return this}return this.scrollToId(i.id)},toString:function(){return"ReadOnlyHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});b.templates={historyPanel:Handlebars.templates["template-history-historyPanel"]};return{ReadOnlyHistoryPanel:b}}); \ No newline at end of file diff -r 1ed94d89769ed2b51232c6c714c3bb21d93cc3ce -r 707ba3eee2abb828ac262e76c5b9fbbc6c85cd03 static/style/blue/base.css --- a/static/style/blue/base.css +++ b/static/style/blue/base.css @@ -1602,7 +1602,11 @@ .workflow-invocation-complete{border:solid 1px #6A6;border-left-width:5px;margin:10px 0;padding-left:5px} .icon-btn{display:inline-block;height:22px;width:22px;text-align:center;line-height:19px;font-size:1.2em;border-radius:3px;border:1px solid #bfbfbf;background-color:#f2f2f2;color:#333;cursor:pointer} .icon-btn:hover{background-color:white;color:maroon} -.icon-btn.disabled{background-color:transparent;color:#bfbfbf} +.icon-btn.disabled{background-color:transparent;color:#BBB;border-color:#BBB} +.icon-btn-group{display:inline-block}.icon-btn-group .icon-btn:not(:last-child){margin:0px;border-radius:0px;border-right:none} +.icon-btn-group .icon-btn:first-child{margin-right:0px;border-top-left-radius:3px;border-bottom-left-radius:3px} +.icon-btn-group .icon-btn:last-child{margin-left:0px;border-radius:0px 3px 3px 0px} +.icon-btn-group .icon-btn:only-child{margin:0px;border-radius:3px} .search-input .search-query{width:100%;padding-right:24px} .search-input .search-clear,.search-input .search-loading{position:relative;display:inline-block;float:right;margin-top:-23px;margin-right:4px;font-size:1.4em;line-height:23px;color:grey} .search-input .search-clear:hover{color:#303030} @@ -1618,6 +1622,7 @@ .history-panel .dataset .dataset-primary-actions{display:inline-block;float:right;margin:6px 10px 0}.history-panel .dataset .dataset-primary-actions .icon-btn:not(:last-child){margin:0px;border-radius:0px;border-right:none} .history-panel .dataset .dataset-primary-actions .icon-btn:first-child{margin-right:0px;border-top-left-radius:3px;border-bottom-left-radius:3px} .history-panel .dataset .dataset-primary-actions .icon-btn:last-child{margin-left:0px;border-radius:0px 3px 3px 0px} +.history-panel .dataset .dataset-primary-actions .icon-btn:only-child{margin:0px;border-radius:3px} .history-panel .dataset .dataset-primary-actions .icon-btn{margin-left:2px} .history-panel .dataset .dataset-body{display:none;padding:0 10px 6px 8px}.history-panel .dataset .dataset-body [class$=messagesmall]{margin:0px 0px 8px 0px} .history-panel .dataset .dataset-body label{margin:0px;padding:0px;font-weight:normal} @@ -1632,9 +1637,14 @@ .history-panel .dataset .dataset-body .dataset-actions .left{display:inline-block;float:left}.history-panel .dataset .dataset-body .dataset-actions .left .icon-btn:not(:last-child){margin:0px;border-radius:0px;border-right:none} .history-panel .dataset .dataset-body .dataset-actions .left .icon-btn:first-child{margin-right:0px;border-top-left-radius:3px;border-bottom-left-radius:3px} .history-panel .dataset .dataset-body .dataset-actions .left .icon-btn:last-child{margin-left:0px;border-radius:0px 3px 3px 0px} +.history-panel .dataset .dataset-body .dataset-actions .left .icon-btn:only-child{margin:0px;border-radius:3px} .history-panel .dataset .dataset-body .dataset-actions .left .icon-btn{margin-right:2px} .history-panel .dataset .dataset-body .dataset-actions .left:not(:empty){margin-bottom:8px} -.history-panel .dataset .dataset-body .dataset-actions .right{float:right}.history-panel .dataset .dataset-body .dataset-actions .right .icon-btn{margin-left:2px} +.history-panel .dataset .dataset-body .dataset-actions .right{display:inline-block;float:right}.history-panel .dataset .dataset-body .dataset-actions .right .icon-btn:not(:last-child){margin:0px;border-radius:0px;border-right:none} +.history-panel .dataset .dataset-body .dataset-actions .right .icon-btn:first-child{margin-right:0px;border-top-left-radius:3px;border-bottom-left-radius:3px} +.history-panel .dataset .dataset-body .dataset-actions .right .icon-btn:last-child{margin-left:0px;border-radius:0px 3px 3px 0px} +.history-panel .dataset .dataset-body .dataset-actions .right .icon-btn:only-child{margin:0px;border-radius:3px} +.history-panel .dataset .dataset-body .dataset-actions .right .icon-btn{margin-left:2px} .history-panel .dataset .dataset-body .tags-display{display:none;margin-bottom:8px}.history-panel .dataset .dataset-body .tags-display .select2-container{min-width:0px}.history-panel .dataset .dataset-body .tags-display .select2-container .select2-choices{border-radius:3px} .history-panel .dataset .dataset-body .annotation-display{display:none;margin-bottom:8px}.history-panel .dataset .dataset-body .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.30000000000000004);padding:4px;white-space:pre-line} .history-panel .dataset .dataset-body .annotation-display .annotation:empty{height:22px} @@ -1643,9 +1653,6 @@ .history-panel .dataset .dataset-body .dataset-display-applications .display-application:last-child{margin-bottom:8px} .history-panel .dataset .dataset-body .dataset-peek{margin-bottom:8px}.history-panel .dataset .dataset-body .dataset-peek pre.peek{width:100%;margin:0px;border-radius:3px;background:white;color:black;font-size:10px;overflow:auto}.history-panel .dataset .dataset-body .dataset-peek pre.peek th{color:white;background:#5f6990} .history-panel .dataset .dataset-body .dataset-peek pre.peek table,.history-panel .dataset .dataset-body .dataset-peek pre.peek th,.history-panel .dataset .dataset-body .dataset-peek pre.peek tr,.history-panel .dataset .dataset-body .dataset-peek pre.peek td{font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:10px} -.history-panel .dataset .icon-btn-group{display:inline-block}.history-panel .dataset .icon-btn-group .icon-btn:not(:last-child){margin:0px;border-radius:0px;border-right:none} -.history-panel .dataset .icon-btn-group .icon-btn:first-child{margin-right:0px;border-top-left-radius:3px;border-bottom-left-radius:3px} -.history-panel .dataset .icon-btn-group .icon-btn:last-child{margin-left:0px;border-radius:0px 3px 3px 0px} .history-panel .dataset .state-icon{font-family:FontAwesome;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;display:inline-block;vertical-align:middle;width:16px;height:16px;line-height:16px;text-align:center;font-size:16px} .state-icon-error{background-color:white;border-radius:8px}.state-icon-error:before{font-size:20px;line-height:16px;color:red;content:"\f057"} .history-panel [class$="messagesmall"]{margin:0px} @@ -1659,7 +1666,11 @@ .history-panel .history-controls .history-title input{width:100%;margin:-2px 0 -2px -4px;font-weight:bold} .history-panel .history-controls .history-subtitle{margin-bottom:8px} .history-panel .history-controls .history-size{float:left} -.history-panel .history-controls .history-secondary-actions{float:right}.history-panel .history-controls .history-secondary-actions .icon-btn{margin-left:2px} +.history-panel .history-controls .history-secondary-actions{display:inline-block;float:right}.history-panel .history-controls .history-secondary-actions .icon-btn:not(:last-child){margin:0px;border-radius:0px;border-right:none} +.history-panel .history-controls .history-secondary-actions .icon-btn:first-child{margin-right:0px;border-top-left-radius:3px;border-bottom-left-radius:3px} +.history-panel .history-controls .history-secondary-actions .icon-btn:last-child{margin-left:0px;border-radius:0px 3px 3px 0px} +.history-panel .history-controls .history-secondary-actions .icon-btn:only-child{margin:0px;border-radius:3px} +.history-panel .history-controls .history-secondary-actions .icon-btn{margin-left:2px} .history-panel .history-controls .quota-message{display:none;margin:8px 0px 5px 0px} .history-panel .history-controls .tags-display,.history-panel .history-controls .annotation-display{display:none;margin-bottom:8px}.history-panel .history-controls .tags-display label,.history-panel .history-controls .annotation-display label{display:block;margin:0px;padding:0px;font-weight:normal;color:#555} .history-panel .history-controls .tags-display label:after,.history-panel .history-controls .annotation-display label:after{content:':'} This diff is so big that we needed to truncate the remainder. 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