commit/galaxy-central: carlfeberhard: History & collections: client-side file and class renaming: - li: readonly list item view, - li-edit: editable list item view, - primary identity first (eg. hda-*), - moved hda/hdca into history directory; Fix bug for dataset URLs
1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/588c52157560/ Changeset: 588c52157560 User: carlfeberhard Date: 2014-08-12 15:38:08 Summary: History & collections: client-side file and class renaming: - li: readonly list item view, - li-edit: editable list item view, - primary identity first (eg. hda-*), - moved hda/hdca into history directory; Fix bug for dataset URLs Affected #: 74 files diff -r 2f300953ad98992a620b1f0678eb72505f8cdfc2 -r 588c521575605b698ee15376f3b919f6a2cda20c static/scripts/mvc/collection/collection-li-edit.js --- /dev/null +++ b/static/scripts/mvc/collection/collection-li-edit.js @@ -0,0 +1,33 @@ +define([ + "mvc/collection/collection-li", + "utils/localization" +], function( DC_LI, _l ){ +//============================================================================== +/* + +NOTE: not much going on here. Until we find out what operations a user will need + to perform on their own DC's, we'll leave this empty. + +*/ +//============================================================================== +var _super = DC_LI.DCListItemView; +/** @class Editing view for DatasetCollectionElement. + */ +var DCEListItemEdit = _super.extend({ + + /** logger used to record this.log messages, commonly set to console */ + //logger : console, + + // ......................................................................... misc + /** string rep */ + toString : function(){ + var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); + return 'DCEListItemEdit(' + modelString + ')'; + } +}); + +//============================================================================== + return { + DCEListItemEdit : DCEListItemEdit + }; +}); diff -r 2f300953ad98992a620b1f0678eb72505f8cdfc2 -r 588c521575605b698ee15376f3b919f6a2cda20c static/scripts/mvc/collection/collection-li.js --- /dev/null +++ b/static/scripts/mvc/collection/collection-li.js @@ -0,0 +1,223 @@ +define([ + "mvc/dataset/dataset-li", + "mvc/base-mvc", + "utils/localization" +], function( DATASET_LI, BASE_MVC, _l ){ +/* global Backbone, LoggableMixin */ +//============================================================================== +var ListItemView = BASE_MVC.ListItemView; +/** @class Read only view for DatasetCollection. + */ +var DCListItemView = ListItemView.extend({ +//TODO: may not be needed + + /** logger used to record this.log messages, commonly set to console */ + //logger : console, + + className : ListItemView.prototype.className + " dataset-collection", + /** */ + fxSpeed : 'fast', + +//TODO: ununsed + /** set up */ + initialize : function( attributes ){ + if( attributes.logger ){ this.logger = this.model.logger = attributes.logger; } + this.log( this + '(DCListItemView).initialize:', attributes ); + ListItemView.prototype.initialize.call( this, attributes ); + }, + + /** In this override, don't show`or render any details (no need to do anything here) + * - currently the parent control will load a panel for this collection over itself + * @fires expanded when a body has been expanded + */ + expand : function(){ + var view = this; + return view._fetchModelDetails() + .always(function(){ + view.trigger( 'expanded', view ); + }); + }, + + // ......................................................................... rendering + //TODO:?? possibly move to listItem + /** render a subtitle to show the user what sort of collection this is */ + _renderSubtitle : function(){ + var $subtitle = $( '<div class="subtitle"></div>' ); + //TODO: would be good to get this in the subtitle + //var len = this.model.elements.length; + switch( this.model.get( 'collection_type' ) ){ + case 'list': + return $subtitle.text( _l( 'a list of datasets' ) ); + case 'paired': + return $subtitle.text( _l( 'a pair of datasets' ) ); + case 'list:paired': + return $subtitle.text( _l( 'a list of paired datasets' ) ); + } + return $subtitle; + }, + + // ......................................................................... misc + /** String representation */ + toString : function(){ + var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); + return 'DCListItemView(' + modelString + ')'; + } +}); + +// ............................................................................ TEMPLATES +/** underscore templates */ +DCListItemView.prototype.templates = (function(){ + + // use element identifier + var titleBarTemplate = BASE_MVC.wrapTemplate([ + '<div class="title-bar clear" tabindex="0">', + '<div class="title">', + '<span class="name"><%- collection.element_identifier || collection.name %></span>', + '</div>', + '<div class="subtitle"></div>', + '</div>' + ], 'collection' ); + + return _.extend( {}, ListItemView.prototype.templates, { + titleBar : titleBarTemplate + }); +}()); + + +//============================================================================== +/** @class Read only view for DatasetCollectionElement. + */ +var DCEListItemView = ListItemView.extend({ +//TODO: this might be expendable - compacted with HDAListItemView + + /** logger used to record this.log messages, commonly set to console */ + // comment this out to suppress log output + logger : console, + + /** add the DCE class to the list item */ + className : ListItemView.prototype.className + " dataset-collection-element", + /** jq fx speed for this view */ + fxSpeed : 'fast', + + /** */ + initialize : function( attributes ){ + if( attributes.logger ){ this.logger = this.model.logger = attributes.logger; } + this.log( 'DCEListItemView.initialize:', attributes ); + ListItemView.prototype.initialize.call( this, attributes ); + }, + + // ......................................................................... misc + /** String representation */ + toString : function(){ + var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); + return 'DCEListItemView(' + modelString + ')'; + } +}); + +// ............................................................................ TEMPLATES +/** underscore templates */ +DCEListItemView.prototype.templates = (function(){ + + // use the element identifier here - since that will persist and the user will need it + var titleBarTemplate = BASE_MVC.wrapTemplate([ + '<div class="title-bar clear" tabindex="0">', + '<div class="title">', + '<span class="name"><%- element.element_identifier %></span>', + '</div>', + '<div class="subtitle"></div>', + '</div>' + ], 'element' ); + + return _.extend( {}, ListItemView.prototype.templates, { + titleBar : titleBarTemplate + }); +}()); + + +//============================================================================== +/** @class Read only view for a DatasetCollectionElement that is also an DatasetAssociation + * (a dataset contained in a dataset collection). + */ +var DatasetDCEListItemView = DATASET_LI.DatasetListItemView.extend({ + + className : DATASET_LI.DatasetListItemView.prototype.className + " dataset-collection-element", + + /** logger used to record this.log messages, commonly set to console */ + //logger : console, + + /** */ + initialize : function( attributes ){ + if( attributes.logger ){ this.logger = this.model.logger = attributes.logger; } + this.log( 'DatasetDCEListItemView.initialize:', attributes ); + DATASET_LI.DatasetListItemView.prototype.initialize.call( this, attributes ); + }, + + // ......................................................................... misc + /** String representation */ + toString : function(){ + var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); + return 'DatasetDCEListItemView(' + modelString + ')'; + } +}); + +// ............................................................................ TEMPLATES +/** underscore templates */ +DatasetDCEListItemView.prototype.templates = (function(){ + + // use the element identifier here and not the dataset name + //TODO:?? can we steal the DCE titlebar? + var titleBarTemplate = BASE_MVC.wrapTemplate([ + '<div class="title-bar clear" tabindex="0">', + '<span class="state-icon"></span>', + '<div class="title">', + '<span class="name"><%- element.element_identifier %></span>', + '</div>', + '</div>' + ], 'element' ); + + return _.extend( {}, DATASET_LI.DatasetListItemView.prototype.templates, { + titleBar : titleBarTemplate + }); +}()); + + +//============================================================================== +/** @class Read only view for a DatasetCollectionElement that is also a DatasetCollection + * (a nested DC). + */ +var NestedDCDCEListItemView = DCListItemView.extend({ + + className : DCListItemView.prototype.className + " dataset-collection-element", + + /** logger used to record this.log messages, commonly set to console */ + // comment this out to suppress log output + //logger : console, + + /** In this override, add the state as a class for use with state-based CSS */ + _swapNewRender : function( $newRender ){ + DCListItemView.prototype._swapNewRender.call( this, $newRender ); +//TODO: model currently has no state + var state = this.model.get( 'state' ) || 'ok'; + //if( this.model.has( 'state' ) ){ + this.$el.addClass( 'state-' + state ); + //} + return this.$el; + }, + + // ......................................................................... misc + /** String representation */ + toString : function(){ + var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); + return 'NestedDCDCEListItemView(' + modelString + ')'; + } +}); + + +//============================================================================== + return { + DCListItemView : DCListItemView, + DCEListItemView : DCEListItemView, + DatasetDCEListItemView : DatasetDCEListItemView, + NestedDCDCEListItemView : NestedDCDCEListItemView + }; +}); diff -r 2f300953ad98992a620b1f0678eb72505f8cdfc2 -r 588c521575605b698ee15376f3b919f6a2cda20c static/scripts/mvc/collection/collection-model.js --- a/static/scripts/mvc/collection/collection-model.js +++ b/static/scripts/mvc/collection/collection-model.js @@ -2,7 +2,7 @@ "mvc/dataset/dataset-model", "mvc/base-mvc", "utils/localization" -], function( DATASET, BASE_MVC, _l ){ +], function( DATASET_MODEL, BASE_MVC, _l ){ //============================================================================== /* Notes: @@ -118,13 +118,13 @@ //============================================================================== /** @class Backbone model for a dataset collection element that is a dataset (HDA). */ -var DatasetDCE = DATASET.DatasetAssociation.extend( BASE_MVC.mixin( DatasetCollectionElementMixin, +var DatasetDCE = DATASET_MODEL.DatasetAssociation.extend( BASE_MVC.mixin( DatasetCollectionElementMixin, /** @lends DatasetDCE.prototype */{ /** logger used to record this.log messages, commonly set to console */ //logger : console, - defaults : _.extend( {}, DATASET.DatasetAssociation.prototype.defaults, DatasetCollectionElementMixin.defaults ), + defaults : _.extend( {}, DATASET_MODEL.DatasetAssociation.prototype.defaults, DatasetCollectionElementMixin.defaults ), // because all objects have constructors (as this hashmap would even if this next line wasn't present) // the constructor in hcontentMixin won't be attached by BASE_MVC.mixin to this model @@ -132,7 +132,7 @@ /** call the mixin constructor */ constructor : function( attributes, options ){ this.debug( '\t DatasetDCE.constructor:', attributes, options ); - //DATASET.DatasetAssociation.prototype.constructor.call( this, attributes, options ); + //DATASET_MODEL.DatasetAssociation.prototype.constructor.call( this, attributes, options ); DatasetCollectionElementMixin.constructor.call( this, attributes, options ); }, @@ -140,7 +140,7 @@ /** set up */ initialize : function( attributes, options ){ this.debug( this + '(DatasetDCE).initialize:', attributes, options ); - DATASET.DatasetAssociation.prototype.initialize.call( this, attributes, options ); + DATASET_MODEL.DatasetAssociation.prototype.initialize.call( this, attributes, options ); }, /** String representation. */ diff -r 2f300953ad98992a620b1f0678eb72505f8cdfc2 -r 588c521575605b698ee15376f3b919f6a2cda20c static/scripts/mvc/collection/collection-panel.js --- a/static/scripts/mvc/collection/collection-panel.js +++ b/static/scripts/mvc/collection/collection-panel.js @@ -1,20 +1,15 @@ define([ - "mvc/collection/dataset-collection-base", + "mvc/collection/collection-model", + "mvc/collection/collection-li", "mvc/base-mvc", "utils/localization" -], function( DC_BASE, BASE_MVC, _l ){ +], function( DC_MODEL, DC_LI, BASE_MVC, _l ){ /* ============================================================================= TODO: ============================================================================= */ // ============================================================================= /** @class non-editable, read-only View/Controller for a dataset collection. - * @name CollectionPanel - * - * @augments Backbone.View - * @borrows LoggableMixin#logger as #logger - * @borrows LoggableMixin#log as #log - * @constructs */ var CollectionPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( /** @lends CollectionPanel.prototype */{ @@ -29,8 +24,8 @@ /** (in ms) that jquery effects will use */ fxSpeed : 'fast', - DatasetDCEViewClass : DC_BASE.DatasetDCEBaseView, - NestedDCEViewClass : DC_BASE.NestedDCEBaseView, + DatasetDCEViewClass : DC_LI.DatasetDCEListItemView, + NestedDCDCEViewClass : DC_LI.NestedDCDCEListItemView, // ......................................................................... SET UP /** Set up the view, set up storage, bind listeners to HistoryContents events @@ -44,9 +39,13 @@ } this.log( this + '.initialize:', attributes ); + this.linkTarget = attributes.linkTarget || '_blank'; + this.hasUser = attributes.hasUser; this.panelStack = []; this.parentName = attributes.parentName; + + //window.collectionPanel = this; }, /** create any event listeners for the panel @@ -70,7 +69,7 @@ }, // ------------------------------------------------------------------------ panel rendering - /** Render urls, historyPanel body, and hdas (if any are shown) + /** Render panel * @fires: rendered when the panel is attached and fully visible * @see Backbone.View#render */ @@ -122,7 +121,7 @@ return this; }, - /** render with history data + /** render with collection data * @returns {jQuery} dom fragment as temporary container to be swapped out later */ renderModel : function( ){ @@ -139,7 +138,7 @@ return $newRender; }, - /** Set up HistoryPanel js/widget behaviours */ + /** Set up js/widget behaviours */ _setUpBehaviours : function( $where ){ //TODO: these should be either sub-MVs, or handled by events $where = $where || this.$el; @@ -152,16 +151,16 @@ $container : function(){ return ( this.findContainerFn )?( this.findContainerFn.call( this ) ):( this.$el.parent() ); }, - /** where hdaViews are attached */ + /** where list content views are attached */ $datasetsList : function( $where ){ return ( $where || this.$el ).find( '.datasets-list' ); }, // ------------------------------------------------------------------------ sub-views - /** 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 + /** Set up/render a view for each DCE to be shown, init with model and listeners. + * DCE views are cached to the map this.contentViews (using the model.id as key). + * @param {jQuery} $whereTo what dom element to prepend the DCE views to + * @returns the number of visible DCE views */ renderContents : function( $whereTo ){ //this.debug( 'renderContents, elements:', this.model.elements ); @@ -172,7 +171,7 @@ contentViews = {}, //NOTE: no filtering here visibleContents = this.model.getVisibleContents(); - this.log( 'renderContents, visibleContents:', visibleContents, $whereTo ); + //this.debug( 'renderContents, visibleContents:', visibleContents, $whereTo ); this.$datasetsList( $whereTo ).empty(); if( visibleContents && visibleContents.length ){ @@ -192,8 +191,8 @@ //this.debug( 'content json:', JSON.stringify( content, null, ' ' ) ); var contentView = null, ContentClass = this._getContentClass( content ); - this.debug( 'ContentClass:', ContentClass ); - this.debug( 'content:', content ); + //this.debug( 'content:', content ); + //this.debug( 'ContentClass:', ContentClass ); contentView = new ContentClass({ model : content, linkTarget : this.linkTarget, @@ -201,20 +200,20 @@ hasUser : this.hasUser, logger : this.logger }); - this.debug( 'contentView:', contentView ); + //this.debug( 'contentView:', contentView ); this._setUpContentListeners( contentView ); return contentView; }, /** */ _getContentClass : function( content ){ - this.debug( this + '._getContentClass:', content ); - this.debug( 'DCEViewClass:', this.DCEViewClass ); + //this.debug( this + '._getContentClass:', content ); +//TODO: subclasses use DCEViewClass - but are currently unused - decide switch( content.get( 'element_type' ) ){ case 'hda': - return this.DCEViewClass; + return this.DatasetDCEViewClass; case 'dataset_collection': - return this.DCEViewClass; + return this.NestedDCDCEViewClass; } throw new TypeError( 'Unknown element type:', content.get( 'element_type' ) ); }, @@ -232,14 +231,16 @@ /** */ _addCollectionPanel : function( collectionView ){ +//TODO: a bit hackish var currPanel = this, collectionModel = collectionView.model; - this.debug( 'collection panel (stack), collectionView:', collectionView ); - this.debug( 'collection panel (stack), collectionModel:', collectionModel ); + //this.debug( 'collection panel (stack), collectionView:', collectionView ); + //this.debug( 'collection panel (stack), collectionModel:', collectionModel ); var panel = new PairCollectionPanel({ model : collectionModel, - parentName : this.model.get( 'name' ) + parentName : this.model.get( 'name' ), + linkTarget : this.linkTarget }); currPanel.panelStack.push( panel ); @@ -277,7 +278,7 @@ 'click .navigation .back' : 'close' }, - /** */ + /** close/remove this collection panel */ close : function( event ){ this.$el.remove(); this.trigger( 'close' ); @@ -334,7 +335,8 @@ /** @class non-editable, read-only View/Controller for a dataset collection. */ var ListCollectionPanel = CollectionPanel.extend({ - DCEViewClass : DC_BASE.DatasetDCEBaseView, + //TODO: not strictly needed - due to switch in CollectionPanel._getContentClass + DatasetDCEViewClass : DC_LI.DatasetDCEListItemView, // ........................................................................ misc /** string rep */ @@ -360,7 +362,8 @@ /** @class non-editable, read-only View/Controller for a dataset collection. */ var ListOfPairsCollectionPanel = CollectionPanel.extend({ - DCEViewClass : DC_BASE.NestedDCDCEBaseView, + //TODO: not strictly needed - due to switch in CollectionPanel._getContentClass + NestedDCDCEViewClass : DC_LI.NestedDCDCEListItemView, // ........................................................................ misc /** string rep */ diff -r 2f300953ad98992a620b1f0678eb72505f8cdfc2 -r 588c521575605b698ee15376f3b919f6a2cda20c static/scripts/mvc/collection/dataset-collection-base.js --- a/static/scripts/mvc/collection/dataset-collection-base.js +++ /dev/null @@ -1,222 +0,0 @@ -define([ - "mvc/dataset/dataset-list-element", - "mvc/base-mvc", - "utils/localization" -], function( DATASET_LI, BASE_MVC, _l ){ -/* global Backbone, LoggableMixin */ -//============================================================================== -var ListItemView = BASE_MVC.ListItemView; -/** @class Read only view for DatasetCollection. - */ -var DCBaseView = ListItemView.extend({ -//TODO: may not be needed - - /** logger used to record this.log messages, commonly set to console */ - //logger : console, - - className : ListItemView.prototype.className + " dataset-collection", - /** */ - fxSpeed : 'fast', - -//TODO: ununsed - /** set up */ - initialize : function( attributes ){ - if( attributes.logger ){ this.logger = this.model.logger = attributes.logger; } - this.log( 'DCBaseView.initialize:', attributes ); - ListItemView.prototype.initialize.call( this, attributes ); - }, - - /** In this override, don't show`or render any details (no need to do anything here) - * - currently the parent control will load a panel for this collection over itself - * @fires expanded when a body has been expanded - */ - expand : function(){ - var view = this; - return view._fetchModelDetails() - .always(function(){ - view.trigger( 'expanded', view ); - }); - }, - - // ......................................................................... rendering - //TODO:?? possibly move to listItem - /** render a subtitle to show the user what sort of collection this is */ - _renderSubtitle : function(){ - var $subtitle = $( '<div class="subtitle"></div>' ); - //TODO: would be good to get this in the subtitle - //var len = this.model.elements.length; - switch( this.model.get( 'collection_type' ) ){ - case 'list': - return $subtitle.text( _l( 'a list of datasets' ) ); - case 'paired': - return $subtitle.text( _l( 'a pair of datasets' ) ); - case 'list:paired': - return $subtitle.text( _l( 'a list of paired datasets' ) ); - } - return $subtitle; - }, - - // ......................................................................... misc - /** String representation */ - toString : function(){ - var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); - return 'DCBaseView(' + modelString + ')'; - } -}); - -// ............................................................................ TEMPLATES -/** underscore templates */ -DCBaseView.prototype.templates = (function(){ - - // use element identifier - var titleBarTemplate = BASE_MVC.wrapTemplate([ - '<div class="title-bar clear" tabindex="0">', - '<div class="title">', - '<span class="name"><%- collection.element_identifier || collection.name %></span>', - '</div>', - '<div class="subtitle"></div>', - '</div>' - ], 'collection' ); - - return _.extend( {}, ListItemView.prototype.templates, { - titleBar : titleBarTemplate - }); -}()); - - -//============================================================================== -/** @class Read only view for DatasetCollectionElement. - */ -var DCEBaseView = ListItemView.extend({ -//TODO: this might be expendable - compacted with HDADCEBaseView - - /** logger used to record this.log messages, commonly set to console */ - // comment this out to suppress log output - logger : console, - - /** add the DCE class to the list item */ - className : ListItemView.prototype.className + " dataset-collection-element", - /** jq fx speed for this view */ - fxSpeed : 'fast', - - /** */ - initialize : function( attributes ){ - if( attributes.logger ){ this.logger = this.model.logger = attributes.logger; } - this.log( 'DCEBaseView.initialize:', attributes ); - ListItemView.prototype.initialize.call( this, attributes ); - }, - - // ......................................................................... misc - /** String representation */ - toString : function(){ - var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); - return 'DCEBaseView(' + modelString + ')'; - } -}); - -// ............................................................................ TEMPLATES -/** underscore templates */ -DCEBaseView.prototype.templates = (function(){ - - // use the element identifier here - since that will persist and the user will need it - var titleBarTemplate = BASE_MVC.wrapTemplate([ - '<div class="title-bar clear" tabindex="0">', - '<div class="title">', - '<span class="name"><%- element.element_identifier %></span>', - '</div>', - '<div class="subtitle"></div>', - '</div>' - ], 'element' ); - - return _.extend( {}, ListItemView.prototype.templates, { - titleBar : titleBarTemplate - }); -}()); - - -//============================================================================== -/** @class Read only view for a DatasetCollectionElement that is also an HDA. - */ -var DatasetDCEBaseView = DATASET_LI.DatasetListItemView.extend({ - - className : DATASET_LI.DatasetListItemView.prototype.className + " dataset-collection-element", - - /** logger used to record this.log messages, commonly set to console */ - // comment this out to suppress log output - //logger : console, - - /** */ - initialize : function( attributes ){ - if( attributes.logger ){ this.logger = this.model.logger = attributes.logger; } - this.log( 'DatasetDCEBaseView.initialize:', attributes ); - DATASET_LI.DatasetListItemView.prototype.initialize.call( this, attributes ); - }, - - // ......................................................................... misc - /** String representation */ - toString : function(){ - var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); - return 'DatasetDCEBaseView(' + modelString + ')'; - } -}); - -// ............................................................................ TEMPLATES -/** underscore templates */ -DatasetDCEBaseView.prototype.templates = (function(){ - - // use the element identifier here and not the dataset name - //TODO:?? can we steal the DCE titlebar? - var titleBarTemplate = BASE_MVC.wrapTemplate([ - '<div class="title-bar clear" tabindex="0">', - '<span class="state-icon"></span>', - '<div class="title">', - '<span class="name"><%- element.element_identifier %></span>', - '</div>', - '</div>' - ], 'element' ); - - return _.extend( {}, DATASET_LI.DatasetListItemView.prototype.templates, { - titleBar : titleBarTemplate - }); -}()); - -//============================================================================== -/** @class Read only view for a DatasetCollectionElement that is also a DatasetCollection - * (a nested DC). - */ -var NestedDCDCEBaseView = DCBaseView.extend({ - - className : DCBaseView.prototype.className + " dataset-collection-element", - - /** logger used to record this.log messages, commonly set to console */ - // comment this out to suppress log output - //logger : console, - - /** In this override, add the state as a class for use with state-based CSS */ - _swapNewRender : function( $newRender ){ - DATASET_LI.DatasetListItemView.prototype._swapNewRender.call( this, $newRender ); -//TODO: model currently has no state - var state = this.model.get( 'state' ) || 'ok'; - //if( this.model.has( 'state' ) ){ - this.$el.addClass( 'state-' + state ); - //} - return this.$el; - }, - - // ......................................................................... misc - /** String representation */ - toString : function(){ - var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); - return 'NestedDCDCEBaseView(' + modelString + ')'; - } -}); - - -//============================================================================== - return { - DCBaseView : DCBaseView, - DCEBaseView : DCEBaseView, - DatasetDCEBaseView : DatasetDCEBaseView, - NestedDCDCEBaseView : NestedDCDCEBaseView - }; -}); diff -r 2f300953ad98992a620b1f0678eb72505f8cdfc2 -r 588c521575605b698ee15376f3b919f6a2cda20c static/scripts/mvc/collection/dataset-collection-edit.js --- a/static/scripts/mvc/collection/dataset-collection-edit.js +++ /dev/null @@ -1,39 +0,0 @@ -define([ - "mvc/dataset/states", - "mvc/collection/dataset-collection-base", - "utils/localization" -], function( STATES, DC_BASE_VIEW, _l ){ -//============================================================================== -/* - -NOTE: not much going on here. Until we find out what operations a user will need - to perform on their own DC's, we'll leave this empty. - -*/ -//============================================================================== -var _super = DC_BASE_VIEW.DCBaseView; -/** @class Editing view for DatasetCollection. - * @name DatasetCollectionEditView - * - * @augments DCBaseView - * @constructs - */ -var DCEditView = _super.extend({ - - /** logger used to record this.log messages, commonly set to console */ - // comment this out to suppress log output - //logger : console, - - // ......................................................................... misc - /** string rep */ - toString : function(){ - var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); - return 'DCEditView(' + modelString + ')'; - } -}); - -//============================================================================== - return { - DCEditView : DCEditView - }; -}); diff -r 2f300953ad98992a620b1f0678eb72505f8cdfc2 -r 588c521575605b698ee15376f3b919f6a2cda20c static/scripts/mvc/collection/hdca-base.js --- a/static/scripts/mvc/collection/hdca-base.js +++ /dev/null @@ -1,67 +0,0 @@ -define([ - "mvc/dataset/states", - "mvc/collection/dataset-collection-base", - "mvc/base-mvc", - "utils/localization" -], function( STATES, DC_BASE, BASE_MVC, _l ){ -/* global Backbone */ -//============================================================================== -var _super = DC_BASE.DCBaseView; -/** @class Read only view for HistoryDatasetCollectionAssociation (a dataset collection inside a history). - */ -var HDCABaseView = _super.extend({ - - /** logger used to record this.log messages, commonly set to console */ - //logger : console, - -//TODO: not a dataset - /** */ - className : _super.prototype.className + " history-content", - - /** In this override, add the state as a class for use with state-based CSS */ - _swapNewRender : function( $newRender ){ - _super.prototype._swapNewRender.call( this, $newRender ); -//TODO: model currently has no state - var state = this.model.get( 'state' ) || STATES.OK; - //if( this.model.has( 'state' ) ){ - this.$el.addClass( 'state-' + state ); - //} - return this.$el; - }, - - // ......................................................................... misc - /** String representation */ - toString : function(){ - var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); - return 'HDCABaseView(' + modelString + ')'; - } -}); - -/** underscore templates */ -HDCABaseView.prototype.templates = (function(){ - -// could steal this from hda-base (or use mixed content) - var titleBarTemplate = BASE_MVC.wrapTemplate([ - // adding the hid display to the title - '<div class="title-bar clear" tabindex="0">', - '<span class="state-icon"></span>', - '<div class="title">', - //TODO: remove whitespace and use margin-right - '<span class="hid"><%- collection.hid %></span> ', - '<span class="name"><%- collection.name %></span>', - '</div>', - '<div class="subtitle"></div>', - '</div>' - ], 'collection' ); - - return _.extend( {}, _super.prototype.templates, { - titleBar : titleBarTemplate - }); -}()); - - -//============================================================================== - return { - HDCABaseView : HDCABaseView - }; -}); diff -r 2f300953ad98992a620b1f0678eb72505f8cdfc2 -r 588c521575605b698ee15376f3b919f6a2cda20c static/scripts/mvc/collection/hdca-edit.js --- a/static/scripts/mvc/collection/hdca-edit.js +++ /dev/null @@ -1,55 +0,0 @@ -define([ - "mvc/dataset/states", - "mvc/collection/hdca-base", - "utils/localization" -], function( STATES, HDCA_BASE, _l ){ -//============================================================================== -var _super = HDCA_BASE.HDCABaseView; -/** @class Editing view for HistoryDatasetCollectionAssociation. - */ -var HDCAEditView = _super.extend({ - - /** logger used to record this.log messages, commonly set to console */ - //logger : console, - - // ......................................................................... delete - /** In this override, add the delete button. */ - _renderPrimaryActions : function(){ - this.log( this + '._renderPrimaryActions' ); - // render the display, edit attr and delete icon-buttons - return _super.prototype._renderPrimaryActions.call( this ) - .concat([ - this._renderDeleteButton() - ]); - }, - - /** Render icon-button to delete this collection. */ - _renderDeleteButton : function(){ - var self = this, - deleted = this.model.get( 'deleted' ); - return faIconButton({ - title : deleted? _l( 'Dataset collection is already deleted' ): _l( 'Delete' ), - classes : 'delete-btn', - faIcon : 'fa-times', - disabled : deleted, - onclick : function() { - // ...bler... tooltips being left behind in DOM (hover out never called on deletion) - self.$el.find( '.icon-btn.delete-btn' ).trigger( 'mouseout' ); - self.model[ 'delete' ](); - } - }); - }, - - // ......................................................................... misc - /** string rep */ - toString : function(){ - var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); - return 'HDCAEditView(' + modelString + ')'; - } -}); - -//============================================================================== - return { - HDCAEditView : HDCAEditView - }; -}); diff -r 2f300953ad98992a620b1f0678eb72505f8cdfc2 -r 588c521575605b698ee15376f3b919f6a2cda20c static/scripts/mvc/collection/hdca-model.js --- a/static/scripts/mvc/collection/hdca-model.js +++ /dev/null @@ -1,83 +0,0 @@ -define([ - "mvc/history/history-content-base", - "mvc/collection/collection-model", - "utils/localization" -], function( HISTORY_CONTENT, DC_MODEL, _l ){ -/*============================================================================== - -Models for DatasetCollections contained within a history. - -TODO: - these might be compactable to one class if some duplication with - collection-model is used. - -==============================================================================*/ -var hcontentMixin = HISTORY_CONTENT.HistoryContentMixin, - ListDC = DC_MODEL.ListDatasetCollection, - PairDC = DC_MODEL.PairDatasetCollection, - ListPairedDC = DC_MODEL.ListPairedDatasetCollection; - -//============================================================================== -/** @class Backbone model for List Dataset Collection within a History. - * @constructs - */ -var HistoryListDatasetCollection = ListDC.extend( hcontentMixin ).extend( -/** @lends HistoryListDatasetCollection.prototype */{ - - initialize : function( model, options ){ - ListDC.prototype.initialize.call( this, model, options ); - hcontentMixin.initialize.call( this, model, options ); - }, - - /** String representation. */ - toString : function(){ - return ([ 'HistoryListDatasetCollection(', this.get( 'name' ), ')' ].join( '' )); - } -}); - - -//============================================================================== -/** @class Backbone model for Pair Dataset Collection within a History. - * @constructs - */ -var HistoryPairDatasetCollection = PairDC.extend( hcontentMixin ).extend( -/** @lends HistoryPairDatasetCollection.prototype */{ - - initialize : function( model, options ){ - PairDC.prototype.initialize.call( this, model, options ); - hcontentMixin.initialize.call( this, model, options ); - }, - - /** String representation. */ - toString : function(){ - return ([ 'HistoryPairDatasetCollection(', this.get( 'name' ), ')' ].join( '' )); - } -}); - - -//============================================================================== -/** @class Backbone model for List of Pairs Dataset Collection within a History. - * @constructs - */ -var HistoryListPairedDatasetCollection = ListPairedDC.extend( hcontentMixin ).extend( -/** @lends HistoryListPairedDatasetCollection.prototype */{ - - initialize : function( model, options ){ - ListPairedDC.prototype.initialize.call( this, model, options ); - hcontentMixin.initialize.call( this, model, options ); - }, - - /** String representation. */ - toString : function(){ - return ([ 'HistoryListPairedDatasetCollection(', this.get( 'name' ), ')' ].join( '' )); - } -}); - - -//============================================================================== - return { - HistoryListDatasetCollection : HistoryListDatasetCollection, - HistoryPairDatasetCollection : HistoryPairDatasetCollection, - HistoryListPairedDatasetCollection : HistoryListPairedDatasetCollection - }; -}); diff -r 2f300953ad98992a620b1f0678eb72505f8cdfc2 -r 588c521575605b698ee15376f3b919f6a2cda20c static/scripts/mvc/dataset/dataset-li-edit.js --- /dev/null +++ b/static/scripts/mvc/dataset/dataset-li-edit.js @@ -0,0 +1,351 @@ +define([ + "mvc/dataset/states", + "mvc/dataset/dataset-li", + "mvc/tags", + "mvc/annotations", + "mvc/base-mvc", + "utils/localization" +], function( STATES, DATASET_LI, TAGS, ANNOTATIONS, BASE_MVC, _l ){ +//============================================================================== +var _super = DATASET_LI.DatasetListItemView; +/** @class Editing view for DatasetAssociation. + */ +var DatasetListItemEdit = _super.extend( +/** @lends DatasetListItemEdit.prototype */{ + + /** logger used to record this.log messages, commonly set to console */ + //logger : console, + + /** set up: options */ + initialize : function( attributes ){ + _super.prototype.initialize.call( this, attributes ); +//TODO: shouldn't this err if false? + this.hasUser = attributes.hasUser; + + /** allow user purge of dataset files? */ + this.purgeAllowed = attributes.purgeAllowed || false; + + //TODO: move to HiddenUntilActivatedViewMixin + /** should the tags editor be shown or hidden initially? */ + this.tagsEditorShown = attributes.tagsEditorShown || false; + /** should the tags editor be shown or hidden initially? */ + this.annotationEditorShown = attributes.annotationEditorShown || false; + }, + + // ......................................................................... titlebar actions + /** In this override, add the other two primary actions: edit and delete */ + _renderPrimaryActions : function(){ + var actions = _super.prototype._renderPrimaryActions.call( this ); + if( this.model.get( 'state' ) === STATES.NOT_VIEWABLE ){ + return actions; + } + // render the display, edit attr and delete icon-buttons + return _super.prototype._renderPrimaryActions.call( this ).concat([ + this._renderEditButton(), + this._renderDeleteButton() + ]); + }, + +//TODO: move titleButtons into state renderers, remove state checks in the buttons + + /** Render icon-button to edit the attributes (format, permissions, etc.) this dataset. */ + _renderEditButton : function(){ + // don't show edit while uploading, in-accessible + // DO show if in error (ala previous history panel) + if( ( this.model.get( 'state' ) === STATES.DISCARDED ) + || ( !this.model.get( 'accessible' ) ) ){ + return null; + } + + var purged = this.model.get( 'purged' ), + deleted = this.model.get( 'deleted' ), + editBtnData = { + title : _l( 'Edit attributes' ), + href : this.model.urls.edit, + target : this.linkTarget, + faIcon : 'fa-pencil', + classes : 'edit-btn' + }; + + // disable if purged or deleted and explain why in the tooltip + if( deleted || purged ){ + editBtnData.disabled = true; + if( purged ){ + editBtnData.title = _l( 'Cannot edit attributes of datasets removed from disk' ); + } else if( deleted ){ + editBtnData.title = _l( 'Undelete dataset to edit attributes' ); + } + + // disable if still uploading or new + } else if( _.contains( [ STATES.UPLOAD, STATES.NEW ], this.model.get( 'state' ) ) ){ + editBtnData.disabled = true; + editBtnData.title = _l( 'This dataset is not yet editable' ); + } + return faIconButton( editBtnData ); + }, + + /** Render icon-button to delete this hda. */ + _renderDeleteButton : function(){ + // don't show delete if... + if( ( !this.model.get( 'accessible' ) ) ){ + return null; + } + + var self = this, + deletedAlready = this.model.isDeletedOrPurged(); + return faIconButton({ + title : !deletedAlready? _l( 'Delete' ) : _l( 'Dataset is already deleted' ), + disabled : deletedAlready, + faIcon : 'fa-times', + classes : 'delete-btn', + onclick : function() { + // ...bler... tooltips being left behind in DOM (hover out never called on deletion) + self.$el.find( '.icon-btn.delete-btn' ).trigger( 'mouseout' ); + self.model[ 'delete' ](); + } + }); + }, + + // ......................................................................... details + /** In this override, add tags and annotations controls, make the ? dbkey a link to editing page */ + _renderDetails : function(){ + //TODO: generalize to be allow different details for each state + var $details = _super.prototype._renderDetails.call( this ), + state = this.model.get( 'state' ); + + if( !this.model.isDeletedOrPurged() && _.contains([ STATES.OK, STATES.FAILED_METADATA ], state ) ){ + this._renderTags( $details ); + this._renderAnnotation( $details ); + this._makeDbkeyEditLink( $details ); + } + +//TODO: TRIPLE tap, ugh. + this._setUpBehaviors( $details ); + return $details; + }, + + /** Add less commonly used actions in the details section based on state */ + _renderSecondaryActions : function(){ + var actions = _super.prototype._renderSecondaryActions.call( this ); + switch( this.model.get( 'state' ) ){ + case STATES.UPLOAD: + case STATES.NEW: + case STATES.NOT_VIEWABLE: + return actions; + case STATES.ERROR: + // error button comes first + actions.unshift( this._renderErrButton() ); + return actions.concat([ this._renderRerunButton() ]); + case STATES.OK: + case STATES.FAILED_METADATA: + return actions.concat([ this._renderRerunButton(), this._renderVisualizationsButton() ]); + } + return actions.concat([ this._renderRerunButton() ]); + }, + + /** Render icon-button to report an error on this dataset to the galaxy admin. */ + _renderErrButton : function(){ + return faIconButton({ + title : _l( 'View or report this error' ), + href : this.model.urls.report_error, + classes : 'report-error-btn', + target : this.linkTarget, + faIcon : 'fa-bug' + }); + }, + + /** Render icon-button to re-run the job that created this dataset. */ + _renderRerunButton : function(){ + return faIconButton({ + title : _l( 'Run this job again' ), + href : this.model.urls.rerun, + classes : 'rerun-btn', + target : this.linkTarget, + faIcon : 'fa-refresh' + }); + }, + + /** Render an icon-button or popupmenu of links based on the applicable visualizations */ + _renderVisualizationsButton : function(){ + //TODO: someday - lazyload visualizations + var visualizations = this.model.get( 'visualizations' ); + if( ( this.model.isDeletedOrPurged() ) + || ( !this.hasUser ) + || ( !this.model.hasData() ) + || ( _.isEmpty( visualizations ) ) ){ + return null; + } + if( !_.isObject( visualizations[0] ) ){ + this.warn( 'Visualizations have been switched off' ); + return null; + } + + var $visualizations = $( this.templates.visualizations( visualizations, this ) ); + // use addBack here to include the root $visualizations elem (for the case of 1 visualization) + this._addScratchBookFn( $visualizations.find( '.visualization-link' ).addBack( '.visualization-link' ) ); + return $visualizations; + }, + + /** add scratchbook functionality to visualization links */ + _addScratchBookFn : function( $links ){ + $links.click( function( ev ){ + if( Galaxy.frame && Galaxy.frame.active ){ + Galaxy.frame.add({ + title : "Visualization", + type : "url", + content : $( this ).attr( 'href' ) + }); + ev.preventDefault(); + ev.stopPropagation(); + } + }); + }, + +//TODO: if possible move these to readonly view - but display the owner's tags/annotation (no edit) + /** Render the tags list/control */ + _renderTags : function( $where ){ + if( !this.hasUser ){ return; } + var view = this; + this.tagsEditor = new TAGS.TagsEditor({ + model : this.model, + el : $where.find( '.tags-display' ), + onshowFirstTime : function(){ this.render(); }, + // persist state on the hda view (and not the editor) since these are currently re-created each time + onshow : function(){ view.tagsEditorShown = true; }, + onhide : function(){ view.tagsEditorShown = false; }, + $activator : faIconButton({ + title : _l( 'Edit dataset tags' ), + classes : 'tag-btn', + faIcon : 'fa-tags' + }).appendTo( $where.find( '.actions .right' ) ) + }); + if( this.tagsEditorShown ){ this.tagsEditor.toggle( true ); } + }, + + /** Render the annotation display/control */ + _renderAnnotation : function( $where ){ + if( !this.hasUser ){ return; } + var view = this; + this.annotationEditor = new ANNOTATIONS.AnnotationEditor({ + model : this.model, + el : $where.find( '.annotation-display' ), + onshowFirstTime : function(){ this.render(); }, + // persist state on the hda view (and not the editor) since these are currently re-created each time + onshow : function(){ view.annotationEditorShown = true; }, + onhide : function(){ view.annotationEditorShown = false; }, + $activator : faIconButton({ + title : _l( 'Edit dataset annotation' ), + classes : 'annotate-btn', + faIcon : 'fa-comment' + }).appendTo( $where.find( '.actions .right' ) ) + }); + if( this.annotationEditorShown ){ this.annotationEditor.toggle( true ); } + }, + + /** If the format/dbkey/genome_build isn't set, make the display a link to the edit page */ + _makeDbkeyEditLink : function( $details ){ + // make the dbkey a link to editing + if( this.model.get( 'metadata_dbkey' ) === '?' + && !this.model.isDeletedOrPurged() ){ + var editableDbkey = $( '<a class="value">?</a>' ) + .attr( 'href', this.model.urls.edit ) + .attr( 'target', this.linkTarget ); + $details.find( '.dbkey .value' ).replaceWith( editableDbkey ); + } + }, + + // ......................................................................... events + /** event map */ + events : _.extend( _.clone( _super.prototype.events ), { + 'click .undelete-link' : function( ev ){ this.model.undelete(); return false; }, + 'click .purge-link' : '_confirmPurge' + }), + + /** listener for item purge */ + _confirmPurge : function _confirmPurge( ev ){ +//TODO: confirm dialog + this.model.purge(); + return false; + }, + + // ......................................................................... misc + /** string rep */ + toString : function(){ + var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); + return 'HDAEditView(' + modelString + ')'; + } +}); + + +// ............................................................................ TEMPLATES +/** underscore templates */ +DatasetListItemEdit.prototype.templates = (function(){ +//TODO: move to require text! plugin + + var warnings = _.extend( {}, _super.prototype.templates.warnings, { + failed_metadata : BASE_MVC.wrapTemplate([ + // in this override, provide a link to the edit page + '<% if( dataset.state === "failed_metadata" ){ %>', + '<div class="failed_metadata-warning warningmessagesmall">', + _l( 'An error occurred setting the metadata for this dataset' ), + '<br /><a href="<%= dataset.urls.edit %>" target="<%= view.linkTarget %>">', + _l( 'Set it manually or retry auto-detection' ), + '</a>', + '</div>', + '<% } %>' + ], 'dataset' ), + + deleted : BASE_MVC.wrapTemplate([ + // in this override, provide links to undelete or purge the dataset + '<% if( dataset.deleted && !dataset.purged ){ %>', + // deleted not purged + '<div class="deleted-msg warningmessagesmall">', + _l( 'This dataset has been deleted' ), + '<br /><a class="undelete-link" href="javascript:void(0);">', _l( 'Undelete it' ), '</a>', + '<% if( view.purgeAllowed ){ %>', + '<br /><a class="purge-link" href="javascript:void(0);">', + _l( 'Permanently remove it from disk' ), + '</a>', + '<% } %>', + '</div>', + '<% } %>' + ], 'dataset' ) + }); + + var visualizationsTemplate = BASE_MVC.wrapTemplate([ + '<% if( visualizations.length === 1 ){ %>', + '<a class="visualization-btn visualization-link icon-btn" href="<%= visualizations[0].href %>"', + ' target="<%= visualizations[0].target %>" title="', _l( 'Visualize in' ), + ' <%= visualizations[0].html %>">', + '<span class="fa fa-bar-chart-o"></span>', + '</a>', + + '<% } else { %>', + '<div class="visualizations-dropdown dropdown">', + '<a class="visualization-btn icon-btn" data-toggle="dropdown" title="', _l( 'Visualize' ), '">', + '<span class="fa fa-bar-chart-o"></span>', + '</a>', + '<ul class="dropdown-menu" role="menu">', + '<% _.each( visualizations, function( visualization ){ %>', + '<li><a class="visualization-link" href="<%= visualization.href %>"', + ' target="<%= visualization.target %>">', + '<%= visualization.html %>', + '</a></li>', + '<% }); %>', + '</ul>', + '</div>', + '<% } %>' + ], 'visualizations' ); + + return _.extend( {}, _super.prototype.templates, { + warnings : warnings, + visualizations : visualizationsTemplate + }); +}()); + + +//============================================================================== + return { + DatasetListItemEdit : DatasetListItemEdit + }; +}); diff -r 2f300953ad98992a620b1f0678eb72505f8cdfc2 -r 588c521575605b698ee15376f3b919f6a2cda20c static/scripts/mvc/dataset/dataset-li.js --- /dev/null +++ b/static/scripts/mvc/dataset/dataset-li.js @@ -0,0 +1,464 @@ +define([ + "mvc/dataset/states", + "mvc/base-mvc", + "utils/localization" +], function( STATES, BASE_MVC, _l ){ +/* global Backbone */ +//============================================================================== +var _super = BASE_MVC.ListItemView; +/** @class Read only list view for either LDDAs, HDAs, or HDADCEs. + * Roughly, any DatasetInstance (and not a raw Dataset). + */ +var DatasetListItemView = _super.extend( +/** @lends DatasetListItemView.prototype */{ + + /** logger used to record this.log messages, commonly set to console */ + //logger : console, + + className : _super.prototype.className + " dataset", + + /** Set up: instance vars, options, and event handlers */ + initialize : function( attributes ){ + if( attributes.logger ){ this.logger = this.model.logger = attributes.logger; } + this.log( this + '.initialize:', attributes ); + _super.prototype.initialize.call( this, attributes ); + + /** where should pages from links be displayed? (default to new tab/window) */ + this.linkTarget = attributes.linkTarget || '_blank'; + + this._setUpListeners(); + }, + + /** event listeners */ + _setUpListeners : function(){ + // hide the primary actions in the title bar when selectable + this.on( 'selectable', function( isSelectable ){ + if( isSelectable ){ + this.$( '.primary-actions' ).hide(); + } else { + this.$( '.primary-actions' ).show(); + } + }); + + // re-rendering on any model changes + this.model.on( 'change', function( model, options ){ + // if the model moved into the ready state and is expanded without details, fetch those details now + if( this.model.changedAttributes().state && this.model.inReadyState() + && this.expanded && !this.model.hasDetails() ){ + // will render automatically (due to fetch -> change) + this.model.fetch(); + + } else { + this.render(); + } + }, this ); + + this.on( 'all', function( event ){ + this.log( event ); + }, this ); + }, + + // ......................................................................... expandable + /** In this override, only get details if in the ready state. + * Note: fetch with no 'change' event triggering to prevent automatic rendering. + */ + _fetchModelDetails : function(){ + var view = this; + if( view.model.inReadyState() && !view.model.hasDetails() ){ + return view.model.fetch({ silent: true }); + } + return jQuery.when(); + }, + + // ......................................................................... removal + /** Remove this view's html from the DOM and remove all event listeners. + * @param {Number or String} speed jq effect speed + * @param {Function} callback an optional function called when removal is done (scoped to this view) + */ + remove : function( speed, callback ){ + var view = this; + speed = speed || this.fxSpeed; + this.$el.fadeOut( speed, function(){ + Backbone.View.prototype.remove.call( view ); + if( callback ){ callback.call( view ); } + }); + }, + + // ......................................................................... rendering + /* TODO: + dataset states are the issue primarily making dataset rendering complex + each state should have it's own way of displaying/set of details + often with different actions that can be applied + throw in deleted/purged/visible and things get complicated easily + I've considered (a couple of times) - creating a view for each state + - but recreating the view during an update...seems wrong + */ + /** Render this HDA, set up ui. + * @param {Number or String} speed jq fx speed + * @returns {Object} this + */ + render : function( speed ){ + //HACK: hover exit doesn't seem to be called on prev. tooltips when RE-rendering - so: no tooltip hide + // handle that here by removing previous view's tooltips + //this.$el.find("[title]").tooltip( "destroy" ); + return _super.prototype.render.call( this, speed ); + }, + + /** In this override, add the dataset state as a class for use with state-based CSS */ + _swapNewRender : function( $newRender ){ + _super.prototype._swapNewRender.call( this, $newRender ); + if( this.model.has( 'state' ) ){ + this.$el.addClass( 'state-' + this.model.get( 'state' ) ); + } + return this.$el; + }, + + // ................................................................................ titlebar + /** In this override, add the dataset display button. */ + _renderPrimaryActions : function(){ + // render just the display for read-only + return [ this._renderDisplayButton() ]; + }, + + /** Render icon-button to display dataset data */ + _renderDisplayButton : function(){ +//TODO:?? too complex - possibly move into template + // don't show display if not viewable or not accessible + var state = this.model.get( 'state' ); + if( ( state === STATES.NOT_VIEWABLE ) + || ( state === STATES.DISCARDED ) + || ( !this.model.get( 'accessible' ) ) ){ + return null; + } + + var displayBtnData = { + target : this.linkTarget, + classes : 'display-btn' + }; + + // show a disabled display if the data's been purged + if( this.model.get( 'purged' ) ){ + displayBtnData.disabled = true; + displayBtnData.title = _l( 'Cannot display datasets removed from disk' ); + + // disable if still uploading + } else if( state === STATES.UPLOAD ){ + displayBtnData.disabled = true; + displayBtnData.title = _l( 'This dataset must finish uploading before it can be viewed' ); + + // disable if still new + } else if( state === STATES.NEW ){ + displayBtnData.disabled = true; + displayBtnData.title = _l( 'This dataset is not yet viewable' ); + + } else { + displayBtnData.title = _l( 'View data' ); + + // default link for dataset + displayBtnData.href = this.model.urls.display; + + // add frame manager option onclick event + var self = this; + displayBtnData.onclick = function( ev ){ + if( Galaxy.frame && Galaxy.frame.active ){ + // Create frame with TabularChunkedView. + Galaxy.frame.add({ + title : "Data Viewer: " + self.model.get( 'name' ), + type : "other", + content : function( parent_elt ){ + var new_dataset = new DATA.TabularDataset({ id: self.model.get( 'id' ) }); + $.when( new_dataset.fetch() ).then( function(){ + DATA.createTabularDatasetChunkedView({ + model: new_dataset, + parent_elt: parent_elt, + embedded: true, + height: '100%' + }); + }); + } + }); + ev.preventDefault(); + } + }; + } + displayBtnData.faIcon = 'fa-eye'; + return faIconButton( displayBtnData ); + }, + + // ......................................................................... rendering details + /** Render the enclosing div of the hda body and, if expanded, the html in the body + * @returns {jQuery} rendered DOM + */ + _renderDetails : function(){ + //TODO: generalize to be allow different details for each state + var $details = _super.prototype._renderDetails.call( this ); + $details.find( '.actions .left' ).empty().append( this._renderSecondaryActions() ); + $details.find( '.summary' ).html( this._renderSummary() ); + $details.find( '.display-applications' ).html( this._renderDisplayApplications() ); + +//TODO: double tap + this._setUpBehaviors( $details ); + return $details; + }, + + /** Defer to the appropo summary rendering fn based on state */ + _renderSummary : function(){ + var summaryRenderFn = this.templates.summaries[ this.model.get( 'state' ) ]; + summaryRenderFn = summaryRenderFn || this.templates.summaries.unknown; + return summaryRenderFn( this.model.toJSON(), this ); + }, + + /** Render the external display application links */ + _renderDisplayApplications : function(){ + if( this.model.isDeletedOrPurged() ){ return ''; } + // render both old and new display apps using the same template + return [ + this.templates.displayApplications( this.model.get( 'display_apps' ), this ), + this.templates.displayApplications( this.model.get( 'display_types' ), this ) + ].join( '' ); + }, + + // ......................................................................... secondary/details actions + /** A series of links/buttons for less commonly used actions: re-run, info, etc. */ + _renderSecondaryActions : function(){ + this.debug( '_renderSecondaryActions' ); + switch( this.model.get( 'state' ) ){ + case STATES.NOT_VIEWABLE: + return []; + case STATES.OK: + case STATES.FAILED_METADATA: + case STATES.ERROR: + return [ this._renderDownloadButton(), this._renderShowParamsButton() ]; + } + return [ this._renderShowParamsButton() ]; + }, + + /** Render icon-button to show the input and output (stdout/err) for the job that created this. + * @returns {jQuery} rendered DOM + */ + _renderShowParamsButton : function(){ + // gen. safe to show in all cases + return faIconButton({ + title : _l( 'View details' ), + classes : 'dataset-params-btn', + href : this.model.urls.show_params, + target : this.linkTarget, + faIcon : 'fa-info-circle' + }); + }, + + /** Render icon-button/popupmenu to download the data (and/or the associated meta files (bai, etc.)) for this. + * @returns {jQuery} rendered DOM + */ + _renderDownloadButton : function(){ +//TODO: to (its own) template fn + // don't show anything if the data's been purged + if( this.model.get( 'purged' ) || !this.model.hasData() ){ return null; } + + // return either: a popupmenu with links to download assoc. meta files (if there are meta files) + // or a single download icon-button (if there are no meta files) + if( !_.isEmpty( this.model.get( 'meta_files' ) ) ){ + return this._renderMetaFileDownloadButton(); + } + + return $([ + '<a class="download-btn icon-btn" href="', this.model.urls.download, '" title="' + _l( 'Download' ) + '">', + '<span class="fa fa-floppy-o"></span>', + '</a>' + ].join( '' )); + }, + + /** Render the download button which opens a dropdown with links to download assoc. meta files (indeces, etc.) */ + _renderMetaFileDownloadButton : function(){ + var urls = this.model.urls; + return $([ + '<div class="metafile-dropdown dropdown">', + '<a class="download-btn icon-btn" href="javascript:void(0)" data-toggle="dropdown"', + ' title="' + _l( 'Download' ) + '">', + '<span class="fa fa-floppy-o"></span>', + '</a>', + '<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">', + '<li><a href="' + urls.download + '">', _l( 'Download dataset' ), '</a></li>', + _.map( this.model.get( 'meta_files' ), function( meta_file ){ + return [ + '<li><a href="', urls.meta_download + meta_file.file_type, '">', + _l( 'Download' ), ' ', meta_file.file_type, + '</a></li>' + ].join( '' ); + }).join( '\n' ), + '</ul>', + '</div>' + ].join( '\n' )); + }, + + // ......................................................................... misc + /** String representation */ + toString : function(){ + var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); + return 'DatasetListItemView(' + modelString + ')'; + } +}); + +// ............................................................................ TEMPLATES +/** underscore templates */ +DatasetListItemView.prototype.templates = (function(){ +//TODO: move to require text! plugin + + var warnings = _.extend( {}, _super.prototype.templates.warnings, { + failed_metadata : BASE_MVC.wrapTemplate([ + // failed metadata is rendered as a warning on an otherwise ok dataset view + '<% if( model.state === "failed_metadata" ){ %>', + '<div class="warningmessagesmall">', + _l( 'An error occurred setting the metadata for this dataset' ), + '</div>', + '<% } %>' + ]), + error : BASE_MVC.wrapTemplate([ + // error during index fetch - show error on dataset + '<% if( model.error ){ %>', + '<div class="errormessagesmall">', + _l( 'There was an error getting the data for this dataset' ), ': <%- model.error %>', + '</div>', + '<% } %>' + ]), + purged : BASE_MVC.wrapTemplate([ + '<% if( model.purged ){ %>', + '<div class="purged-msg warningmessagesmall">', + _l( 'This dataset has been deleted and removed from disk' ), + '</div>', + '<% } %>' + ]), + deleted : BASE_MVC.wrapTemplate([ + // deleted not purged + '<% if( model.deleted && !model.purged ){ %>', + '<div class="deleted-msg warningmessagesmall">', + _l( 'This dataset has been deleted' ), + '</div>', + '<% } %>' + ]) + + //NOTE: hidden warning is only needed for HDAs + }); + + var detailsTemplate = BASE_MVC.wrapTemplate([ + '<div class="details">', + '<div class="summary"></div>', + + '<div class="actions clear">', + '<div class="left"></div>', + '<div class="right"></div>', + '</div>', + + // do not display tags, annotation, display apps, or peek when deleted + '<% if( !dataset.deleted && !dataset.purged ){ %>', + '<div class="tags-display"></div>', + '<div class="annotation-display"></div>', + + '<div class="display-applications"></div>', + + '<% if( dataset.peek ){ %>', + '<pre class="dataset-peek"><%= dataset.peek %></pre>', + '<% } %>', + '<% } %>', + '</div>' + ], 'dataset' ); + +//TODO: still toooooooooooooo complex - rework + var summaryTemplates = {}; + summaryTemplates[ STATES.OK ] = summaryTemplates[ STATES.FAILED_METADATA ] = BASE_MVC.wrapTemplate([ + '<% if( dataset.misc_blurb ){ %>', + '<div class="blurb">', + '<span class="value"><%- dataset.misc_blurb %></span>', + '</div>', + '<% } %>', + + '<% if( dataset.data_type ){ %>', + '<div class="datatype">', + '<label class="prompt">', _l( 'format' ), '</label>', + '<span class="value"><%- dataset.data_type %></span>', + '</div>', + '<% } %>', + + '<% if( dataset.metadata_dbkey ){ %>', + '<div class="dbkey">', + '<label class="prompt">', _l( 'database' ), '</label>', + '<span class="value">', + '<%- dataset.metadata_dbkey %>', + '</span>', + '</div>', + '<% } %>', + + '<% if( dataset.misc_info ){ %>', + '<div class="info">', + '<span class="value"><%- dataset.misc_info %></span>', + '</div>', + '<% } %>' + ], 'dataset' ); + summaryTemplates[ STATES.NEW ] = BASE_MVC.wrapTemplate([ + '<div>', _l( 'This is a new dataset and not all of its data are available yet' ), '</div>' + ], 'dataset' ); + summaryTemplates[ STATES.NOT_VIEWABLE ] = BASE_MVC.wrapTemplate([ + '<div>', _l( 'You do not have permission to view this dataset' ), '</div>' + ], 'dataset' ); + summaryTemplates[ STATES.DISCARDED ] = BASE_MVC.wrapTemplate([ + '<div>', _l( 'The job creating this dataset was cancelled before completion' ), '</div>' + ], 'dataset' ); + summaryTemplates[ STATES.QUEUED ] = BASE_MVC.wrapTemplate([ + '<div>', _l( 'This job is waiting to run' ), '</div>' + ], 'dataset' ); + summaryTemplates[ STATES.UPLOAD ] = BASE_MVC.wrapTemplate([ + '<div>', _l( 'This dataset is currently uploading' ), '</div>' + ], 'dataset' ); + summaryTemplates[ STATES.SETTING_METADATA ] = BASE_MVC.wrapTemplate([ + '<div>', _l( 'Metadata is being auto-detected' ), '</div>' + ], 'dataset' ); + summaryTemplates[ STATES.RUNNING ] = BASE_MVC.wrapTemplate([ + '<div>', _l( 'This job is currently running' ), '</div>' + ], 'dataset' ); + summaryTemplates[ STATES.PAUSED ] = BASE_MVC.wrapTemplate([ + '<div>', _l( 'This job is paused. Use the "Resume Paused Jobs" in the history menu to resume' ), '</div>' + ], 'dataset' ); + summaryTemplates[ STATES.ERROR ] = BASE_MVC.wrapTemplate([ + '<% if( !dataset.purged ){ %>', + '<div><%- dataset.misc_blurb %></div>', + '<% } %>', + '<span class="help-text">', _l( 'An error occurred with this dataset' ), ':</span>', + '<div class="job-error-text"><%- dataset.misc_info %></div>' + ], 'dataset' ); + summaryTemplates[ STATES.EMPTY ] = BASE_MVC.wrapTemplate([ + '<div>', _l( 'No data' ), ': <i><%- dataset.misc_blurb %></i></div>' + ], 'dataset' ); + summaryTemplates.unknown = BASE_MVC.wrapTemplate([ + '<div>Error: unknown dataset state: "<%- dataset.state %>"</div>' + ], 'dataset' ); + + // this is applied to both old and new style display apps + var displayApplicationsTemplate = BASE_MVC.wrapTemplate([ + '<% _.each( apps, function( app ){ %>', + '<div class="display-application">', + '<span class="display-application-location"><%- app.label %></span> ', + '<span class="display-application-links">', + '<% _.each( app.links, function( link ){ %>', + '<a target="<%= link.target %>" href="<%= link.href %>">', + '<% print( _l( link.text ) ); %>', + '</a> ', + '<% }); %>', + '</span>', + '</div>', + '<% }); %>' + ], 'apps' ); + + return _.extend( {}, _super.prototype.templates, { + warnings : warnings, + details : detailsTemplate, + summaries : summaryTemplates, + displayApplications : displayApplicationsTemplate + }); +}()); + + +// ============================================================================ + return { + DatasetListItemView : DatasetListItemView + }; +}); 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