diff --git a/run_functional_tests.sh b/run_functional_tests.sh new file mode 120000 index 00000000000..5236e32d82f --- /dev/null +++ b/run_functional_tests.sh @@ -0,0 +1 @@ +run_tests.sh \ No newline at end of file diff --git a/static/scripts/mvc/base-mvc.js b/static/scripts/mvc/base-mvc.js index 267afd7c4cf..83780d880ae 100644 --- a/static/scripts/mvc/base-mvc.js +++ b/static/scripts/mvc/base-mvc.js @@ -42,6 +42,7 @@ var LoggableMixin = /** @lends LoggableMixin# */{ if( this.logger ){ var log = this.logger.log; if( typeof this.logger.log === 'object' ){ +//TODO:! there has to be a way to get the lineno/file into this log = Function.prototype.bind.call( this.logger.log, this.logger ); } return log.apply( this.logger, arguments ); @@ -148,6 +149,23 @@ var SessionStorageModel = Backbone.Model.extend({ }()); +//============================================================================== +/** Function that allows mixing of hashs into bbone MVC while showing the mixins first + * (before the more local class overrides/hash). + * Basically, a simple reversal of param order on _.defaults() - to show mixins in top of definition. + * @example: + * var NewModel = Something.extend( mixin( MyMixinA, MyMixinB, { ... myVars : ... }) ); + * + * NOTE: this does not combine any hashes (like events, etc.) and you're expected to handle that + */ +function mixin( mixinHash1, /* mixinHash2, etc: ... variadic */ propsHash ){ + var args = Array.prototype.slice.call( arguments, 0 ), + lastArg = args.pop(); + args.unshift( lastArg ); + return _.defaults.apply( _, args ); +} + + //============================================================================== /** A mixin for models that allow T/F/Matching to their attributes - useful when * searching or filtering collections of models. @@ -328,205 +346,6 @@ var HiddenUntilActivatedViewMixin = /** @lends hiddenUntilActivatedMixin# */{ }; -//============================================================================== -/** Function that allows mixing of hashs into bbone MVC while showing the mixins first - * (before the more local class overrides/hash). - * Basically, a simple reversal of param order on _.defaults() - to show mixins in top of definition. - * @example: - * var NewModel = Something.extend( mixin( MyMixinA, MyMixinB, { ... myVars : ... }) ); - * - * NOTE: this does not combine any hashes (like events, etc.) and you're expected to handle that - */ -function mixin( mixinHash1, /* mixinHash2, etc: ... variadic */ propsHash ){ - var args = Array.prototype.slice.call( arguments, 0 ), - lastArg = args.pop(); - args.unshift( lastArg ); - return _.defaults.apply( _, args ); -} - -//============================================================================== -/** Return an underscore template fn from an array of strings. - * @param {String[]} template the template strings to compile into the underscore template fn - * @param {String} jsonNamespace an optional namespace for the json data passed in (defaults to 'model') - * @returns {Function} the (wrapped) underscore template fn - * The function accepts: - * - * The template strings can access: - * the json/model hash using model ("<%- model.myAttr %>) using the jsonNamespace above - * _l: the localizer function - * view (if passed): ostensibly, the view using the template (handy for view instance vars) - * Because they're namespaced, undefined attributes will not throw an error. - * - * @example: - * templateBler : BASE_MVC.wrapTemplate([ - * '
', - * '<% print( _l( mynamespace.message ) ); %>:<%= view.status %>' - * '
' - * ], 'mynamespace' ) - * - * Meant to be called in a View's definition in order to compile only once. - * - */ -function wrapTemplate( template, jsonNamespace ){ - jsonNamespace = jsonNamespace || 'model'; - var templateFn = _.template( template.join( '' ) ); - return function( json, view ){ - var templateVars = { view : view || {}, _l : _l }; - templateVars[ jsonNamespace ] = json || {}; - return templateFn( templateVars ); - }; -} - -//============================================================================== -/** A view which, when first rendered, shows only summary data/attributes, but - * can be expanded to show further details (and optionally fetch those - * details from the server). - */ -var ExpandableView = Backbone.View.extend( LoggableMixin ).extend({ -//TODO: Although the reasoning behind them is different, this shares a lot with HiddenUntilActivated above: combine them - //PRECONDITION: model must have method hasDetails - //PRECONDITION: subclasses must have templates.el and templates.details - - initialize : function( attributes ){ - /** are the details of this view expanded/shown or not? */ - this.expanded = attributes.expanded || false; - //this.log( '\t expanded:', this.expanded ); - this.fxSpeed = attributes.fxSpeed || this.fxSpeed; - }, - - // ........................................................................ render main - /** jq fx speed */ - fxSpeed : 'fast', - - /** Render this content, set up ui. - * @param {Number or String} speed the speed of the render - */ - render : function( speed ){ - var $newRender = this._buildNewRender(); - this._setUpBehaviors( $newRender ); - this._queueNewRender( $newRender, speed ); - return this; - }, - - /** Build a temp div containing the new children for the view's $el. - * If the view is already expanded, build the details as well. - */ - _buildNewRender : function(){ - // create a new render using a skeleton template, render title buttons, render body, and set up events, etc. - var $newRender = $( this.templates.el( this.model.toJSON(), this ) ); - if( this.expanded ){ - this.$details( $newRender ).replaceWith( this._renderDetails().show() ); - } - return $newRender; - }, - - /** Fade out the old el, swap in the new contents, then fade in. - * @param {Number or String} speed jq speed to use for rendering effects - * @fires rendered when rendered - */ - _queueNewRender : function( $newRender, speed ) { - speed = ( speed === undefined )?( this.fxSpeed ):( speed ); - var view = this; - - $( view ).queue( 'fx', [ - function( next ){ this.$el.fadeOut( speed, next ); }, - function( next ){ - view._swapNewRender( $newRender ); - next(); - }, - function( next ){ this.$el.fadeIn( speed, next ); }, - function( next ){ - this.trigger( 'rendered', view ); - next(); - } - ]); - }, - - /** empty out the current el, move the $newRender's children in */ - _swapNewRender : function( $newRender ){ - return this.$el.empty().attr( 'class', this.className ).append( $newRender.children() ); - }, - - /** set up js behaviors, event handlers for elements within the given container - * @param {jQuery} $container jq object that contains the elements to process (defaults to this.$el) - */ - _setUpBehaviors : function( $where ){ - $where = $where || this.$el; - // set up canned behavior on children (bootstrap, popupmenus, editable_text, etc.) - //make_popup_menus( $where ); - $where.find( '[title]' ).tooltip({ placement : 'bottom' }); - }, - - // ......................................................................... details - /** shortcut to details DOM (as jQ) */ - $details : function( $where ){ - $where = $where || this.$el; - return $where.find( '.details' ); - }, - - /** build the DOM for the details and set up behaviors on it */ - _renderDetails : function(){ - var $newDetails = $( this.templates.details( this.model.toJSON(), this ) ); - this._setUpBehaviors( $newDetails ); - return $newDetails; - }, - - // ......................................................................... expansion/details - /** Show or hide the details - * @param {Boolean} expand if true, expand; if false, collapse - */ - toggleExpanded : function( expand ){ - expand = ( expand === undefined )?( !this.expanded ):( expand ); - if( expand ){ - this.expand(); - } else { - this.collapse(); - } - return this; - }, - - /** Render and show the full, detailed body of this view including extra data and controls. - * note: if the model does not have detailed data, fetch that data before showing the body - * @fires expanded when a body has been expanded - */ - expand : function(){ - var view = this; - return view._fetchModelDetails() - .always(function(){ - var $newDetails = view._renderDetails(); - view.$details().replaceWith( $newDetails ); - // needs to be set after the above or the slide will not show - view.expanded = true; - $newDetails.slideDown( view.fxSpeed, function(){ - view.trigger( 'expanded', view ); - }); - }); - }, - - /** Check for model details and, if none, fetch them. - * @returns {jQuery.promise} the model.fetch.xhr if details are being fetched, an empty promise if not - */ - _fetchModelDetails : function(){ - if( !this.model.hasDetails() ){ - return this.model.fetch(); - } - return jQuery.when(); - }, - - /** Hide the body/details of an HDA. - * @fires collapsed when a body has been collapsed - */ - collapse : function(){ - var view = this; - view.expanded = false; - this.$details().slideUp( view.fxSpeed, function(){ - view.trigger( 'collapsed', view ); - }); - } - -}); - - //============================================================================== /** Mixin for views that can be dragged and dropped * Allows for the drag behavior to be turned on/off, setting/removing jQuery event @@ -711,186 +530,48 @@ var SelectableViewMixin = { //============================================================================== -/** A view that is displayed in some larger list/grid/collection. - * Inherits from Expandable, Selectable, Draggable. - * The DOM contains warnings, a title bar, and a series of primary action controls. - * Primary actions are meant to be easily accessible item functions (such as delete) - * that are rendered in the title bar. +/** Return an underscore template fn from an array of strings. + * @param {String[]} template the template strings to compile into the underscore template fn + * @param {String} jsonNamespace an optional namespace for the json data passed in (defaults to 'model') + * @returns {Function} the (wrapped) underscore template fn + * The function accepts: * - * Details are rendered when the user clicks the title bar or presses enter/space when - * the title bar is in focus. + * The template strings can access: + * the json/model hash using model ("<%- model.myAttr %>) using the jsonNamespace above + * _l: the localizer function + * view (if passed): ostensibly, the view using the template (handy for view instance vars) + * Because they're namespaced, undefined attributes will not throw an error. + * + * @example: + * templateBler : BASE_MVC.wrapTemplate([ + * '
', + * '<% print( _l( mynamespace.message ) ); %>:<%= view.status %>' + * '
' + * ], 'mynamespace' ) + * + * Meant to be called in a View's definition in order to compile only once. * - * Designed as a base class for history panel contents - but usable elsewhere (I hope). */ -var ListItemView = ExpandableView.extend( mixin( SelectableViewMixin, DraggableViewMixin, { - -//TODO: that's a little contradictory - tagName : 'div', - className : 'list-item', - - /** Set up the base class and all mixins */ - initialize : function( attributes ){ - ExpandableView.prototype.initialize.call( this, attributes ); - SelectableViewMixin.initialize.call( this, attributes ); - DraggableViewMixin.initialize.call( this, attributes ); - }, - - // ........................................................................ rendering - /** In this override, call methods to build warnings, titlebar and primary actions */ - _buildNewRender : function(){ - var $newRender = ExpandableView.prototype._buildNewRender.call( this ); - $newRender.find( '.warnings' ).replaceWith( this._renderWarnings() ); - $newRender.find( '.title-bar' ).replaceWith( this._renderTitleBar() ); - $newRender.find( '.primary-actions' ).append( this._renderPrimaryActions() ); - $newRender.find( '.subtitle' ).replaceWith( this._renderSubtitle() ); - return $newRender; - }, - - /** In this override, render the selector controls and set up dragging before the swap */ - _swapNewRender : function( $newRender ){ - ExpandableView.prototype._swapNewRender.call( this, $newRender ); - if( this.selectable ){ this.showSelector( 0 ); } - if( this.draggable ){ this.draggableOn(); } - return this.$el; - }, - - /** Render any warnings the item may need to show (e.g. "I'm deleted") */ - _renderWarnings : function(){ - var view = this, - $warnings = $( '
' ), - json = view.model.toJSON(); -//TODO:! unordered (map) - _.each( view.templates.warnings, function( templateFn ){ - $warnings.append( $( templateFn( json, view ) ) ); - }); - return $warnings; - }, - - /** Render the title bar (the main/exposed SUMMARY dom element) */ - _renderTitleBar : function(){ - return $( this.templates.titleBar( this.model.toJSON(), this ) ); - }, - - /** Return an array of jQ objects containing common/easily-accessible item controls */ - _renderPrimaryActions : function(){ - // override this - return []; - }, - - /** Render the title bar (the main/exposed SUMMARY dom element) */ - _renderSubtitle : function(){ - return $( this.templates.subtitle( this.model.toJSON(), this ) ); - }, - - // ......................................................................... events - /** event map */ - events : { - // expand the body when the title is clicked or when in focus and space or enter is pressed - 'click .title-bar' : '_clickTitleBar', - 'keydown .title-bar' : '_keyDownTitleBar', - - // dragging - don't work, originalEvent === null - //'dragstart .dataset-title-bar' : 'dragStartHandler', - //'dragend .dataset-title-bar' : 'dragEndHandler' - - 'click .selector' : 'toggleSelect' - }, - - /** expand when the title bar is clicked */ - _clickTitleBar : function( event ){ - event.stopPropagation(); - this.toggleExpanded(); - }, - - /** expand when the title bar is in focus and enter or space is pressed */ - _keyDownTitleBar : function( event ){ - // bail (with propagation) if keydown and not space or enter - var KEYCODE_SPACE = 32, KEYCODE_RETURN = 13; - if( event && ( event.type === 'keydown' ) - &&( event.keyCode === KEYCODE_SPACE || event.keyCode === KEYCODE_RETURN ) ){ - this.toggleExpanded(); - event.stopPropagation(); - return false; - } - return true; - }, - - // ......................................................................... misc - /** String representation */ - toString : function(){ - var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); - return 'ListItemView(' + modelString + ')'; - } -})); - -// ............................................................................ TEMPLATES -/** underscore templates */ -ListItemView.prototype.templates = (function(){ -//TODO: move to require text! plugin - - var elTemplato = wrapTemplate([ - '
', - // errors, messages, etc. - '
', - - // multi-select checkbox - '
', - '', - '
', - // space for title bar buttons - gen. floated to the right - '
', - '
', - - // expandable area for more details - '
', - '
' - ]); - - var warnings = {}; - - var titleBarTemplate = wrapTemplate([ - // adding a tabindex here allows focusing the title bar and the use of keydown to expand the dataset display - '
', -//TODO: prob. belongs in dataset-list-item - '', - '
', - '<%- element.name %>', - '
', - '
', - '
' - ], 'element' ); - - var subtitleTemplate = wrapTemplate([ - // override this - '
' - ]); - - var detailsTemplate = wrapTemplate([ - // override this - '
' - ]); - - return { - el : elTemplato, - warnings : warnings, - titleBar : titleBarTemplate, - subtitle : subtitleTemplate, - details : detailsTemplate +function wrapTemplate( template, jsonNamespace ){ + jsonNamespace = jsonNamespace || 'model'; + var templateFn = _.template( template.join( '' ) ); + return function( json, view ){ + var templateVars = { view : view || {}, _l : _l }; + templateVars[ jsonNamespace ] = json || {}; + return templateFn( templateVars ); }; -}()); +} //============================================================================== return { LoggableMixin : LoggableMixin, SessionStorageModel : SessionStorageModel, + mixin : mixin, SearchableModelMixin : SearchableModelMixin, HiddenUntilActivatedViewMixin : HiddenUntilActivatedViewMixin, - mixin : mixin, - wrapTemplate : wrapTemplate, - ExpandableView : ExpandableView, DraggableViewMixin : DraggableViewMixin, SelectableViewMixin : SelectableViewMixin, - ListItemView : ListItemView + wrapTemplate : wrapTemplate }; }); diff --git a/static/scripts/mvc/collection/collection-li.js b/static/scripts/mvc/collection/collection-li.js index 8c41bb33761..e2792836881 100644 --- a/static/scripts/mvc/collection/collection-li.js +++ b/static/scripts/mvc/collection/collection-li.js @@ -1,11 +1,12 @@ define([ + "mvc/list/list-item", "mvc/dataset/dataset-li", "mvc/base-mvc", "utils/localization" -], function( DATASET_LI, BASE_MVC, _l ){ +], function( LIST_ITEM, DATASET_LI, BASE_MVC, _l ){ /* global Backbone, LoggableMixin */ //============================================================================== -var ListItemView = BASE_MVC.ListItemView; +var ListItemView = LIST_ITEM.ListItemView; /** @class Read only view for DatasetCollection. */ var DCListItemView = ListItemView.extend( @@ -97,8 +98,7 @@ 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, + //logger : console, /** add the DCE class to the list item */ className : ListItemView.prototype.className + " dataset-collection-element", diff --git a/static/scripts/mvc/collection/collection-model.js b/static/scripts/mvc/collection/collection-model.js index e0582c07190..df42d87ddfb 100644 --- a/static/scripts/mvc/collection/collection-model.js +++ b/static/scripts/mvc/collection/collection-model.js @@ -270,6 +270,11 @@ var DatasetCollection = Backbone.Model return this.save( { deleted: false }, options ); }, + /** Is this collection deleted or purged? */ + isDeletedOrPurged : function(){ + return ( this.get( 'deleted' ) || this.get( 'purged' ) ); + }, + // ........................................................................ searchable /** searchable attributes for collections */ searchAttributes : [ diff --git a/static/scripts/mvc/collection/collection-panel.js b/static/scripts/mvc/collection/collection-panel.js index c655c01d1f8..1ac10b14085 100644 --- a/static/scripts/mvc/collection/collection-panel.js +++ b/static/scripts/mvc/collection/collection-panel.js @@ -1,9 +1,10 @@ define([ + "mvc/list/list-panel", "mvc/collection/collection-model", "mvc/collection/collection-li", "mvc/base-mvc", "utils/localization" -], function( DC_MODEL, DC_LI, BASE_MVC, _l ){ +], function( LIST_PANEL, DC_MODEL, DC_LI, BASE_MVC, _l ){ /* ============================================================================= TODO: @@ -11,36 +12,29 @@ TODO: // ============================================================================= /** @class non-editable, read-only View/Controller for a dataset collection. */ -var CollectionPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( +var _super = LIST_PANEL.ModelListPanel; +var CollectionPanel = _super.extend( /** @lends CollectionPanel.prototype */{ //MODEL is either a DatasetCollection (or subclass) or a DatasetCollectionElement (list of pairs) /** logger used to record this.log messages, commonly set to console */ //logger : console, - tagName : 'div', - className : 'dataset-collection-panel', - - /** (in ms) that jquery effects will use */ - fxSpeed : 'fast', + className : _super.prototype.className + ' dataset-collection-panel', /** sub view class used for datasets */ DatasetDCEViewClass : DC_LI.DatasetDCEListItemView, /** sub view class used for nested collections */ NestedDCDCEViewClass : DC_LI.NestedDCDCEListItemView, + /** key of attribute in model to assign to this.collection */ + modelCollectionKey : 'elements', // ......................................................................... SET UP /** Set up the view, set up storage, bind listeners to HistoryContents events * @param {Object} attributes optional settings for the panel */ initialize : function( attributes ){ - attributes = attributes || {}; - // set the logger if requested - if( attributes.logger ){ - this.logger = attributes.logger; - } - this.log( this + '.initialize:', attributes ); - + _super.prototype.initialize.call( this, attributes ); this.linkTarget = attributes.linkTarget || '_blank'; this.hasUser = attributes.hasUser; @@ -50,185 +44,49 @@ var CollectionPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( //window.collectionPanel = this; }, - /** create any event listeners for the panel - * @fires: rendered:initial on the first render - * @fires: empty-history when switching to a history with no HDAs or creating a new history - */ - _setUpListeners : function(){ - // debugging - //if( this.logger ){ - this.on( 'all', function( event ){ - this.log( this + '', arguments ); - }, this ); - //} - return this; - }, - - // ------------------------------------------------------------------------ history/hda event listening - /** listening for history and HDA events */ - _setUpModelEventHandlers : function(){ - return this; - }, - - // ------------------------------------------------------------------------ panel rendering - /** Render panel - * @fires: rendered when the panel is attached and fully visible - * @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 ); - //this.debug( this + '.render, fxSpeed:', speed ); - var panel = this, - $newRender; - - // handle the possibility of no model (can occur if fetching the model returns an error) - if( !this.model ){ - return this; - } - $newRender = this.renderModel(); - - // fade out existing, swap with the new, fade in, set up behaviours - $( panel ).queue( 'fx', [ - function( next ){ - if( speed && panel.$el.is( ':visible' ) ){ - panel.$el.fadeOut( speed, next ); - } else { - next(); - } - }, - function( next ){ - // swap over from temp div newRender - panel.$el.empty(); - if( $newRender ){ - panel.$el.append( $newRender.children() ); - } - next(); - }, - function( next ){ - if( speed && !panel.$el.is( ':visible' ) ){ - panel.$el.fadeIn( speed, next ); - } else { - next(); - } - }, - function( next ){ - //TODO: ideally, these would be set up before the fade in (can't because of async save text) - if( callback ){ callback.call( this ); } - panel.trigger( 'rendered', this ); - next(); - } - ]); - return this; - }, - - /** render with collection data - * @returns {jQuery} dom fragment as temporary container to be swapped out later - */ - renderModel : function( ){ - // tmp div for final swap in render -//TODO: ugh - reuse issue - refactor out - var type = this.model.get( 'collection_type' ) || this.model.object.get( 'collection_type' ), - json = _.extend( this.model.toJSON(), { - parentName : this.parentName, - type : type - }), - $newRender = $( '
' ).append( this.templates.panel( json ) ); - this._setUpBehaviours( $newRender ); - this.renderContents( $newRender ); - return $newRender; - }, - - /** Set up js/widget behaviours */ - _setUpBehaviours : function( $where ){ - //TODO: these should be either sub-MVs, or handled by events - $where = $where || this.$el; - $where.find( '[title]' ).tooltip({ placement: 'bottom' }); - 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 list content views are attached */ - $datasetsList : function( $where ){ - return ( $where || this.$el ).find( '.datasets-list' ); - }, - // ------------------------------------------------------------------------ sub-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 ); - $whereTo = $whereTo || this.$el; - - this.warn( this + '.renderContents:, model:', this.model ); - var panel = this, - contentViews = {}, - //NOTE: no filtering here - visibleContents = this.model.getVisibleContents(); - //this.debug( 'renderContents, visibleContents:', visibleContents, $whereTo ); - - this.$datasetsList( $whereTo ).empty(); - if( visibleContents && visibleContents.length ){ - visibleContents.each( function( content ){ - var contentId = content.id, - contentView = panel._createContentView( content ); - contentViews[ contentId ] = contentView; - panel._attachContentView( contentView.render(), $whereTo ); - }); - } - this.contentViews = contentViews; - return this.contentViews; + /** In this override, use model.getVisibleContents */ + _filterCollection : function(){ +//TODO: should *not* be model.getVisibleContents + return this.model.getVisibleContents(); }, - /** */ - _createContentView : function( content ){ - //this.debug( 'content json:', JSON.stringify( content, null, ' ' ) ); - var contentView = null, - ContentClass = this._getContentClass( content ); - //this.debug( 'content:', content ); - //this.debug( 'ContentClass:', ContentClass ); - contentView = new ContentClass({ - model : content, - linkTarget : this.linkTarget, - //draggable : true, - hasUser : this.hasUser, - logger : this.logger - }); - //this.debug( 'contentView:', contentView ); - this._setUpContentListeners( contentView ); - return contentView; - }, - - /** */ - _getContentClass : function( content ){ - //this.debug( this + '._getContentClass:', content ); + /** override to return proper view class based on element_type */ + _getItemViewClass : function( model ){ + //this.debug( this + '._getItemViewClass:', model ); //TODO: subclasses use DCEViewClass - but are currently unused - decide - switch( content.get( 'element_type' ) ){ + switch( model.get( 'element_type' ) ){ case 'hda': return this.DatasetDCEViewClass; case 'dataset_collection': return this.NestedDCDCEViewClass; } - throw new TypeError( 'Unknown element type:', content.get( 'element_type' ) ); + throw new TypeError( 'Unknown element type:', model.get( 'element_type' ) ); }, - /** Set up listeners for content view events. In this override, handle collection expansion. */ - _setUpContentListeners : function( contentView ){ + /** override to add link target and anon */ + _getItemViewOptions : function( model ){ + var options = _super.prototype._getItemViewOptions.call( this, model ); + return _.extend( options, { + linkTarget : this.linkTarget, + hasUser : this.hasUser + }); + }, + + /** when a sub-view is clicked in the collection panel that is itself a collection, + * hide this panel's elements and show the sub-collection in its own panel. + */ + _setUpItemViewListeners : function( view ){ var panel = this; - if( contentView.model.get( 'element_type' ) === 'dataset_collection' ){ - contentView.on( 'expanded', function( collectionView ){ + _super.prototype._setUpItemViewListeners.call( panel, view ); + //TODO:?? doesn't seem to belong here + if( view.model.get( 'element_type' ) === 'dataset_collection' ){ + view.on( 'expanded', function( collectionView ){ panel.info( 'expanded', collectionView ); panel._addCollectionPanel( collectionView ); }); } + return panel; }, /** When a sub-collection is clicked, hide the current panel and render the sub-collection in its own panel */ @@ -246,7 +104,7 @@ var CollectionPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( }); currPanel.panelStack.push( panel ); - currPanel.$( '.controls' ).add( '.datasets-list' ).hide(); + currPanel.$( '.controls' ).add( '.list-items' ).hide(); currPanel.$el.append( panel.$el ); panel.on( 'close', function(){ currPanel.render(); @@ -266,14 +124,6 @@ var CollectionPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( } }, - /** attach an contentView to the panel */ - _attachContentView : function( contentView, $whereTo ){ - $whereTo = $whereTo || this.$el; - var $datasetsList = this.$datasetsList( $whereTo ); - $datasetsList.append( contentView.$el ); - return this; - }, - // ------------------------------------------------------------------------ panel events /** event map */ events : { @@ -293,16 +143,16 @@ var CollectionPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( } }); -//----------------------------------------------------------------------------- TEMPLATES -/** underscore templates */ -CollectionPanel.templates = CollectionPanel.prototype.templates = (function(){ -// use closure to run underscore template fn only once at module load - var _panelTemplate = _.template([ + +//------------------------------------------------------------------------------ TEMPLATES +CollectionPanel.prototype.templates = (function(){ + + var controlsTemplate = BASE_MVC.wrapTemplate([ '
', '', @@ -310,29 +160,25 @@ CollectionPanel.templates = CollectionPanel.prototype.templates = (function(){ '
<%- collection.name || collection.element_identifier %>
', '
', //TODO: remove logic from template - '<% if( collection.type === "list" ){ %>', + '<% if( collection.collection_type === "list" ){ %>', _l( 'a list of datasets' ), - '<% } else if( collection.type === "paired" ){ %>', + '<% } else if( collection.collection_type === "paired" ){ %>', _l( 'a pair of datasets' ), - '<% } else if( collection.type === "list:paired" ){ %>', + '<% } else if( collection.collection_type === "list:paired" ){ %>', _l( 'a list of paired datasets' ), '<% } %>', '
', '
', - '
', - // where the datasets/hdas are added - '
' - ].join( '' )); + '' + ], 'collection' ); - // we override here in order to pass the localizer (_L) into the template scope - since we use it as a fn within - return { - panel : function( json ){ - return _panelTemplate({ _l: _l, collection: json }); - } - }; + return _.extend( _.clone( _super.prototype.templates ), { + controls : controlsTemplate + }); }()); + // ============================================================================= /** @class non-editable, read-only View/Controller for a dataset collection. */ var ListCollectionPanel = CollectionPanel.extend( diff --git a/static/scripts/mvc/dataset/dataset-li.js b/static/scripts/mvc/dataset/dataset-li.js index 16acfc4eb9f..583e0121b40 100644 --- a/static/scripts/mvc/dataset/dataset-li.js +++ b/static/scripts/mvc/dataset/dataset-li.js @@ -1,8 +1,9 @@ define([ + "mvc/list/list-item", "mvc/dataset/states", "mvc/base-mvc", "utils/localization" -], function( STATES, BASE_MVC, _l ){ +], function( LIST_ITEM, STATES, BASE_MVC, _l ){ /* global Backbone */ /*============================================================================== TODO: @@ -11,7 +12,7 @@ TODO: simplify button rendering ==============================================================================*/ -var _super = BASE_MVC.ListItemView; +var _super = LIST_ITEM.ListItemView; /** @class Read only list view for either LDDAs, HDAs, or HDADCEs. * Roughly, any DatasetInstance (and not a raw Dataset). */ diff --git a/static/scripts/mvc/dataset/dataset-list.js b/static/scripts/mvc/dataset/dataset-list.js index 8d8d4108eec..fb606663171 100644 --- a/static/scripts/mvc/dataset/dataset-list.js +++ b/static/scripts/mvc/dataset/dataset-list.js @@ -1,5 +1,5 @@ define([ - "mvc/dataset/list-panel", + "mvc/list/list-panel", "mvc/dataset/dataset-li", "mvc/base-mvc", "utils/localization" diff --git a/static/scripts/mvc/dataset/dataset-model.js b/static/scripts/mvc/dataset/dataset-model.js index afd82bea38b..a2a365e118c 100644 --- a/static/scripts/mvc/dataset/dataset-model.js +++ b/static/scripts/mvc/dataset/dataset-model.js @@ -94,6 +94,9 @@ var DatasetAssociation = Backbone.Model.extend( BASE_MVC.LoggableMixin ).extend( this.trigger( 'state:ready', currModel, newState, this.previous( 'state' ) ); } }); + this.on( 'change:urls', function(){ + console.warn( 'change:urls', arguments ); + }); // the download url (currenlty) relies on having a correct file extension this.on( 'change:id change:file_ext', function( currModel ){ this._generateUrls(); @@ -104,6 +107,8 @@ var DatasetAssociation = Backbone.Model.extend( BASE_MVC.LoggableMixin ).extend( /** override to add urls */ toJSON : function(){ var json = Backbone.Model.prototype.toJSON.call( this ); + //console.warn( 'returning json?' ); + //return json; return _.extend( json, { urls : this.urls }); diff --git a/static/scripts/mvc/history/hda-li.js b/static/scripts/mvc/history/hda-li.js index 8eae89c9d75..a54c679af76 100644 --- a/static/scripts/mvc/history/hda-li.js +++ b/static/scripts/mvc/history/hda-li.js @@ -17,12 +17,12 @@ var HDAListItemView = _super.extend( /** logger used to record this.log messages, commonly set to console */ //logger : console, + className : _super.prototype.className + " history-content", + initialize : function( attributes, options ){ _super.prototype.initialize.call( this, attributes, options ); }, - className : _super.prototype.className + " history-content", - // ......................................................................... misc /** String representation */ toString : function(){ diff --git a/static/scripts/mvc/history/history-contents.js b/static/scripts/mvc/history/history-contents.js index 51b45469531..4a0f8b6d8b3 100644 --- a/static/scripts/mvc/history/history-contents.js +++ b/static/scripts/mvc/history/history-contents.js @@ -53,6 +53,7 @@ var HistoryContents = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).exten */ initialize : function( models, options ){ options = options || {}; +//TODO: could probably use the contents.history_id instead this.historyId = options.historyId; //this._setUpListeners(); @@ -226,7 +227,7 @@ var HistoryContents = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).exten if( !existing ){ return model; } // merge the models _BEFORE_ calling the superclass version - var merged = existing.toJSON(); + var merged = _.clone( existing.attributes ); _.extend( merged, model ); return merged; }); @@ -303,6 +304,13 @@ var HistoryContents = Backbone.Collection.extend( BASE_MVC.LoggableMixin ).exten return xhr; }, + /** In this override, copy the historyId to the clone */ + clone : function(){ + var clone = Backbone.Collection.prototype.clone.call( this ); + clone.historyId = this.historyId; + return clone; + }, + /** debugging */ print : function(){ var contents = this; diff --git a/static/scripts/mvc/history/history-model.js b/static/scripts/mvc/history/history-model.js index c6ef019f0e1..d33ce1be61f 100644 --- a/static/scripts/mvc/history/history-model.js +++ b/static/scripts/mvc/history/history-model.js @@ -232,7 +232,7 @@ History.UPDATE_DELAY = 4000; /** Get data for a history then its hdas using a sequential ajax call, return a deferred to receive both */ History.getHistoryData = function getHistoryData( historyId, options ){ options = options || {}; - var hdaDetailIds = options.hdaDetailIds || []; + var detailIdsFn = options.detailIdsFn || []; var hdcaDetailIds = options.hdcaDetailIds || []; //console.debug( 'getHistoryData:', historyId, options ); @@ -250,20 +250,20 @@ History.getHistoryData = function getHistoryData( historyId, options ){ // get the number of hdas accrd. to the history return historyData && historyData.empty; } - function getHdas( historyData ){ + function getContents( historyData ){ // get the hda data // if no hdas accrd. to history: return empty immed. if( isEmpty( historyData ) ){ return []; } // if there are hdas accrd. to history: get those as well - if( _.isFunction( hdaDetailIds ) ){ - hdaDetailIds = hdaDetailIds( historyData ); + if( _.isFunction( detailIdsFn ) ){ + detailIdsFn = detailIdsFn( historyData ); } if( _.isFunction( hdcaDetailIds ) ){ hdcaDetailIds = hdcaDetailIds( historyData ); } var data = {}; - if( hdaDetailIds.length ) { - data.dataset_details = hdaDetailIds.join( ',' ); + if( detailIdsFn.length ) { + data.dataset_details = detailIdsFn.join( ',' ); } if( hdcaDetailIds.length ) { // for symmetry, not actually used by backend of consumed @@ -274,10 +274,10 @@ History.getHistoryData = function getHistoryData( historyId, options ){ } // getting these concurrently is 400% slower (sqlite, local, vanilla) - so: - // chain the api calls - getting history first then hdas + // chain the api calls - getting history first then contents var historyFn = options.historyFn || getHistory, - hdaFn = options.hdaFn || getHdas; + contentsFn = options.contentsFn || getContents; // chain ajax calls: get history first, then hdas var historyXHR = historyFn( historyId ); @@ -291,15 +291,15 @@ History.getHistoryData = function getHistoryData( historyId, options ){ df.reject( xhr, 'loading the history' ); }); - var hdaXHR = historyXHR.then( hdaFn ); - hdaXHR.then( function( hdaJSON ){ - df.notify({ status: 'dataset data retrieved', historyJSON: historyJSON, hdaJSON: hdaJSON }); + var contentsXHR = historyXHR.then( contentsFn ); + contentsXHR.then( function( contentsJSON ){ + df.notify({ status: 'contents data retrieved', historyJSON: historyJSON, contentsJSON: contentsJSON }); // we've got both: resolve the outer scope deferred - df.resolve( historyJSON, hdaJSON ); + df.resolve( historyJSON, contentsJSON ); }); - hdaXHR.fail( function( xhr, status, message ){ + contentsXHR.fail( function( xhr, status, message ){ // call reject on the outer deferred to allow its fail callback to run - df.reject( xhr, 'loading the datasets', { history: historyJSON } ); + df.reject( xhr, 'loading the contents', { history: historyJSON } ); }); return df; diff --git a/static/scripts/mvc/history/history-panel-annotated.js b/static/scripts/mvc/history/history-panel-annotated.js index 4144b0c7fb2..015ecd4e231 100644 --- a/static/scripts/mvc/history/history-panel-annotated.js +++ b/static/scripts/mvc/history/history-panel-annotated.js @@ -1,118 +1,97 @@ define([ "mvc/history/history-panel", "mvc/history/hda-li", + "mvc/history/hdca-li", + "mvc/base-mvc", "utils/localization" -], function( HPANEL, HDA_LI, _l ){ +], function( HPANEL, HDA_LI, HDCA_LI, BASE_MVC, _l ){ /* ============================================================================= TODO: ============================================================================= */ -var _super = HPANEL.ReadOnlyHistoryPanel; +var _super = HPANEL.HistoryPanel; // used in history/display.mako and history/embed.mako /** @class View/Controller for a tabular view of the history model. - * @name AnnotatedHistoryPanel * * As ReadOnlyHistoryPanel, but with: * history annotation always shown * datasets displayed in a table: * datasets in left cells, dataset annotations in the right - * - * @augments Backbone.View - * @borrows LoggableMixin#logger as #logger - * @borrows LoggableMixin#log as #log - * @constructs */ var AnnotatedHistoryPanel = _super.extend( /** @lends AnnotatedHistoryPanel.prototype */{ /** logger used to record this.log messages, commonly set to console */ - // comment this out to suppress log output //logger : console, - className : 'annotated-history-panel', - - //TODO:?? possibly into own annotated class - /** class to use for constructing the HDA views */ - //HDAViewClass : HDA_LI.HDAListItemView, + className : _super.prototype.className + ' annotated-history-panel', // ------------------------------------------------------------------------ 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 + /** In this override, add the history annotation */ - renderModel : function( ){ - // why do we need this here? why isn't className being applied? - this.$el.addClass( this.className ); - var $newRender = _super.prototype.renderModel.call( this ), - // move datasets from div to table - $datasetsList = this.$datasetsList( $newRender ), - $datasetsTable = $( '' ).addClass( 'datasets-list datasets-table' ); - $datasetsTable.append( $datasetsList.children() ); - $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() ); - - // 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() ); - + _buildNewRender : function(){ + //TODO: shouldn't this display regardless (on all non-current panels)? + var $newRender = _super.prototype._buildNewRender.call( this ); + this.renderHistoryAnnotation( $newRender ); return $newRender; }, /** render the history's annotation as its own field */ - renderHistoryAnnotation : function(){ + renderHistoryAnnotation : function( $newRender ){ var annotation = this.model.get( 'annotation' ); - if( !annotation ){ return null; } - return $([ - '
', annotation, '
' - ].join( '' )); + if( !annotation ){ return; } + $newRender.find( '.controls .annotation-display' ).text( annotation ); }, - /** 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 + /** In this override, convert the list-items tag to a table + * and add table header cells to indicate the dataset, annotation columns */ - renderHdas : function( $whereTo ){ + renderItems : function( $whereTo ){ $whereTo = $whereTo || this.$el; - var hdaViews = _super.prototype.renderHdas.call( this, $whereTo ); - this.$datasetsList( $whereTo ).prepend( $( '' ).addClass( 'headers' ).append([ - $( '
' ).text( _l( 'Dataset' ) ), - $( '' ).text( _l( 'Annotation' ) ) - ])); - return hdaViews; + + // convert to table + $whereTo.find( '.list-items' ) + .replaceWith( $( '' ).addClass( 'list-items' ) ); + + // render rows/contents and prepend headers + var views = _super.prototype.renderItems.call( this, $whereTo ); + this.$list( $whereTo ) + .prepend( $( '' ).addClass( 'headers' ).append([ + $( '' ).addClass( 'dataset-row' ).append([ - $( '' ).append([ + $( '
' ).text( _l( 'Dataset' ) ), + $( '' ).text( _l( 'Annotation' ) ) + ])); + return views; }, - // ------------------------------------------------------------------------ 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 + // ------------------------------------------------------------------------ sub-views + /** In this override, wrap the content view in a table row + * with the content in the left td and annotation/extra-info in the right */ - attachContentView : 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 - var stateClass = _.find( hdaView.el.classList, function( c ){ return ( /^state\-/ ).test( c ); }), - annotation = hdaView.model.get( 'annotation' ) || '', - $tr = $( '
' ).addClass( 'dataset-container' ).append( hdaView.$el ) - // visually match the cell bg to the dataset at runtime (prevents the empty space) - // (getting bg via jq on hidden elem doesn't work on chrome/webkit - so use states) - //.css( 'background-color', hdaView.$el.css( 'background-color' ) ), - .addClass( stateClass? stateClass.replace( '-', '-color-' ): '' ), - $( '' ).addClass( 'additional-info' ).text( annotation ) - ]); - this.$datasetsList( $whereTo ).append( $tr ); + _attachItems : function( $whereTo ){ + this.$list( $whereTo ).append( this.views.map( function( view ){ + //TODO:?? possibly make this more flexible: instead of annotation use this._additionalInfo() + // build a row around the dataset with the std itemView in the first cell and the annotation in the next + var stateClass = _.find( view.el.classList, function( c ){ return ( /^state\-/ ).test( c ); }), + annotation = view.model.get( 'annotation' ) || '', + $tr = $( '
' ).addClass( 'contents-container' ).append( view.$el ) + // visually match the cell bg to the dataset at runtime (prevents the empty space) + // (getting bg via jq on hidden elem doesn't work on chrome/webkit - so use states) + //.css( 'background-color', view.$el.css( 'background-color' ) ), + .addClass( stateClass? stateClass.replace( '-', '-color-' ): '' ), + $( '' ).addClass( 'additional-info' ).text( annotation ) + ]); + return $tr; + })); + return this; }, // ------------------------------------------------------------------------ panel events /** event map */ events : _.extend( _.clone( _super.prototype.events ), { + // clicking on any part of the row will expand the items 'click tr' : function( ev ){ - //if( !ev.target.hasAttribute( 'href' ) ){ - $( ev.currentTarget ).find( '.dataset-title-bar' ).click(); - //} + $( ev.currentTarget ).find( '.title-bar' ).click(); }, // prevent propagation on icon btns so they won't bubble up to tr and toggleBodyVisibility 'click .icon-btn' : function( ev ){ @@ -127,6 +106,7 @@ var AnnotatedHistoryPanel = _super.extend( } }); + //============================================================================== return { AnnotatedHistoryPanel : AnnotatedHistoryPanel diff --git a/static/scripts/mvc/history/history-panel-edit-current.js b/static/scripts/mvc/history/history-panel-edit-current.js index 74fab60b35f..447f75e0a35 100644 --- a/static/scripts/mvc/history/history-panel-edit-current.js +++ b/static/scripts/mvc/history/history-panel-edit-current.js @@ -11,8 +11,6 @@ define([ var HistoryPanelPrefs = BASE_MVC.SessionStorageModel.extend( /** @lends HistoryPanelPrefs.prototype */{ defaults : { - /** is the panel currently showing the search/filter controls? */ - searching : false, /** should the tags editor be shown or hidden initially? */ tagsEditorShown : false, /** should the annotation editor be shown or hidden initially? */ @@ -32,31 +30,26 @@ HistoryPanelPrefs.storageKey = function storageKey(){ TODO: ============================================================================= */ -var _super = HPANEL_EDIT.HistoryPanel; +var _super = HPANEL_EDIT.HistoryPanelEdit; // used in root/index.mako /** @class View/Controller for the user's current history model as used in the history * panel (current right hand panel) of the analysis page. - * @name HistoryPanel * - * @augments Backbone.View - * @borrows LoggableMixin#logger as #logger - * @borrows LoggableMixin#log as #log - * @constructs + * The only history panel that will poll for updates. */ var CurrentHistoryPanel = _super.extend( /** @lends CurrentHistoryPanel.prototype */{ + className : _super.prototype.className + ' current-history-panel', + /** logger used to record this.log messages, commonly set to console */ - // comment this out to suppress log output //logger : console, emptyMsg : _l( "This history is empty. Click 'Get Data' on the left tool menu to start" ), noneFoundMsg : _l( "No matching datasets found" ), // ......................................................................... SET UP - /** Set up the view, set up storage, bind listeners to HistoryContents events - * @param {Object} attributes - */ + /** Set up the view, set up storage, bind listeners to HistoryContents events */ initialize : function( attributes ){ attributes = attributes || {}; @@ -72,19 +65,19 @@ var CurrentHistoryPanel = _super.extend( this.panelStack = []; }, - // ------------------------------------------------------------------------ loading history/hda models - /** (re-)loads the user's current history & hdas w/ details */ + // ------------------------------------------------------------------------ loading history/item models + /** (re-)loads the user's current history & contents w/ details */ loadCurrentHistory : function( attributes ){ this.debug( this + '.loadCurrentHistory' ); // implemented as a 'fresh start' or for when there is no model (intial panel render) var panel = this; - return this.loadHistoryWithHDADetails( 'current', attributes ) - .then(function( historyData, hdaData ){ + return this.loadHistoryWithDetails( 'current', attributes ) + .then(function( historyData, contentsData ){ panel.trigger( 'current-history', panel ); }); }, - /** loads a history & hdas w/ details and makes them the current history */ + /** loads a history & contents w/ details and makes them the current history */ switchToHistory : function( historyId, attributes ){ //this.info( 'switchToHistory:', historyId, attributes ); var panel = this, @@ -94,8 +87,8 @@ var CurrentHistoryPanel = _super.extend( // method : 'PUT' //}); }; - return this.loadHistoryWithHDADetails( historyId, attributes, historyFn ) - .then(function( historyData, hdaData ){ + return this.loadHistoryWithDetails( historyId, attributes, historyFn ) + .then(function( historyData, contentsData ){ panel.trigger( 'switched-history', panel ); }); }, @@ -115,7 +108,7 @@ var CurrentHistoryPanel = _super.extend( // id undefined bc there is no historyId yet - the server will provide // (no need for details - nothing expanded in new history) return this.loadHistory( undefined, attributes, historyFn ) - .then(function( historyData, hdaData ){ + .then(function( historyData, contentsData ){ panel.trigger( 'new-history', panel ); }); }, @@ -130,98 +123,60 @@ var CurrentHistoryPanel = _super.extend( return this; }, - // ------------------------------------------------------------------------ history/hda event listening - /** listening for history and HDA events */ - _setUpModelEventHandlers : function(){ - _super.prototype._setUpModelEventHandlers.call( this ); - // ---- history - // update the quota meter when current history changes size - if( Galaxy && Galaxy.quotaMeter ){ - this.listenTo( this.model, 'change:nice_size', function(){ - //this.info( '!! model size changed:', this.model.get( 'nice_size' ) ) -//TODO: global - Galaxy.quotaMeter.update(); - }); - } + // ------------------------------------------------------------------------ history/content event listening + /** listening for collection events */ + _setUpCollectionListeners : function(){ + _super.prototype._setUpCollectionListeners.call( this ); - // if an a hidden hda is created (gen. by a workflow), moves thru the updater to the ready state, + // if a hidden item is created (gen. by a workflow), moves thru the updater to the ready state, // then: remove it from the collection if the panel is set to NOT show hidden datasets - this.model.contents.on( 'state:ready', function( hda, newState, oldState ){ - if( ( !hda.get( 'visible' ) ) + this.collection.on( 'state:ready', function( model, newState, oldState ){ + if( ( !model.get( 'visible' ) ) && ( !this.storage.get( 'show_hidden' ) ) ){ - this.removeHdaView( this.hdaViews[ hda.id ] ); + this.removeItemView( this.viewFromModel( model ) ); } }, this ); }, - // ------------------------------------------------------------------------ panel rendering - /** Render urls, historyPanel body, and hdas (if any are shown) - * @fires: rendered when the panel is attached and fully visible - * @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, - $newRender; - - // handle the possibility of no model (can occur if fetching the model returns an error) - if( this.model ){ - $newRender = this.renderModel(); - } else { - $newRender = this.renderWithoutModel(); + /** listening for history events */ + _setUpModelListeners : function(){ + _super.prototype._setUpModelListeners.call( this ); + // ---- history + // update the quota meter when current history changes size +//TODO: global - have Galaxy listen to this instead + if( Galaxy && Galaxy.quotaMeter ){ + this.listenTo( this.model, 'change:nice_size', function(){ + //this.info( '!! model size changed:', this.model.get( 'nice_size' ) ) + Galaxy.quotaMeter.update(); + }); } - // fade out existing, swap with the new, fade in, set up behaviours - $( panel ).queue( 'fx', [ - function( next ){ - if( speed && panel.$el.is( ':visible' ) ){ - panel.$el.fadeOut( speed, next ); - } else { - next(); - } - }, - function( next ){ - // swap over from temp div newRender - panel.$el.empty(); - if( $newRender ){ - panel.$el.append( $newRender.children() ); - panel.renderBasedOnPrefs(); - } - next(); - }, - function( next ){ - if( speed && !panel.$el.is( ':visible' ) ){ - panel.$el.fadeIn( speed, next ); - } else { - next(); - } - }, - function( next ){ - //TODO: ideally, these would be set up before the fade in (can't because of async save text) - if( callback ){ callback.call( this ); } - panel.trigger( 'rendered', this ); - next(); - } - ]); - return this; }, - /** perform additional rendering based on preferences */ - renderBasedOnPrefs : function(){ - if( this.preferences.get( 'searching' ) ){ - this.toggleSearchControls( 0, true ); - } + // ------------------------------------------------------------------------ panel rendering + /** In this override, handle null models and move the search input to the top */ + _buildNewRender : function(){ + if( !this.model ){ return $(); } + var $newRender = _super.prototype._buildNewRender.call( this ); + //TODO: hacky + $newRender.find( '.search' ).prependTo( $newRender.find( '.controls' ) ); + this._renderQuotaMessage( $newRender ); + return $newRender; + }, + + /** render the message displayed when a user is over quota and can't run jobs */ + _renderQuotaMessage : function( $whereTo ){ + $whereTo = $whereTo || this.$el; + return $( this.templates.quotaMsg( {}, this ) ).prependTo( $whereTo.find( '.messages' ) ); }, /** In this override, add links to open data uploader or get data in the tools section */ - _renderEmptyMsg : function( $whereTo ){ + _renderEmptyMessage : function( $whereTo ){ var panel = this, $emptyMsg = panel.$emptyMessage( $whereTo ), $toolMenu = $( '.toolMenuContainer' ); - if( ( _.isEmpty( panel.hdaViews ) && !panel.searchFor ) + if( ( _.isEmpty( panel.views ) && !panel.searchFor ) && ( Galaxy && Galaxy.upload && $toolMenu.size() ) ){ $emptyMsg.empty(); @@ -246,25 +201,12 @@ var CurrentHistoryPanel = _super.extend( // $( this ).fadeTo( 200, 1.0 ); //}); }); - - $emptyMsg.show(); - - - } else { - _super.prototype._renderEmptyMsg.call( this, $whereTo ); + return $emptyMsg.show(); } - return this; + return _super.prototype._renderEmptyMessage.call( this, $whereTo ); }, - /** In this override, save the search control visibility state to preferences */ - toggleSearchControls : function( eventOrSpeed, show ){ - var visible = _super.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 - */ + /** 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 @@ -278,9 +220,7 @@ var CurrentHistoryPanel = _super.extend( 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 - */ + /** 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 @@ -296,54 +236,62 @@ var CurrentHistoryPanel = _super.extend( }, // ------------------------------------------------------------------------ sub-views - /** Set up HistoryPanel listeners for HDAView events. In this override, handle collection expansion. - */ - _setUpHdaListeners : function( hdaView ){ - _super.prototype._setUpHdaListeners.call( this, hdaView ); + /** Override to reverse order of views - newest contents on top */ + _attachItems : function( $whereTo ){ + this.$list( $whereTo ).append( this.views.reverse().map( function( view ){ + return view.$el; + })); + return this; + }, - var historyView = this; - if( hdaView instanceof historyView.HDCAViewClass ){ - hdaView.off( 'expanded' ); - hdaView.on( 'expanded', function( collectionView ){ - this.info( 'expanded', collectionView ); - historyView._addCollectionPanel( collectionView ); + /** In this override, handle collection expansion. */ + _setUpItemViewListeners : function( view ){ + var panel = this; + _super.prototype._setUpItemViewListeners.call( panel, view ); + + if( view instanceof panel.HDCAViewClass ){ + view.off( 'expanded' ); + view.on( 'expanded', function( collectionView ){ + panel.info( 'expanded', collectionView ); + panel._addCollectionPanel( collectionView ); }); - hdaView.off( 'collapsed' ); + view.off( 'collapsed' ); } }, + /** In this override, handle collection expansion. */ _addCollectionPanel : function( collectionView ){ - var historyView = this, + var panel = this, collectionModel = collectionView.model; this.debug( 'history panel (stack), collectionModel:', collectionModel ); - var panel = new ( this._getCollectionPanelClass( collectionModel ) )({ + var overlaid = new ( this._getCollectionPanelClass( collectionModel ) )({ model : collectionModel, HDAViewClass : this.HDAViewClass, parentName : this.model.get( 'name' ), linkTarget : this.linkTarget }); - historyView.panelStack.push( panel ); + panel.panelStack.push( overlaid ); - historyView.$( '.history-controls' ).add( '.datasets-list' ).hide(); - historyView.$el.append( panel.$el ); - panel.on( 'close', function(){ - historyView.render(); + panel.$( '.controls' ).add( panel.$list() ).hide(); + panel.$el.append( overlaid.$el ); + overlaid.on( 'close', function(){ + panel.render(); collectionView.collapse(); - historyView.panelStack.pop(); + panel.panelStack.pop(); }); //TODO: to hdca-model, hasDetails - if( !panel.model.hasDetails() ){ - var xhr = panel.model.fetch(); + if( !overlaid.model.hasDetails() ){ + var xhr = overlaid.model.fetch(); xhr.done( function(){ //this.debug( 'collection data fetched' ); - //this.debug( JSON.stringify( panel.model.toJSON(), null, ' ' ) ); + //this.debug( JSON.stringify( overlaid.model.toJSON(), null, ' ' ) ); //TODO: (re-)render collection contents - panel.render(); + overlaid.render(); }); } else { - panel.render(); + overlaid.render(); } }, @@ -359,18 +307,43 @@ var CurrentHistoryPanel = _super.extend( throw new TypeError( 'Uknown collection_type: ' + model.get( 'collection_type' ) ); }, - /** In this override, check for any overlaid panels first - * @param {HistoryDatasetAssociation} hda - */ - addContentView : function( hda ){ + /** In this override, check for any overlaid panels first */ + addItemView : function( model, collection, options ){ + this.log( this + '.addItemView:', model ); + if( !this._filterItem( model ) ){ return undefined; } + + var panel = this; + // don't show/add if there are panels overlaid on this one if( this.panelStack.length ){ - return this; + return panel; } - return _super.prototype.addContentView.call( this, hda ); + + // current history item order is reversed - use unshift and prepend + var view = panel._createItemView( model ); + this.views.unshift( view ); + panel.scrollToTop(); + + $({}).queue([ + function fadeOutEmptyMsg( next ){ + var $emptyMsg = panel.$emptyMessage(); + if( $emptyMsg.is( ':visible' ) ){ + $emptyMsg.fadeOut( panel.fxSpeed, next ); + } else { + next(); + } + }, + function createAndPrepend( next ){ + //view.render().$el.hide(); + panel.scrollToTop(); + panel.$list().prepend( view.render( 0 ).$el.hide() ); + view.$el.slideDown( panel.fxSpeed ); + } + ]); + return view; }, // ........................................................................ external objects/MVC - //TODO: remove quota meter from panel and remove this +//TODO: remove quota meter from panel and remove this /** add listeners to an external quota meter (mvc/user/user-quotameter.js) */ connectToQuotaMeter : function( quotaMeter ){ if( !quotaMeter ){ @@ -431,6 +404,22 @@ var CurrentHistoryPanel = _super.extend( }); +//------------------------------------------------------------------------------ TEMPLATES +CurrentHistoryPanel.prototype.templates = (function(){ + + var quotaMsgTemplate = BASE_MVC.wrapTemplate([ + '
', + _l( 'You are over your disk quota' ), '. ', + _l( 'Tool execution is on hold until your disk usage drops below your allocated quota' ), '.', + '
' + ], 'history' ); + return _.extend( _.clone( _super.prototype.templates ), { + quotaMsg : quotaMsgTemplate + }); + +}()); + + //============================================================================== return { CurrentHistoryPanel : CurrentHistoryPanel diff --git a/static/scripts/mvc/history/history-panel-edit.js b/static/scripts/mvc/history/history-panel-edit.js index 304c1a56dc2..ad561d548c9 100644 --- a/static/scripts/mvc/history/history-panel-edit.js +++ b/static/scripts/mvc/history/history-panel-edit.js @@ -23,24 +23,18 @@ define([ TODO: ============================================================================= */ -var _super = HPANEL.ReadOnlyHistoryPanel; +var _super = HPANEL.HistoryPanel; // base class for current-history-panel and used as-is in history/view.mako /** @class Editable View/Controller for the history model. - * @name HistoryPanel * * Allows: - * (everything ReadOnlyHistoryPanel allows) + * (everything HistoryPanel allows) * changing the name * displaying and editing tags and annotations - * multi-selection and operations on mulitple hdas - * - * @augments Backbone.View - * @borrows LoggableMixin#logger as #logger - * @borrows LoggableMixin#log as #log - * @constructs + * multi-selection and operations on mulitple content items */ -var HistoryPanel = _super.extend( -/** @lends HistoryPanel.prototype */{ +var HistoryPanelEdit = _super.extend( +/** @lends HistoryPanelEdit.prototype */{ /** logger used to record this.log messages, commonly set to console */ //logger : console, @@ -56,13 +50,9 @@ var HistoryPanel = _super.extend( */ initialize : function( attributes ){ attributes = attributes || {}; + _super.prototype.initialize.call( this, attributes ); // ---- set up instance vars - /** selected hda ids */ - this.selectedHdaIds = []; - /** last selected hda */ - this.lastSelectedViewId = null; - /** editor for tags - sub-view */ this.tagsEditor = null; /** editor for annotations - sub-view */ @@ -73,52 +63,44 @@ var HistoryPanel = _super.extend( // states/modes the panel can be in /** is the panel currently showing the dataset selection controls? */ - this.selecting = attributes.selecting || false; this.annotationEditorShown = attributes.annotationEditorShown || false; this.tagsEditorShown = attributes.tagsEditorShown || false; - _super.prototype.initialize.call( this, attributes ); + this.multiselectActions = attributes.multiselectActions || this._getActions(); }, // ------------------------------------------------------------------------ panel rendering - /** listening for history and HDA events */ - _setUpModelEventHandlers : function(){ - _super.prototype._setUpModelEventHandlers.call( this ); + /** listening for collection events */ + _setUpCollectionListeners : function(){ + _super.prototype._setUpCollectionListeners.call( this ); - this.model.on( 'change:nice_size', this.updateHistoryDiskSize, this ); - - this.model.contents.on( 'change:deleted', this._handleHdaDeletionChange, this ); - this.model.contents.on( 'change:visible', this._handleHdaVisibleChange, this ); - this.model.contents.on( 'change:purged', function( hda ){ - // hafta get the new nice-size w/o the purged hda + this.collection.on( 'change:deleted', this._handleHdaDeletionChange, this ); + this.collection.on( 'change:visible', this._handleHdaVisibleChange, this ); + this.collection.on( 'change:purged', function( model ){ + // hafta get the new nice-size w/o the purged model this.model.fetch(); }, this ); + return this; + }, + + /** listening for history and HDA events */ + _setUpModelListeners : function(){ + _super.prototype._setUpModelListeners.call( this ); + this.model.on( 'change:nice_size', this.updateHistoryDiskSize, this ); + return this; }, // ------------------------------------------------------------------------ panel rendering - /** 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 = $( '
' ); + /** In this override, add tag and annotation editors and a btn to toggle the selectors */ + _buildNewRender : function(){ + // create a new render using a skeleton template, render title buttons, render body, and set up events, etc. + var $newRender = _super.prototype._buildNewRender.call( this ); + if( !this.model ){ return $newRender; } - $newRender.append( HistoryPanel.templates.historyPanel( this.model.toJSON() ) ); - this.$emptyMessage( $newRender ).text( this.emptyMsg ); if( Galaxy && Galaxy.currUser && Galaxy.currUser.id && Galaxy.currUser.id === this.model.get( 'user_id' ) ){ this._renderTags( $newRender ); this._renderAnnotation( $newRender ); } - // search and select available to both anon/logged-in users - $newRender.find( '.history-secondary-actions' ).prepend( this._renderSelectButton() ); - $newRender.find( '.history-dataset-actions' ).toggle( this.selecting ); - $newRender.find( '.history-secondary-actions' ).prepend( this._renderSearchButton() ); - - this._setUpBehaviours( $newRender ); - - // render hda views (if any and any shown (show_deleted/hidden) - this.renderHdas( $newRender ); return $newRender; }, @@ -127,9 +109,9 @@ var HistoryPanel = _super.extend( var panel = this; this.tagsEditor = new TAGS.TagsEditor({ model : this.model, - el : $where.find( '.history-controls .tags-display' ), + el : $where.find( '.controls .tags-display' ), onshowFirstTime : function(){ this.render(); }, - // show hide hda view tag editors when this is shown/hidden + // show hide sub-view tag editors when this is shown/hidden onshow : function(){ panel.toggleHDATagEditors( true, panel.fxSpeed ); }, @@ -140,7 +122,7 @@ var HistoryPanel = _super.extend( title : _l( 'Edit history tags' ), classes : 'history-tag-btn', faIcon : 'fa-tags' - }).appendTo( $where.find( '.history-secondary-actions' ) ) + }).appendTo( $where.find( '.controls .actions' ) ) }); }, /** render the annotation sub-view controller */ @@ -148,9 +130,9 @@ var HistoryPanel = _super.extend( var panel = this; this.annotationEditor = new ANNOTATIONS.AnnotationEditor({ model : this.model, - el : $where.find( '.history-controls .annotation-display' ), + el : $where.find( '.controls .annotation-display' ), onshowFirstTime : function(){ this.render(); }, - // show hide hda view annotation editors when this is shown/hidden + // show hide sub-view view annotation editors when this is shown/hidden onshow : function(){ panel.toggleHDAAnnotationEditors( true, panel.fxSpeed ); }, @@ -161,33 +143,22 @@ var HistoryPanel = _super.extend( title : _l( 'Edit history annotation' ), classes : 'history-annotate-btn', faIcon : 'fa-comment' - }).appendTo( $where.find( '.history-secondary-actions' ) ) + }).appendTo( $where.find( '.controls .actions' ) ) }); }, - /** button for starting select mode */ - _renderSelectButton : function( $where ){ - return faIconButton({ - title : _l( 'Operations on multiple datasets' ), - classes : 'history-select-btn', - faIcon : 'fa-check-square-o' - }); - }, - - /** Set up HistoryPanel js/widget behaviours + /** Set up HistoryPanelEdit js/widget behaviours * In this override, add the multi select popup menu and make the name editable */ - _setUpBehaviours : function( $where ){ + _setUpBehaviors : function( $where ){ $where = $where || this.$el; - _super.prototype._setUpBehaviours.call( this, $where ); - - // anon users shouldn't have access to any of the following - if( !this.model ){ - return; - } + _super.prototype._setUpBehaviors.call( this, $where ); + if( !this.model ){ return; } // set up the pupup for actions available when multi selecting - this.actionsPopup = this._setUpDatasetActionsPopup( $where ); + if( this.multiselectActions.length ){ + this.actionsPopup = new PopupMenu( $where.find( '.list-action-popup-btn' ), this.multiselectActions ); + } // anon users shouldn't have access to any of the following if( ( !Galaxy.currUser || Galaxy.currUser.isAnonymous() ) @@ -195,20 +166,22 @@ var HistoryPanel = _super.extend( return; } - var panel = this; - $where.find( '.history-name' ) - .attr( 'title', _l( 'Click to rename history' ) ).tooltip({ placement: 'bottom' }) + var panel = this, + nameSelector = '.controls .name'; + $where.find( nameSelector ) + .attr( 'title', _l( 'Click to rename history' ) ) + .tooltip({ placement: 'bottom' }) .make_text_editable({ on_finish: function( newName ){ var previousName = panel.model.get( 'name' ); if( newName && newName !== previousName ){ - panel.$el.find( '.history-name' ).text( newName ); + panel.$el.find( nameSelector ).text( newName ); panel.model.save({ name: newName }) .fail( function(){ - panel.$el.find( '.history-name' ).text( panel.model.previous( 'name' ) ); + panel.$el.find( nameSelector ).text( panel.model.previous( 'name' ) ); }); } else { - panel.$el.find( '.history-name' ).text( previousName ); + panel.$el.find( nameSelector ).text( previousName ); } } }); @@ -217,61 +190,62 @@ var HistoryPanel = _super.extend( /** return a new popup menu for choosing a multi selection action * ajax calls made for multiple datasets are queued */ - _setUpDatasetActionsPopup : function( $where ){ + _getActions : function(){ var panel = this, actions = [ { html: _l( 'Hide datasets' ), func: function(){ var action = HDA_MODEL.HistoryDatasetAssociation.prototype.hide; - panel.getSelectedHdaCollection().ajaxQueue( action ); + panel.getSelectedModels().ajaxQueue( action ); } }, { html: _l( 'Unhide datasets' ), func: function(){ var action = HDA_MODEL.HistoryDatasetAssociation.prototype.unhide; - panel.getSelectedHdaCollection().ajaxQueue( action ); + panel.getSelectedModels().ajaxQueue( action ); } }, { html: _l( 'Delete datasets' ), func: function(){ var action = HDA_MODEL.HistoryDatasetAssociation.prototype['delete']; - panel.getSelectedHdaCollection().ajaxQueue( action ); + panel.getSelectedModels().ajaxQueue( action ); } }, { html: _l( 'Undelete datasets' ), func: function(){ var action = HDA_MODEL.HistoryDatasetAssociation.prototype.undelete; - panel.getSelectedHdaCollection().ajaxQueue( action ); + panel.getSelectedModels().ajaxQueue( action ); } - } ]; + } + ]; if( panel.purgeAllowed ){ actions.push({ html: _l( 'Permanently delete datasets' ), func: function(){ if( confirm( _l( 'This will permanently remove the data in your datasets. Are you sure?' ) ) ){ var action = HDA_MODEL.HistoryDatasetAssociation.prototype.purge; - panel.getSelectedHdaCollection().ajaxQueue( action ); + panel.getSelectedModels().ajaxQueue( action ); } } }); } actions.push( { - html: _l( 'Build Dataset List (Experimental)' ), func: function() { - panel.getSelectedHdaCollection().promoteToHistoryDatasetCollection( panel.model, "list" ); + html: _l( 'Build Dataset List' ), func: function() { + panel.getSelectedModels().promoteToHistoryDatasetCollection( panel.model, "list" ); } }); actions.push( { // TODO: Only show quick pair if two things selected. - html: _l( 'Build Dataset Pair (Experimental)' ), func: function() { - panel.getSelectedHdaCollection().promoteToHistoryDatasetCollection( panel.model, "paired" ); + html: _l( 'Build Dataset Pair' ), func: function() { + panel.getSelectedModels().promoteToHistoryDatasetCollection( panel.model, "paired" ); } }); actions.push( { // TODO: Only show quick pair if two things selected. - html: _l( 'Build List of Dataset Pairs (Experimental)' ), + html: _l( 'Build List of Dataset Pairs' ), func: _.bind( panel._showPairedCollectionModal, panel ) }); - return new PopupMenu( $where.find( '.history-dataset-action-popup-btn' ), actions ); + return actions; }, _showPairedCollectionModal : function(){ var panel = this, - datasets = panel.getSelectedHdaCollection().toJSON().filter( function( content ){ + datasets = panel.getSelectedModels().toJSON().filter( function( content ){ return content.history_content_type === 'dataset' && content.state === STATES.OK; }); @@ -291,113 +265,53 @@ var HistoryPanel = _super.extend( } }, - // ------------------------------------------------------------------------ hda sub-views - /** If this hda is deleted and we're not showing deleted hdas, remove the view - * @param {HistoryDataAssociation} the hda to check - */ - _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 - }, - - - /** If this hda is hidden and we're not showing hidden hdas, remove the view - * @param {HistoryDataAssociation} the hda to check - */ - _handleHdaVisibleChange : function( hda ){ - if( hda.hidden() && !this.storage.get( 'show_hidden' ) ){ - this.removeHdaView( this.hdaViews[ hda.id ] ); - } // otherwise, the hdaView rendering should handle it - }, - - _getContentOptions : function( content ){ - var options = _super.prototype._getContentOptions.call( this, content ); + // ------------------------------------------------------------------------ sub-views + /** In this override, add purgeAllowed and whether tags/annotation editors should be shown */ + _getItemViewOptions : function( model ){ + var options = _super.prototype._getItemViewOptions.call( this, model ); _.extend( options, { - selectable : this.selecting, purgeAllowed : this.purgeAllowed, +//TODO: not working tagsEditorShown : ( this.tagsEditor && !this.tagsEditor.hidden ), annotationEditorShown : ( this.annotationEditor && !this.annotationEditor.hidden ) }); return options; }, - /** Set up HistoryPanel listeners for HDAView events. Currently binds: - * HDAView#body-visible, HDAView#body-hidden to store expanded states - * @param {HDAView} hdaView HDAView (base or edit) to listen to + /** If this item is deleted and we're not showing deleted items, remove the view + * @param {Model} the item model to check */ - _setUpHdaListeners : function( hdaView ){ - var historyView = this; - _super.prototype._setUpHdaListeners.call( this, hdaView ); - - // maintain a list of hdas that are selected - hdaView.on( 'selected', function( hdaView, event ){ - if( !event ){ return; } - //this.debug( 'selected', event ); - var selectedIds = []; - //this.debug( historyView.lastSelectedViewId, historyView.hdaViews[ historyView.lastSelectedViewId ] ); - - // shift will select a range, but not set lastSelectedViewId - if( ( event.shiftKey ) - && ( historyView.lastSelectedViewId && _.has( historyView.hdaViews, historyView.lastSelectedViewId ) ) ){ - var lastSelectedView = historyView.hdaViews[ historyView.lastSelectedViewId ]; - selectedIds = historyView.selectDatasetRange( hdaView, lastSelectedView ) - .map( function( view ){ return view.model.id; }); - - // only - } else { - var id = hdaView.model.id; - historyView.lastSelectedViewId = id; - //this.debug( 'lastSelectedViewId:', historyView.lastSelectedViewId ); - selectedIds = [ id ]; - } - //this.debug( 'selectedIds:', selectedIds ); - historyView.selectedHdaIds = _.union( historyView.selectedHdaIds, selectedIds ); - //TODO: might want to use getSelectedHdaViews instead managing these lists with ops - //this.debug( 'selected', historyView.selectedHdaIds ); - }); - hdaView.on( 'de-selected', function( hdaView, event ){ - //this.debug( 'de-selected', event ); - var id = hdaView.model.id; - historyView.selectedHdaIds = _.without( historyView.selectedHdaIds, id ); - //this.debug( 'de-selected', historyView.selectedHdaIds ); - }); + _handleHdaDeletionChange : function( itemModel ){ + if( itemModel.get( 'deleted' ) && !this.storage.get( 'show_deleted' ) ){ + this.removeItemView( itemModel ); + } }, - /** toggle the visibility of each hdaView's tagsEditor applying all the args sent to this function */ + /** If this item is hidden and we're not showing hidden items, remove the view + * @param {Model} the item model to check + */ + _handleHdaVisibleChange : function( itemModel ){ + if( itemModel.hidden() && !this.storage.get( 'show_hidden' ) ){ + this.removeItemView( itemModel ); + } + }, + + /** toggle the visibility of each content's tagsEditor applying all the args sent to this function */ toggleHDATagEditors : function( showOrHide ){ - var args = arguments; - _.each( this.hdaViews, function( hdaView ){ - if( hdaView.tagsEditor ){ - hdaView.tagsEditor.toggle.apply( hdaView.tagsEditor, args ); + var args = Array.prototype.slice.call( arguments, 1 ); + _.each( this.views, function( view ){ + if( view.tagsEditor ){ + view.tagsEditor.toggle.apply( view.tagsEditor, args ); } }); }, - /** toggle the visibility of each hdaView's annotationEditor applying all the args sent to this function */ + /** toggle the visibility of each content's annotationEditor applying all the args sent to this function */ toggleHDAAnnotationEditors : function( showOrHide ){ - var args = arguments; - _.each( this.hdaViews, function( hdaView ){ - if( hdaView.annotationEditor ){ - hdaView.annotationEditor.toggle.apply( hdaView.annotationEditor, args ); - } - }); - }, - - /** Remove a view from the panel and if the panel is now empty, re-render - * @param {Int} the id of the hdaView to remove - */ - removeHdaView : function( hdaView ){ - if( !hdaView ){ return; } - - var panel = this; - hdaView.$el.fadeOut( panel.fxSpeed, function(){ - hdaView.off(); - hdaView.remove(); - delete panel.hdaViews[ hdaView.model.id ]; - if( _.isEmpty( panel.hdaViews ) ){ - panel.trigger( 'empty-history', panel ); - panel._renderEmptyMsg(); + var args = Array.prototype.slice.call( arguments, 1 ); + _.each( this.views, function( view ){ + if( view.annotationEditor ){ + view.annotationEditor.toggle.apply( view.annotationEditor, args ); } }); }, @@ -405,9 +319,7 @@ var HistoryPanel = _super.extend( // ------------------------------------------------------------------------ panel events /** event map */ events : _.extend( _.clone( _super.prototype.events ), { - 'click .history-select-btn' : 'toggleSelectors', - 'click .history-select-all-datasets-btn' : 'selectAllDatasets', - 'click .history-deselect-all-datasets-btn' : 'deselectAllDatasets' + 'click .show-selectors-btn' : 'toggleSelectors' }), /** Update the history size display (curr. upper right of panel). @@ -416,88 +328,15 @@ var HistoryPanel = _super.extend( this.$el.find( '.history-size' ).text( this.model.get( 'nice_size' ) ); }, - // ........................................................................ multi-select of hdas -//TODO: to toggle showOrHide pattern - /** show selectors on all visible hdas and associated controls */ - showSelectors : function( speed ){ - speed = ( speed !== undefined )?( speed ):( this.fxSpeed ); - this.selecting = true; - this.$( '.history-dataset-actions' ).slideDown( speed ); - _.each( this.hdaViews, function( view ){ - view.showSelector(); - }); - this.selectedHdaIds = []; - this.lastSelectedViewId = null; - }, - - /** hide selectors on all visible hdas and associated controls */ - hideSelectors : function( speed ){ - speed = ( speed !== undefined )?( speed ):( this.fxSpeed ); - this.selecting = false; - this.$( '.history-dataset-actions' ).slideUp( speed ); - _.each( this.hdaViews, function( view ){ - view.hideSelector(); - }); - this.selectedHdaIds = []; - this.lastSelectedViewId = null; - }, - - /** show or hide selectors on all visible hdas and associated controls */ - toggleSelectors : function(){ - if( !this.selecting ){ - this.showSelectors(); - } else { - this.hideSelectors(); - } - }, - - /** select all visible hdas */ - selectAllDatasets : function( event ){ - _.each( this.hdaViews, function( view ){ - view.select( event ); - }); - }, - - /** deselect all visible hdas */ - deselectAllDatasets : function( event ){ - this.lastSelectedViewId = null; - _.each( this.hdaViews, function( view ){ - view.deselect( event ); - }); - }, - - /** select a range of datasets between A and B */ - selectDatasetRange : function( viewA, viewB ){ - var range = this.hdaViewRange( viewA, viewB ); - _.each( range, function( view ){ - view.select(); - }); - return range; - }, - - /** return an array of all currently selected hdas */ - getSelectedHdaViews : function(){ - return _.filter( this.hdaViews, function( v ){ - return v.selected; - }); - }, - - /** return an HdaCollection of the models of all currenly selected hdas */ - getSelectedHdaCollection : function(){ - return new HISTORY_CONTENTS.HistoryContents( _.map( this.getSelectedHdaViews(), function( hdaView ){ - return hdaView.model; - }), { historyId: this.model.id }); - }, - // ........................................................................ misc /** Return a string rep of the history */ toString : function(){ - return 'HistoryPanel(' + (( this.model )?( this.model.get( 'name' )):( '' )) + ')'; + return 'HistoryPanelEdit(' + (( this.model )?( this.model.get( 'name' )):( '' )) + ')'; } }); //============================================================================== return { - HistoryPanel : HistoryPanel + HistoryPanelEdit : HistoryPanelEdit }; }); diff --git a/static/scripts/mvc/history/history-panel.js b/static/scripts/mvc/history/history-panel.js index e1f0ca195c4..e2482b1e525 100644 --- a/static/scripts/mvc/history/history-panel.js +++ b/static/scripts/mvc/history/history-panel.js @@ -1,19 +1,21 @@ define([ + "mvc/list/list-panel", "mvc/history/history-model", + "mvc/history/history-contents", "mvc/history/hda-li", "mvc/history/hdca-li", "mvc/user/user-model", "mvc/base-mvc", "utils/localization" -], function( HISTORY_MODEL, HDA_LI, HDCA_LI, USER, BASE_MVC, _l ){ +], function( LIST_PANEL, HISTORY_MODEL, HISTORY_CONTENTS, HDA_LI, HDCA_LI, USER, BASE_MVC, _l ){ // ============================================================================ /** session storage for individual history preferences */ var HistoryPrefs = BASE_MVC.SessionStorageModel.extend( /** @lends HistoryPrefs.prototype */{ - +//TODO:?? possibly mark as current T/F - have History.currId() (a class method) return that value defaults : { -//TODO:?? expandedHdas to array? - expandedHdas : {}, +//TODO:?? expandedIds to array? + expandedIds : {}, //TODO:?? move to user? show_deleted : false, show_hidden : false @@ -21,12 +23,13 @@ var HistoryPrefs = BASE_MVC.SessionStorageModel.extend( }, /** add an hda id to the hash of expanded hdas */ addExpandedHda : function( model ){ - var key = 'expandedHdas'; + var key = 'expandedIds'; +//TODO:?? is this right anymore? this.save( key, _.extend( this.get( key ), _.object([ model.id ], [ model.get( 'id' ) ]) ) ); }, /** remove an hda id from the hash of expanded hdas */ removeExpandedHda : function( model ){ - var key = 'expandedHdas'; + var key = 'expandedIds'; this.save( key, _.omit( this.get( key ), model.id ) ); }, toString : function(){ @@ -62,137 +65,348 @@ HistoryPrefs.clearAll = function clearAll( historyId ){ TODO: ============================================================================= */ -// base model and used as-is in history/view.mako /** @class non-editable, read-only View/Controller for a history model. - * @name HistoryPanel - * * Allows: * changing the loaded history - * searching hdas * displaying data, info, and download + * tracking history attrs: size, tags, annotations, name, etc. * Does not allow: * changing the name - * - * @augments Backbone.View - * @borrows LoggableMixin#logger as #logger - * @borrows LoggableMixin#log as #log - * @constructs */ -var ReadOnlyHistoryPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( -/** @lends ReadOnlyHistoryPanel.prototype */{ +var _super = LIST_PANEL.ModelListPanel; +var HistoryPanel = _super.extend( +/** @lends HistoryPanel.prototype */{ /** 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 */ HDAViewClass : HDA_LI.HDAListItemView, /** class to use for constructing the HDCA views */ HDCAViewClass : HDCA_LI.HDCAListItemView, + /** class to used for constructing collection of sub-view models */ + collectionClass : HISTORY_CONTENTS.HistoryContents, + /** key of attribute in model to assign to this.collection */ + modelCollectionKey : 'contents', tagName : 'div', - className : 'history-panel', + className : _super.prototype.className + ' history-panel', - /** (in ms) that jquery effects will use */ - fxSpeed : 'fast', - - /** string to display when the model has no hdas */ + /** string to display when the collection is empty */ emptyMsg : _l( 'This history is empty' ), - /** string to no hdas match the search terms */ + /** displayed when no items match the search terms */ noneFoundMsg : _l( 'No matching datasets found' ), + /** string used for search placeholder */ + searchPlaceholder : _l( 'search datasets' ), // ......................................................................... SET UP - /** Set up the view, set up storage, bind listeners to HistoryContents events + /** Set up the view, bind listeners. * @param {Object} attributes optional settings for the panel */ initialize : function( attributes ){ - attributes = attributes || {}; - // set the logger if requested - if( attributes.logger ){ - this.logger = attributes.logger; - } - this.log( this + '.initialize:', attributes ); - + _super.prototype.initialize.call( this, attributes ); // ---- 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 ); - - // ----- set up panel listeners, handle models passed on init, and call any ready functions - this._setUpListeners(); - // 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? - if( attributes.onready ){ - attributes.onready.call( this ); - } }, - /** create any event listeners for the panel - * @fires: rendered:initial on the first render - * @fires: empty-history when switching to a history with no HDAs or creating a new history - */ - _setUpListeners : function(){ - 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.on( 'loading-done', function(){ - // ...hiding it again when loading is done (or there's been an error) - 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) - this.once( 'rendered', function(){ - this.trigger( 'rendered:initial', this ); - return false; - }); - - // debugging - if( this.logger ){ - this.on( 'all', function( event ){ - this.log( this + '', arguments ); - }, this ); + /** In this override, clear the update timer on the model */ + freeModel : function(){ + _super.prototype.freeModel.call( this ); +//TODO: move to History.free() + if( this.model ){ + this.model.clearUpdateTimeout(); } return this; }, - //TODO: see base-mvc - //onFree : function(){ - // _.each( this.hdaViews, function( view, modelId ){ - // view.free(); - // }); - // this.hdaViews = null; - //}, + /** create any event listeners for the panel + * @fires: rendered:initial on the first render + * @fires: empty-history when switching to a history with no contents or creating a new history + */ + _setUpListeners : function(){ + _super.prototype._setUpListeners.call( this ); + + this.on( 'error', function( model, xhr, options, msg, details ){ + this.errorHandler( model, xhr, options, msg, details ); + }); + + this.on( 'loading-done', function(){ + //TODO:?? if( this.collection.length ){ + if( !this.views.length ){ + this.trigger( 'empty-history', this ); + } + }); + }, + + // ------------------------------------------------------------------------ loading history/hda models + //NOTE: all the following fns replace the existing history model with a new model + // (in the following 'details' refers to the full set of contents api data (urls, display_apps, misc_info, etc.) + // - contents w/o details will have summary data only (name, hid, deleted, visible, state, etc.)) +//TODO: too tangled... + + /** loads a history & contents, getting details of any contents whose ids are stored in sessionStorage + * (but does not make them the current history) + */ + loadHistoryWithDetails : function( historyId, attributes, historyFn, contentsFn ){ + this.info( 'loadHistoryWithDetails:', historyId, attributes, historyFn, contentsFn ); + var detailIdsFn = function( historyData ){ + // will be called to get content ids that need details from the api +//TODO:! non-visible contents are getting details loaded... either stop loading them at all or filter ids thru isVisible + return _.values( HistoryPrefs.get( historyData.id ).get( 'expandedIds' ) ); + }; + return this.loadHistory( historyId, attributes, historyFn, contentsFn, detailIdsFn ); + }, + + /** loads a history & contents (but does not make them the current history) */ + loadHistory : function( historyId, attributes, historyFn, contentsFn, detailIdsFn ){ + this.info( 'loadHistory:', historyId, attributes, historyFn, contentsFn, detailIdsFn ); + var panel = this; + attributes = attributes || {}; + + panel.trigger( 'loading', panel ); + //this.info( 'loadHistory:', historyId, attributes, historyFn, contentsFn, detailIdsFn ); + var xhr = HISTORY_MODEL.History.getHistoryData( historyId, { + historyFn : historyFn, + contentsFn : contentsFn, + detailIdsFn : attributes.initiallyExpanded || detailIdsFn + }); + + 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 + panel.trigger( 'loading-done', panel ); + }); + }, + + /** given an xhr that will provide both history and contents data, pass data to set model or handle xhr errors */ + _loadHistoryFromXHR : function( xhr, attributes ){ + var panel = this; + xhr.then( function( historyJSON, contentsJSON ){ + panel.JSONToModel( historyJSON, contentsJSON, attributes ); + panel.render(); + }); + xhr.fail( function( xhr, where ){ + // render anyways - whether we get a model or not + panel.render(); + }); + return xhr; + }, + +//TODO:?? seems unneccesary +//TODO: Maybe better in History? + /** create a new history model from JSON and call setModel on it */ + JSONToModel : function( newHistoryJSON, newHdaJSON, attributes ){ + this.log( 'JSONToModel:', newHistoryJSON, newHdaJSON, attributes ); + attributes = attributes || {}; + //this.log( 'JSONToModel:', newHistoryJSON, newHdaJSON.length, attributes ); + + var model = new HISTORY_MODEL.History( newHistoryJSON, newHdaJSON, attributes ); +//TODO:?? here? + this.setModel( model ); + return model; + }, + + /** release/free/shutdown old models and set up panel for new models + * @fires new-model with the panel as parameter + */ + setModel : function( model, attributes ){ + attributes = attributes || {}; + _super.prototype.setModel.call( this, model, attributes ); + if( this.model ){ + this._setUpWebStorage( attributes.initiallyExpanded, attributes.show_deleted, attributes.show_hidden ); + } + }, + + // ------------------------------------------------------------------------ browser stored prefs + /** Set up client side storage. Currently PersistanStorage keyed under 'HistoryPanel.' + * @param {Object} initiallyExpanded + * @param {Boolean} show_deleted whether to show deleted contents (overrides stored) + * @param {Boolean} show_hidden + * @see PersistentStorage + */ + _setUpWebStorage : function( initiallyExpanded, show_deleted, show_hidden ){ + //if( !this.model ){ return this; } + //this.log( '_setUpWebStorage', initiallyExpanded, show_deleted, show_hidden ); + this.storage = new HistoryPrefs({ + id: HistoryPrefs.historyStorageKey( this.model.get( 'id' ) ) + }); + + // expandedIds is a map of content.ids -> a boolean repr'ing whether that item's body is already expanded + // store any pre-expanded ids passed in + if( _.isObject( initiallyExpanded ) ){ + this.storage.set( 'expandedIds', initiallyExpanded ); + } + + // get the show_deleted/hidden settings giving priority to values passed in, using web storage otherwise + // if the page has specifically requested show_deleted/hidden, these will be either true or false + // (as opposed to undefined, null) - and we give priority to that setting + if( _.isBoolean( show_deleted ) ){ + this.storage.set( 'show_deleted', show_deleted ); + } + if( _.isBoolean( show_hidden ) ){ + this.storage.set( 'show_hidden', show_hidden ); + } + + this.trigger( 'new-storage', this.storage, this ); + this.log( this + ' (init\'d) storage:', this.storage.get() ); + return this; + }, + + // ------------------------------------------------------------------------ panel rendering + /** In this override, add a btn to toggle the selectors */ + _buildNewRender : function(){ + var $newRender = _super.prototype._buildNewRender.call( this ); + if( this.multiselectActions.length ){ + $newRender.find( '.controls .actions' ).prepend( this._renderSelectButton() ); + } + return $newRender; + }, + + /** button for starting select mode */ + _renderSelectButton : function( $where ){ + return faIconButton({ + title : _l( 'Operations on multiple datasets' ), + classes : 'show-selectors-btn', + faIcon : 'fa-check-square-o' + }); + }, + + // ------------------------------------------------------------------------ sub-views + /** In this override, since history contents are mixed, + * get the appropo view class based on history_content_type + */ + _getItemViewClass : function( model ){ + var contentType = model.get( "history_content_type" ); + switch( contentType ){ + case 'dataset': + return this.HDAViewClass; + case 'dataset_collection': + return this.HDCAViewClass; + } + throw new TypeError( 'Unknown history_content_type: ' + contentType ); + }, + + /** in this override, check if the contents would also display based on show_deleted/hidden */ + _filterItem : function( model ){ + var panel = this; + return ( _super.prototype._filterItem.call( panel, model ) + && ( !model.hidden() || panel.storage.get( 'show_hidden' ) ) + && ( !model.isDeletedOrPurged() || panel.storage.get( 'show_deleted' ) ) ); + }, + + /** in this override, add a linktarget, and expand if id is in web storage */ + _getItemViewOptions : function( model ){ + var options = _super.prototype._getItemViewOptions.call( this, model ); + return _.extend( options, { + linkTarget : this.linkTarget, + expanded : !!this.storage.get( 'expandedIds' )[ model.id ], + hasUser : this.model.ownedByCurrUser() + }); + }, + + /** In this override, add/remove expanded/collapsed model ids to/from web storage */ + _setUpItemViewListeners : function( view ){ + var panel = this; + _super.prototype._setUpItemViewListeners.call( panel, view ); + + //TODO:?? could use 'view:expanded' here? + // maintain a list of items whose bodies are expanded + view.on( 'expanded', function( v ){ + panel.storage.addExpandedHda( v.model ); + }); + view.on( 'collapsed', function( v ){ + panel.storage.removeExpandedHda( v.model ); + }); + return this; + }, + + // ------------------------------------------------------------------------ collection/views syncing + /** convenience alias to the model. Updates the item list only (not the history) */ + refreshContents : function( detailIds, options ){ + if( this.model ){ + return this.model.refresh( detailIds, options ); + } + // may have callbacks - so return an empty promise + return $.when(); + }, + + // ------------------------------------------------------------------------ selection + /** Override to correctly set the historyId of the new collection */ + getSelectedModels : function(){ + var collection = _super.prototype.getSelectedModels.call( this ); + collection.historyId = this.collection.historyId; + return collection; + }, + + // ------------------------------------------------------------------------ panel events + /** event map */ + events : _.extend( _.clone( _super.prototype.events ), { + // toggle list item selectors + 'click .show-selectors-btn' : 'toggleSelectors' + // allow (error) messages to be clicked away +//TODO: switch to common close (X) idiom + //'click .messages' : 'clearMessages', +//TODO: remove + //'click .history-search-btn' : 'toggleSearchControls' + }), + + /** Handle the user toggling the deleted visibility by: + * (1) storing the new value in the persistent storage + * (2) re-rendering the history + * @returns {Boolean} new show_deleted setting + */ + toggleShowDeleted : function( show ){ + show = ( show !== undefined )?( show ):( !this.storage.get( 'show_deleted' ) ); + this.storage.set( 'show_deleted', show ); + //TODO:?? to events on storage('change:show_deleted') + this.renderItems(); + return this.storage.get( 'show_deleted' ); + }, + + /** Handle the user toggling the deleted visibility by: + * (1) storing the new value in the persistent storage + * (2) re-rendering the history + * @returns {Boolean} new show_hidden setting + */ + toggleShowHidden : function( show ){ + show = ( show !== undefined )?( show ):( !this.storage.get( 'show_hidden' ) ); + this.storage.set( 'show_hidden', show ); + //TODO:?? to events on storage('change:show_hidden') + this.renderItems(); + return this.storage.get( 'show_hidden' ); + }, + + /** On the first search, if there are no details - load them, then search */ + _firstSearch : function( searchFor ){ + var panel = this, + inputSelector = '.history-search-input'; + this.log( 'onFirstSearch', searchFor ); + + if( panel.model.contents.haveDetails() ){ + panel.searchItems( searchFor ); + return; + } + + panel.$el.find( inputSelector ).searchInput( 'toggle-loading' ); + panel.model.contents.fetchAllDetails({ silent: true }) + .always( function(){ + panel.$el.find( inputSelector ).searchInput( 'toggle-loading' ); + }) + .done( function(){ + panel.searchItems( searchFor ); + }); + }, //TODO: break this out // ........................................................................ error handling - /** Event handler for errors (from the panel, the history, or the history's HDAs) + /** Event handler for errors (from the panel, the history, or the history's contents) * @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 @@ -203,8 +417,6 @@ var ReadOnlyHistoryPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend this.error( model, xhr, options, msg, details ); //TODO: getting JSON parse errors from jq migrate - window.xhr = xhr; - // interrupted ajax if( xhr && xhr.status === 0 && xhr.readyState === 0 ){ @@ -231,13 +443,17 @@ var ReadOnlyHistoryPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend * note: see errorHandler for more info on params */ _parseErrorMessage : function( model, xhr, options, msg, details, sentryId ){ + //if( xhr.responseText ){ + // xhr.responseText = _.escape( xhr.responseText ); + //} var user = Galaxy.currUser, // add the args (w/ some extra info) into an obj parsed = { message : this._bePolite( msg ), details : { message : msg, - raven : ( window.Raven )?( Raven.lastEventId() ):( undefined ), + raven : ( window.Raven && _.isFunction( Raven.lastEventId) )? + ( Raven.lastEventId() ):( undefined ), agent : navigator.userAgent, // add ajax data from Galaxy object cache url : ( window.Galaxy )?( Galaxy.lastAjax.url ):( undefined ), @@ -269,670 +485,6 @@ var ReadOnlyHistoryPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend return msg + '. ' + _l( 'Please contact a Galaxy administrator if the problem persists' ) + '.'; }, - // ------------------------------------------------------------------------ loading history/hda models - //NOTE: all the following fns replace the existing history model with a new model - // (in the following 'details' refers to the full set of hda api data (urls, display_apps, misc_info, etc.) - // - hdas w/o details will have summary data only (name, hid, deleted, visible, state, etc.)) -//TODO: too tangled... - - /** loads a history & hdas w/ details (but does not make them the current history) */ - loadHistoryWithHDADetails : function( historyId, attributes, historyFn, hdaFn ){ - this.info( 'loadHistoryWithHDADetails:', historyId, attributes, historyFn, hdaFn ); - 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 _.values( 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.info( 'loadHistory:', historyId, attributes, historyFn, hdaFn, hdaDetailIds ); - var panel = this; - attributes = attributes || {}; - - panel.trigger( 'loading-history', panel ); - //this.info( 'loadHistory:', historyId, attributes, historyFn, hdaFn, hdaDetailIds ); - var xhr = HISTORY_MODEL.History.getHistoryData( historyId, { - historyFn : historyFn, - hdaFn : hdaFn, - hdaDetailIds : attributes.initiallyExpanded || hdaDetailIds - }); - - return panel._loadHistoryFromXHR( xhr, attributes ) - .fail( function( xhr, where, history ){ - // throw an error up for the error handler -//TODO: difficult to localize - use template - 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 - panel.trigger( 'loading-done', panel ); - }); - }, - - /** given an xhr that will provide both history and hda data, pass data to set model or handle xhr errors */ - _loadHistoryFromXHR : function( xhr, attributes ){ - var panel = this; - xhr.then( function( historyJSON, hdaJSON ){ - panel.JSONToModel( historyJSON, hdaJSON, attributes ); - }); - xhr.fail( function( xhr, where ){ - // always render - whether we get a model or not - panel.render(); - }); - return xhr; - }, - - - /** 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 || {}; - //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 HISTORY_MODEL.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(); - this.stopListening( this.model ); - this.stopListening( this.model.contents ); - //TODO: see base-mvc - //this.model.free(); - } - this.freeHdaViews(); - return this; - }, - - /** free any hdaViews the panel has */ - freeHdaViews : function(){ - this.hdaViews = {}; - return this; - }, - - // ------------------------------------------------------------------------ browser stored prefs - /** Set up client side storage. Currently PersistanStorage keyed under 'HistoryPanel.' - * @param {Object} initiallyExpanded - * @param {Boolean} show_deleted whether to show deleted HDAs (overrides stored) - * @param {Boolean} show_hidden - * @see PersistentStorage - */ - _setUpWebStorage : function( initiallyExpanded, show_deleted, show_hidden ){ - //this.log( '_setUpWebStorage', initiallyExpanded, show_deleted, show_hidden ); - this.storage = new HistoryPrefs({ - id: HistoryPrefs.historyStorageKey( this.model.get( 'id' ) ) - }); - - // 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 ) ){ - this.storage.set( 'exandedHdas', initiallyExpanded ); - } - - // get the show_deleted/hidden settings giving priority to values passed in, using web storage otherwise - // if the page has specifically requested show_deleted/hidden, these will be either true or false - // (as opposed to undefined, null) - and we give priority to that setting - if( _.isBoolean( show_deleted ) ){ - this.storage.set( 'show_deleted', show_deleted ); - } - if( _.isBoolean( show_hidden ) ){ - this.storage.set( 'show_hidden', show_hidden ); - } - - this.trigger( 'new-storage', this.storage, this ); - this.log( this + ' (init\'d) storage:', this.storage.get() ); - return this; - }, - - // ------------------------------------------------------------------------ history/hda event listening - /** listening for history and HDA events */ - _setUpModelEventHandlers : function(){ - // ---- hdas - // bind events from the model's hda collection - // note: don't listen to the hdas for errors, history will pass that to us - //this.model.contents.on( 'reset', this.addAll, this ); - this.model.contents.on( 'add', this.addContentView, this ); - - // on a model error - bounce it up to the panel and remove it from the model - this.model.on( 'error error:contents', function( model, xhr, options, msg ){ - this.errorHandler( model, xhr, options, msg ); - }, this ); - return this; - }, - - // ------------------------------------------------------------------------ panel rendering - /** Render urls, historyPanel body, and hdas (if any are shown) - * @fires: rendered when the panel is attached and fully visible - * @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, - $newRender; - - // handle the possibility of no model (can occur if fetching the model returns an error) - if( this.model ){ - $newRender = this.renderModel(); - } else { - $newRender = this.renderWithoutModel(); - } - - // fade out existing, swap with the new, fade in, set up behaviours - $( panel ).queue( 'fx', [ - function( next ){ - if( speed && panel.$el.is( ':visible' ) ){ - panel.$el.fadeOut( speed, next ); - } else { - next(); - } - }, - function( next ){ - // swap over from temp div newRender - panel.$el.empty(); - if( $newRender ){ - panel.$el.append( $newRender.children() ); - } - next(); - }, - function( next ){ - if( speed && !panel.$el.is( ':visible' ) ){ - panel.$el.fadeIn( speed, next ); - } else { - next(); - } - }, - function( next ){ - //TODO: ideally, these would be set up before the fade in (can't because of async save text) - if( callback ){ callback.call( this ); } - panel.trigger( 'rendered', this ); - next(); - } - ]); - return this; - }, - - /** render without history data - * @returns {jQuery} dom fragment with message container only - */ - renderWithoutModel : function( ){ - // we'll always need the message container - var $newRender = $( '
' ), - $msgContainer = $( '
' ).addClass( 'message-container' ) - .css({ 'margin': '4px' }); - return $newRender.append( $msgContainer ); - }, - - /** 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 = $( '
' ); - - // render based on anonymity, set up behaviors - $newRender.append( ReadOnlyHistoryPanel.templates.historyPanel( this.model.toJSON() ) ); - this.$emptyMessage( $newRender ).text( this.emptyMsg ); - // search and select available to both anon/logged-in users - $newRender.find( '.history-secondary-actions' ).prepend( this._renderSearchButton() ); - - this._setUpBehaviours( $newRender ); - - // render hda views (if any and any shown (show_deleted/hidden) - this.renderHdas( $newRender ); - return $newRender; - }, - - /** render the empty/none-found message */ - _renderEmptyMsg : function( $whereTo ){ - var panel = this, - $emptyMsg = panel.$emptyMessage( $whereTo ); - - if( !_.isEmpty( panel.hdaViews ) ){ - $emptyMsg.hide(); - - } else if( panel.searchFor ){ - $emptyMsg.text( panel.noneFoundMsg ).show(); - - } else { - $emptyMsg.text( panel.emptyMsg ).show(); - } - return this; - }, - - /** button for opening search */ - _renderSearchButton : function( $where ){ - return faIconButton({ - title : _l( 'Search datasets' ), - classes : 'history-search-btn', - faIcon : 'fa-search' - }); - }, - - /** Set up HistoryPanel js/widget behaviours */ - _setUpBehaviours : function( $where ){ - //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 - /** 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.contents.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.id, - hdaView = panel._createContentView( hda ); - newHdaViews[ hdaId ] = hdaView; - // persist selection - if( _.contains( panel.selectedHdaIds, hdaId ) ){ - hdaView.selected = true; - } - panel.attachContentView( hdaView.render(), $whereTo ); - }); - } - this.hdaViews = newHdaViews; - this._renderEmptyMsg( $whereTo ); - return this.hdaViews; - }, - - /** Create an HDA view for the given HDA and set up listeners (but leave attachment for addHdaView) - * @param {HistoryDatasetAssociation} hda - */ - _createContentView : function( content ){ - var ContentClass = this._getContentClass( content ), - options = _.extend( this._getContentOptions( content ), { - model : content - }), - contentView = new ContentClass( options ); - this._setUpHdaListeners( contentView ); - return contentView; - }, - - _getContentClass : function( content ){ - var contentType = content.get( "history_content_type" ); - switch( contentType ){ - case 'dataset': - return this.HDAViewClass; - case 'dataset_collection': - return this.HDCAViewClass; - } - throw new TypeError( 'Unknown history_content_type: ' + contentType ); - }, - - _getContentOptions : function( content ){ - return { - linkTarget : this.linkTarget, - expanded : !!this.storage.get( 'expandedHdas' )[ content.id ], - //draggable : true, - hasUser : this.model.ownedByCurrUser(), - logger : this.logger - }; - }, - - /** Set up HistoryPanel listeners for HDAView events. Currently binds: - * HDAView#body-visible, HDAView#body-hidden to store expanded states - * @param {HDAView} hdaView HDAView (base or edit) to listen to - */ - _setUpHdaListeners : function( hdaView ){ - 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( 'expanded', function( view ){ - panel.storage.addExpandedHda( view.model ); - }); - hdaView.on( 'collapsed', function( view ){ - panel.storage.removeExpandedHda( view.model ); - }); - return this; - }, - - /** attach an hdaView to the panel */ - attachContentView : function( hdaView, $whereTo ){ - $whereTo = $whereTo || this.$el; - var $datasetsList = this.$datasetsList( $whereTo ); - $datasetsList.prepend( hdaView.$el ); - return this; - }, - - /** Add an hda view to the panel for the given hda - * @param {HistoryDatasetAssociation} hda - */ - addContentView : function( hda ){ - this.log( 'add.' + this, hda ); - var panel = this; - - // 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 panel; - } - - // create and prepend to current el, if it was empty fadeout the emptyMsg first - $({}).queue([ - function fadeOutEmptyMsg( next ){ - var $emptyMsg = panel.$emptyMessage(); - if( $emptyMsg.is( ':visible' ) ){ - $emptyMsg.fadeOut( panel.fxSpeed, next ); - } else { - next(); - } - }, - function createAndPrepend( next ){ - var hdaView = panel._createContentView( hda ); - panel.hdaViews[ hda.id ] = hdaView; - hdaView.render().$el.hide(); - panel.scrollToTop(); - panel.attachContentView( hdaView ); - hdaView.$el.slideDown( panel.fxSpeed ); - } - ]); - return panel; - }, - -//TODO: removeHdaView? - /** 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 - */ - views : function( at ){ - var panel = this, - visibleHdas = this.model.contents.getVisible( - this.storage.get( 'show_deleted' ), - this.storage.get( 'show_hidden' ), - this.filters - ); - if( at !== undefined ){ - return panel.hdaViews[ visibleHdas.at( at ).id ]; - } - return visibleHdas.map( function( hda ){ - return panel.hdaViews[ hda.id ]; - }); - }, - - /** convenience alias to the model. Updates the hda list only (not the history) */ - refreshContents : function( detailIds, options ){ - if( this.model ){ - return this.model.refresh( detailIds, options ); - } - // may have callbacks - so return an empty promise - 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.contents.length ){ return undefined; } - // var model = this.model.contents.findWhere( terms ); - // return ( model )?( this.hdaViews[ model.id ] ):( undefined ); - //}, - - hdaViewRange : function( viewA, viewB ){ - if( viewA === viewB ){ return [ viewA ]; } - //TODO: would probably be better if we cache'd the views as an ordered list (as well as a map) - var panel = this, - withinSet = false, - set = []; - this.model.contents.getVisible( - this.storage.get( 'show_deleted' ), - this.storage.get( 'show_hidden' ), - this.filters - ).each( function( hda ){ - //panel.debug( 'checking: ', hda.get( 'name' ) ); - if( withinSet ){ - //panel.debug( '\t\t adding: ', hda.get( 'name' ) ); - set.push( panel.hdaViews[ hda.id ] ); - if( hda === viewA.model || hda === viewB.model ){ - //panel.debug( '\t found last: ', hda.get( 'name' ) ); - withinSet = false; - } - } else { - if( hda === viewA.model || hda === viewB.model ){ - //panel.debug( 'found first: ', hda.get( 'name' ) ); - withinSet = true; - set.push( panel.hdaViews[ hda.id ] ); - } - } - }); - return set; - }, - - // ------------------------------------------------------------------------ panel events - /** event map */ - events : { - // allow (error) messages to be clicked away -//TODO: switch to common close (X) idiom - 'click .message-container' : 'clearMessages', - 'click .history-search-btn' : 'toggleSearchControls' - }, - - /** Collapse all hda bodies and clear expandedHdas in the storage */ - collapseAllHdaBodies : function(){ - _.each( this.hdaViews, function( item ){ - item.collapse(); - }); - this.storage.set( 'expandedHdas', {} ); - return this; - }, - - /** Handle the user toggling the deleted visibility by: - * (1) storing the new value in the persistent storage - * (2) re-rendering the history - * @returns {Boolean} new show_deleted setting - */ - toggleShowDeleted : function( show ){ - show = ( show !== undefined )?( show ):( !this.storage.get( 'show_deleted' ) ); - this.storage.set( 'show_deleted', show ); - this.renderHdas(); - return this.storage.get( 'show_deleted' ); - }, - - /** Handle the user toggling the deleted visibility by: - * (1) storing the new value in the persistent storage - * (2) re-rendering the history - * @returns {Boolean} new show_hidden setting - */ - toggleShowHidden : function( show ){ - show = ( show !== undefined )?( show ):( !this.storage.get( 'show_hidden' ) ); - this.storage.set( 'show_hidden', show ); - this.renderHdas(); - return this.storage.get( 'show_hidden' ); - }, - - // ........................................................................ 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 - * esc will clear the search - * clicking the clear button will clear the search - * uses searchInput in ui.js - */ - _setUpSearchInput : function( $where ){ - var panel = this, - inputSelector = '.history-search-input'; - - function onFirstSearch( searchFor ){ - //this.log( 'onFirstSearch', searchFor, panel ); - if( panel.model.contents.haveDetails() ){ - panel.searchHdas( searchFor ); - return; - } - panel.$el.find( inputSelector ).searchInput( 'toggle-loading' ); - panel.model.contents.fetchAllDetails({ silent: true }) - .always( function(){ - panel.$el.find( inputSelector ).searchInput( 'toggle-loading' ); - }) - .done( function(){ - panel.searchHdas( searchFor ); - }); - } - $where.searchInput({ - initialVal : panel.searchFor, - name : 'history-search', - placeholder : 'search datasets', - classes : 'history-search', - onfirstsearch : onFirstSearch, - onsearch : _.bind( this.searchHdas, this ), - onclear : _.bind( this.clearHdaSearch, this ) - }); - return $where; - }, - /** 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' ), - 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 ); - } - return show; - }, - - /** filter hda view list to those that contain the searchFor terms - * (see the search section in the HDA model for implementation of the actual searching) - */ - searchHdas : function( searchFor ){ - //note: assumes hda details are loaded - //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 ){ - //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 ){ - speed = ( speed !== undefined )?( speed ):( this.fxSpeed ); - if( !this.indicator ){ - this.indicator = new LoadingIndicator( this.$el, this.$el.parent() ); - } - if( !this.$el.is( ':visible' ) ){ - this.indicator.show( 0, callback ); - } else { - this.$el.fadeOut( speed ); - this.indicator.show( msg, speed, callback ); - } - }, - - /** hide the loading indicator */ - _hideLoadingIndicator : function( speed, callback ){ - speed = ( speed !== undefined )?( speed ):( this.fxSpeed ); - if( this.indicator ){ - this.indicator.hide( speed, callback ); - } - }, - // ........................................................................ (error) messages /** Display a message in the top of the panel. * @param {String} type type of message ('done', 'error', 'warning') @@ -964,29 +516,18 @@ var ReadOnlyHistoryPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend _messageToModalOptions : function( type, msg, details ){ // only error is fleshed out here var panel = this, - $modalBody = $( '
' ), options = { title: 'Details' }; - - //TODO: to some util library - function objToTable( obj ){ - obj = _.omit( obj, _.functions( obj ) ); - return [ - '', - _.map( obj, function( val, key ){ - val = ( _.isObject( val ) )?( objToTable( val ) ):( val ); - return '' - + ''; - }).join( '' ), - '
' + key + '' + val + '
' - ].join( '' ); - } - if( _.isObject( details ) ){ - options.body = $modalBody.append( objToTable( details ) ); + + details = _.omit( details, _.functions( details ) ); + var text = JSON.stringify( details, null, ' ' ), + pre = $( '
' ).text( text );
+            options.body = $( '
' ).append( pre ); } else { - options.body = $modalBody.html( details ); + options.body = $( '
' ).html( details ); } + options.buttons = { 'Ok': function(){ Galaxy.modal.hide(); @@ -997,140 +538,91 @@ var ReadOnlyHistoryPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend return options; }, - /** Remove all messages from the panel. - */ - clearMessages : function(){ - this.$messages().empty(); + /** Remove all messages from the panel. */ + clearMessages : function( ev ){ + $( ev.currentTarget ).fadeOut( this.fxSpeed, function(){ + $( this ).remove(); + }); + //this.$messages().children().not( '.quota-message' ).remove(); return this; }, // ........................................................................ scrolling - /** get the current scroll position of the panel in its parent */ - scrollPosition : function(){ - return this.$container().scrollTop(); - }, - - /** set the current scroll position of the panel in its parent */ - scrollTo : function( pos ){ - this.$container().scrollTop( pos ); - return this; - }, - - /** Scrolls the panel to the top. */ - scrollToTop : function(){ - this.$container().scrollTop( 0 ); - return this; - }, - - /** Scrolls the panel to show the HDA with the given id. - * @param {String} id the id of HDA to scroll into view - * @returns {HistoryPanel} the panel - */ - scrollToId : function( id ){ - // do nothing if id not found - if( ( !id ) || ( !this.hdaViews[ id ] ) ){ - return this; - } - var view = this.hdaViews[ id ]; - //this.scrollIntoView( $viewEl.offset().top ); - this.scrollTo( view.el.offsetTop ); - return this; - }, - - /** Scrolls the panel to show the HDA with the given hid. - * @param {Integer} hid the hid of HDA to scroll into view + /** Scrolls the panel to show the content sub-view with the given hid. + * @param {Integer} hid the hid of item to scroll into view * @returns {HistoryPanel} the panel */ scrollToHid : function( hid ){ - var hda = this.model.contents.getByHid( hid ); - // do nothing if hid not found - if( !hda ){ return this; } - return this.scrollToId( hda.id ); + return this.scrollToItem( _.first( this.viewsWhereModel({ hid: hid }) ) ); }, // ........................................................................ misc - print : function(){ - var panel = this; - panel.debug( this ); - _.each( this.hdaViews, function( view, id ){ - panel.debug( '\t ' + id, view ); - }); - }, - /** Return a string rep of the history */ toString : function(){ - return 'ReadOnlyHistoryPanel(' + (( this.model )?( this.model.get( 'name' )):( '' )) + ')'; + return 'HistoryPanel(' + (( this.model )?( this.model.get( 'name' )):( '' )) + ')'; } }); + + //------------------------------------------------------------------------------ TEMPLATES -var _panelTemplate = [ - '
', - '
', - '
', - '
', +HistoryPanel.prototype.templates = (function(){ - '
', - '<% if( history.name ){ %>', - '
<%= history.name %>
', - '<% } %>', - '
', - - '
', - '<% if( history.nice_size ){ %>', - '
<%= history.nice_size %>
', - '<% } %>', - '
', - '
', - - '<% if( history.deleted ){ %>', - '
', - _l( 'You are currently viewing a deleted history!' ), - '
', - '<% } %>', - - '
', - '<% if( history.message ){ %>', - // should already be localized - '
<%= history.message %>
', - '<% } %>', - '
', - - '
', - _l( 'You are over your disk quota' ), '. ', - _l( 'Tool execution is on hold until your disk usage drops below your allocated quota' ), '.', - '
', - - '
', - '
', - '
', - '
', - '', - '', + var controlsTemplate = BASE_MVC.wrapTemplate([ + '
', + '
', + '
<%= history.name %>
', '
', - '', - '
', - '
', - // end history controls + '
', + //'<%= view.collection.length %>', _l( ' items' ), + '
', + '
<%= history.nice_size %>
', - // where the datasets/hdas are added - '
', - '
', - _l( 'This history is empty' ), - '
' -].join( '' ); + '
', -ReadOnlyHistoryPanel.templates = { - historyPanel : function( historyJSON ){ - return _.template( _panelTemplate, historyJSON, { variable: 'history' }); - } -}; + '
', + '<% if( history.deleted ){ %>', + '
', + _l( 'This history has been deleted' ), + '
', + '<% } %>', + + '<% if( history.message ){ %>', + // should already be localized + '
messagesmall">', + '<%= history.message.text %>', + '
', + '<% } %>', + '
', + + // add tags and annotations + '
', + '
', + + '', + + '
', + '
', + '', + '', + '
', + '', + '
', + '
' + ], 'history' ); + + return _.extend( _.clone( _super.prototype.templates ), { + controls : controlsTemplate + }); +}()); //============================================================================== return { - ReadOnlyHistoryPanel: ReadOnlyHistoryPanel + HistoryPanel: HistoryPanel }; }); diff --git a/static/scripts/mvc/list/list-item.js b/static/scripts/mvc/list/list-item.js new file mode 100644 index 00000000000..db503be829e --- /dev/null +++ b/static/scripts/mvc/list/list-item.js @@ -0,0 +1,332 @@ +define([ + 'mvc/base-mvc', + 'utils/localization' +], function( BASE_MVC, _l ){ + +//============================================================================== +/** A view which, when first rendered, shows only summary data/attributes, but + * can be expanded to show further details (and optionally fetch those + * details from the server). + */ +var ExpandableView = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({ +//TODO: Although the reasoning behind them is different, this shares a lot with HiddenUntilActivated above: combine them + //PRECONDITION: model must have method hasDetails + //PRECONDITION: subclasses must have templates.el and templates.details + + initialize : function( attributes ){ + /** are the details of this view expanded/shown or not? */ + this.expanded = attributes.expanded || false; + //this.log( '\t expanded:', this.expanded ); + this.fxSpeed = attributes.fxSpeed || this.fxSpeed; + }, + + // ........................................................................ render main + /** jq fx speed */ + fxSpeed : 'fast', + + /** Render this content, set up ui. + * @param {Number or String} speed the speed of the render + */ + render : function( speed ){ + var $newRender = this._buildNewRender(); + this._setUpBehaviors( $newRender ); + this._queueNewRender( $newRender, speed ); + return this; + }, + + /** Build a temp div containing the new children for the view's $el. + * If the view is already expanded, build the details as well. + */ + _buildNewRender : function(){ + // create a new render using a skeleton template, render title buttons, render body, and set up events, etc. + var $newRender = $( this.templates.el( this.model.toJSON(), this ) ); + if( this.expanded ){ + this.$details( $newRender ).replaceWith( this._renderDetails().show() ); + } + return $newRender; + }, + + /** Fade out the old el, swap in the new contents, then fade in. + * @param {Number or String} speed jq speed to use for rendering effects + * @fires rendered when rendered + */ + _queueNewRender : function( $newRender, speed ) { + speed = ( speed === undefined )?( this.fxSpeed ):( speed ); + var view = this; + + $( view ).queue( 'fx', [ + function( next ){ this.$el.fadeOut( speed, next ); }, + function( next ){ + view._swapNewRender( $newRender ); + next(); + }, + function( next ){ this.$el.fadeIn( speed, next ); }, + function( next ){ + this.trigger( 'rendered', view ); + next(); + } + ]); + }, + + /** empty out the current el, move the $newRender's children in */ + _swapNewRender : function( $newRender ){ + return this.$el.empty().attr( 'class', this.className ).append( $newRender.children() ); + }, + + /** set up js behaviors, event handlers for elements within the given container + * @param {jQuery} $container jq object that contains the elements to process (defaults to this.$el) + */ + _setUpBehaviors : function( $where ){ + $where = $where || this.$el; + // set up canned behavior on children (bootstrap, popupmenus, editable_text, etc.) + //make_popup_menus( $where ); + $where.find( '[title]' ).tooltip({ placement : 'bottom' }); + }, + + // ......................................................................... details + /** shortcut to details DOM (as jQ) */ + $details : function( $where ){ + $where = $where || this.$el; + return $where.find( '.details' ); + }, + + /** build the DOM for the details and set up behaviors on it */ + _renderDetails : function(){ + var $newDetails = $( this.templates.details( this.model.toJSON(), this ) ); + this._setUpBehaviors( $newDetails ); + return $newDetails; + }, + + // ......................................................................... expansion/details + /** Show or hide the details + * @param {Boolean} expand if true, expand; if false, collapse + */ + toggleExpanded : function( expand ){ + expand = ( expand === undefined )?( !this.expanded ):( expand ); + if( expand ){ + this.expand(); + } else { + this.collapse(); + } + return this; + }, + + /** Render and show the full, detailed body of this view including extra data and controls. + * note: if the model does not have detailed data, fetch that data before showing the body + * @fires expanded when a body has been expanded + */ + expand : function(){ + var view = this; + return view._fetchModelDetails() + .always(function(){ + var $newDetails = view._renderDetails(); + view.$details().replaceWith( $newDetails ); + // needs to be set after the above or the slide will not show + view.expanded = true; + $newDetails.slideDown( view.fxSpeed, function(){ + view.trigger( 'expanded', view ); + }); + }); + }, + + /** Check for model details and, if none, fetch them. + * @returns {jQuery.promise} the model.fetch.xhr if details are being fetched, an empty promise if not + */ + _fetchModelDetails : function(){ + if( !this.model.hasDetails() ){ + return this.model.fetch(); + } + return jQuery.when(); + }, + + /** Hide the body/details of an HDA. + * @fires collapsed when a body has been collapsed + */ + collapse : function(){ + var view = this; + view.expanded = false; + this.$details().slideUp( view.fxSpeed, function(){ + view.trigger( 'collapsed', view ); + }); + } + +}); + + +//============================================================================== +/** A view that is displayed in some larger list/grid/collection. + * Inherits from Expandable, Selectable, Draggable. + * The DOM contains warnings, a title bar, and a series of primary action controls. + * Primary actions are meant to be easily accessible item functions (such as delete) + * that are rendered in the title bar. + * + * Details are rendered when the user clicks the title bar or presses enter/space when + * the title bar is in focus. + * + * Designed as a base class for history panel contents - but usable elsewhere (I hope). + */ +var ListItemView = ExpandableView.extend( + BASE_MVC.mixin( BASE_MVC.SelectableViewMixin, BASE_MVC.DraggableViewMixin, { + +//TODO: that's a little contradictory + tagName : 'div', + className : 'list-item', + + /** Set up the base class and all mixins */ + initialize : function( attributes ){ + ExpandableView.prototype.initialize.call( this, attributes ); + BASE_MVC.SelectableViewMixin.initialize.call( this, attributes ); + BASE_MVC.DraggableViewMixin.initialize.call( this, attributes ); + }, + + // ........................................................................ rendering + /** In this override, call methods to build warnings, titlebar and primary actions */ + _buildNewRender : function(){ + var $newRender = ExpandableView.prototype._buildNewRender.call( this ); + $newRender.find( '.warnings' ).replaceWith( this._renderWarnings() ); + $newRender.find( '.title-bar' ).replaceWith( this._renderTitleBar() ); + $newRender.find( '.primary-actions' ).append( this._renderPrimaryActions() ); + $newRender.find( '.subtitle' ).replaceWith( this._renderSubtitle() ); + return $newRender; + }, + + /** In this override, render the selector controls and set up dragging before the swap */ + _swapNewRender : function( $newRender ){ + ExpandableView.prototype._swapNewRender.call( this, $newRender ); + if( this.selectable ){ this.showSelector( 0 ); } + if( this.draggable ){ this.draggableOn(); } + return this.$el; + }, + + /** Render any warnings the item may need to show (e.g. "I'm deleted") */ + _renderWarnings : function(){ + var view = this, + $warnings = $( '
' ), + json = view.model.toJSON(); +//TODO:! unordered (map) + _.each( view.templates.warnings, function( templateFn ){ + $warnings.append( $( templateFn( json, view ) ) ); + }); + return $warnings; + }, + + /** Render the title bar (the main/exposed SUMMARY dom element) */ + _renderTitleBar : function(){ + return $( this.templates.titleBar( this.model.toJSON(), this ) ); + }, + + /** Return an array of jQ objects containing common/easily-accessible item controls */ + _renderPrimaryActions : function(){ + // override this + return []; + }, + + /** Render the title bar (the main/exposed SUMMARY dom element) */ + _renderSubtitle : function(){ + return $( this.templates.subtitle( this.model.toJSON(), this ) ); + }, + + // ......................................................................... events + /** event map */ + events : { + // expand the body when the title is clicked or when in focus and space or enter is pressed + 'click .title-bar' : '_clickTitleBar', + 'keydown .title-bar' : '_keyDownTitleBar', + + // dragging - don't work, originalEvent === null + //'dragstart .dataset-title-bar' : 'dragStartHandler', + //'dragend .dataset-title-bar' : 'dragEndHandler' + + 'click .selector' : 'toggleSelect' + }, + + /** expand when the title bar is clicked */ + _clickTitleBar : function( event ){ + event.stopPropagation(); + this.toggleExpanded(); + }, + + /** expand when the title bar is in focus and enter or space is pressed */ + _keyDownTitleBar : function( event ){ + // bail (with propagation) if keydown and not space or enter + var KEYCODE_SPACE = 32, KEYCODE_RETURN = 13; + if( event && ( event.type === 'keydown' ) + &&( event.keyCode === KEYCODE_SPACE || event.keyCode === KEYCODE_RETURN ) ){ + this.toggleExpanded(); + event.stopPropagation(); + return false; + } + return true; + }, + + // ......................................................................... misc + /** String representation */ + toString : function(){ + var modelString = ( this.model )?( this.model + '' ):( '(no model)' ); + return 'ListItemView(' + modelString + ')'; + } +})); + +// ............................................................................ TEMPLATES +/** underscore templates */ +ListItemView.prototype.templates = (function(){ +//TODO: move to require text! plugin + + var elTemplato = BASE_MVC.wrapTemplate([ + '
', + // errors, messages, etc. + '
', + + // multi-select checkbox + '
', + '', + '
', + // space for title bar buttons - gen. floated to the right + '
', + '
', + + // expandable area for more details + '
', + '
' + ]); + + var warnings = {}; + + var titleBarTemplate = BASE_MVC.wrapTemplate([ + // adding a tabindex here allows focusing the title bar and the use of keydown to expand the dataset display + '
', +//TODO: prob. belongs in dataset-list-item + '', + '
', + '<%- element.name %>', + '
', + '
', + '
' + ], 'element' ); + + var subtitleTemplate = BASE_MVC.wrapTemplate([ + // override this + '
' + ]); + + var detailsTemplate = BASE_MVC.wrapTemplate([ + // override this + '
' + ]); + + return { + el : elTemplato, + warnings : warnings, + titleBar : titleBarTemplate, + subtitle : subtitleTemplate, + details : detailsTemplate + }; +}()); + + +//============================================================================== + return { + ExpandableView : ExpandableView, + ListItemView : ListItemView + }; +}); diff --git a/static/scripts/mvc/dataset/list-panel.js b/static/scripts/mvc/list/list-panel.js similarity index 58% rename from static/scripts/mvc/dataset/list-panel.js rename to static/scripts/mvc/list/list-panel.js index d645f15c2a5..a1855ea4f30 100644 --- a/static/scripts/mvc/dataset/list-panel.js +++ b/static/scripts/mvc/list/list-panel.js @@ -1,12 +1,24 @@ define([ + "mvc/list/list-item", "mvc/base-mvc", "utils/localization" -], function( BASE_MVC, _l ){ -/* ============================================================================= +], function( LIST_ITEM, BASE_MVC, _l ){ +/* ============================================================================ TODO: -============================================================================= */ -/** @class List that contains ListItemViews. +============================================================================ */ +/** @class View for a list/collection of models and the sub-views of those models. + * Sub-views must (at least have the interface if not) inherit from ListItemView. + * (For a list panel that also includes some 'container' model (History->HistoryContents) + * use ModelWithListPanel) + * + * Allows for: + * searching collection/sub-views + * selecting/multi-selecting sub-views + * + * Currently used: + * for dataset/dataset-choice + * as superclass of ModelListPanel */ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( /** @lends ReadOnlyHistoryPanel.prototype */{ @@ -15,7 +27,9 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( //logger : console, /** class to use for constructing the sub-views */ - viewClass : BASE_MVC.ListItemView, + viewClass : LIST_ITEM.ListItemView, + /** class to used for constructing collection of sub-view models */ + collectionClass : Backbone.Collection, tagName : 'div', className : 'list-panel', @@ -23,10 +37,15 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( /** (in ms) that jquery effects will use */ fxSpeed : 'fast', - /** string to display when the model has no hdas */ + /** string to display when the collection has no contents */ emptyMsg : _l( 'This list is empty' ), - /** string to no hdas match the search terms */ + /** displayed when no items match the search terms */ noneFoundMsg : _l( 'No matching items found' ), + /** string used for search placeholder */ + searchPlaceholder : _l( 'search' ), + + /** actions available for multiselected items */ + multiselectActions : [], // ......................................................................... SET UP /** Set up the view, set up storage, bind listeners to HistoryContents events @@ -67,21 +86,42 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( /** list item views */ this.views = []; /** list item models */ - this.collection = attributes.collection || ( new Backbone.Collection([]) ); + this.collection = attributes.collection || ( new this.collectionClass([]) ); /** filter fns run over collection items to see if they should show in the list */ this.filters = attributes.filters || []; + /** override $scrollContainer fn via attributes - fn should return jq for elem to call scrollTo on */ + this.$scrollContainer = attributes.$scrollContainer || this.$scrollContainer; + //TODO: remove this.title = attributes.title || ''; this.subtitle = attributes.subtitle || ''; + // allow override of multiselectActions through attributes + this.multiselectActions = attributes.multiselectActions || this.multiselectActions; + /** the popup displayed when 'for all selected...' is clicked */ + this.actionsPopup = null; + this._setUpListeners(); }, + /** free any sub-views the list has */ + freeViews : function(){ +//TODO: stopListening? remove? + _.each( this.views, function( view ){ + view.off(); + }); + this.views = []; + return this; + }, + + // ------------------------------------------------------------------------ listeners /** create any event listeners for the list */ _setUpListeners : function(){ + this.off(); + this.on( 'error', function( model, xhr, options, msg, details ){ //this.errorHandler( model, xhr, options, msg, details ); console.error( model, xhr, options, msg, details ); @@ -112,21 +152,19 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( return this; }, - /** free any sub-views the list has */ - freeViews : function(){ -//TODO: stopListening? remove? - this.views = []; - return this; - }, - - // ------------------------------------------------------------------------ item listeners - /** listening for history and HDA events */ + /** listening for collection events */ _setUpCollectionListeners : function(){ + this.log( this + '._setUpCollectionListeners', this.collection ); + this.collection.off(); + + // bubble up error events + this.collection.on( 'error', function( model, xhr, options, msg, details ){ + this.trigger( 'error', model, xhr, options, msg, details ); + }, this ); this.collection.on( 'reset', function(){ this.renderItems(); }, this ); - this.collection.on( 'add', this.addItemView, this ); this.collection.on( 'remove', this.removeItemView, this ); @@ -139,8 +177,9 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( return this; }, - /** listening for history and HDA events */ + /** listening for sub-view events that bubble up with the 'view:' prefix */ _setUpViewListeners : function(){ + this.log( this + '._setUpViewListeners' ); // shift to select a range this.on( 'view:selected', function( view, ev ){ @@ -155,6 +194,11 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( this.selected.push( view.model.id ); this.lastSelected = view.model.id; }, this ); + + this.on( 'view:de-selected', function( view, ev ){ + this.selected = _.without( this.selected, view.model.id ); + //this.lastSelected = view.model.id; + }, this ); }, // ------------------------------------------------------------------------ rendering @@ -162,6 +206,7 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( * @param {Number or String} speed the speed of the render */ render : function( speed ){ + this.log( this + '.render', speed ); var $newRender = this._buildNewRender(); this._setUpBehaviors( $newRender ); this._queueNewRender( $newRender, speed ); @@ -171,9 +216,9 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( /** Build a temp div containing the new children for the view's $el. */ _buildNewRender : function(){ - // create a new render using a skeleton template, render title buttons, render body, and set up events, etc. - var json = this.model? this.model.toJSON() : {}, - $newRender = $( this.templates.el( json, this ) ); + this.debug( this + '(ListPanel)._buildNewRender' ); + var $newRender = $( this.templates.el( {}, this ) ); + this._renderControls( $newRender ); this._renderTitle( $newRender ); this._renderSubtitle( $newRender ); this._renderSearch( $newRender ); @@ -181,6 +226,15 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( return $newRender; }, + /** Build a temp div containing the new children for the view's $el. + */ + _renderControls : function( $newRender ){ + this.debug( this + '(ListPanel)._renderControls' ); + var $controls = $( this.templates.controls( {}, this ) ); + $newRender.find( '.controls' ).replaceWith( $controls ); + return $controls; + }, + /** */ _renderTitle : function( $where ){ @@ -201,17 +255,18 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( */ _queueNewRender : function( $newRender, speed ) { speed = ( speed === undefined )?( this.fxSpeed ):( speed ); - var view = this; + var panel = this; + panel.log( '_queueNewRender:', $newRender, speed ); - $( view ).queue( 'fx', [ + $( panel ).queue( 'fx', [ function( next ){ this.$el.fadeOut( speed, next ); }, function( next ){ - view._swapNewRender( $newRender ); + panel._swapNewRender( $newRender ); next(); }, function( next ){ this.$el.fadeIn( speed, next ); }, function( next ){ - view.trigger( 'rendered', view ); + panel.trigger( 'rendered', panel ); next(); } ]); @@ -234,7 +289,7 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( // ------------------------------------------------------------------------ sub-$element shortcuts /** the scroll container for this panel - can be $el, $el.parent(), or grandparent depending on context */ $scrollContainer : function(){ - // override + // override or set via attributes.$scrollContainer return this.$el.parent().parent(); }, /** */ @@ -242,8 +297,8 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( return ( $where || this.$el ).find( '.list-items' ); }, /** container where list messages are attached */ - $messages : function( $where ){ - return ( $where || this.$el ).find( '.message-container' ); + $messages : function( $where ){ + return ( $where || this.$el ).find( '.messages' ); }, /** the message displayed when no views can be shown (no views, none matching search) */ $emptyMessage : function( $where ){ @@ -252,52 +307,50 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( // ------------------------------------------------------------------------ hda sub-views /** - * @param {jQuery} $whereTo what dom element to prepend the HDA views to + * @param {jQuery} $whereTo what dom element to prepend the sub-views to * @returns the visible item views */ renderItems : function( $whereTo ){ $whereTo = $whereTo || this.$el; - var list = this, - newViews = []; + var panel = this; + panel.log( this + '.renderItems', $whereTo ); - var $list = this.$list( $whereTo ), - item$els = this._filterCollection().map( function( itemModel ){ + var $list = panel.$list( $whereTo ); +//TODO: free prev. views? + panel.views = panel._filterCollection().map( function( itemModel ){ //TODO: creates views each time - not neccessarily good - var view = list._createItemView( itemModel ); - newViews.push( view ); - return view.render( 0 ).$el; + return panel._createItemView( itemModel ).render( 0 ); }); - this.debug( item$els ); - this.debug( newViews ); + //panel.debug( item$els ); + //panel.debug( newViews ); $list.empty(); - if( item$els.length ){ - $list.append( item$els ); - this.$emptyMessage( $whereTo ).hide(); + if( panel.views.length ){ + panel._attachItems( $whereTo ); + panel.$emptyMessage( $whereTo ).hide(); } else { - this._renderEmptyMessage( $whereTo ).show(); + panel._renderEmptyMessage( $whereTo ).show(); } - this.views = newViews; - return newViews; + return panel.views; }, /** */ _filterCollection : function(){ // override this - var list = this; - return list.collection.filter( _.bind( list._filterItem, list ) ); + var panel = this; + return panel.collection.filter( _.bind( panel._filterItem, panel ) ); }, /** */ _filterItem : function( model ){ // override this - var list = this; - return ( _.every( list.filters.map( function( fn ){ return fn.call( model ); }) ) ) - && ( !list.searchFor || model.matchesAll( list.searchFor ) ); + var panel = this; + return ( _.every( panel.filters.map( function( fn ){ return fn.call( model ); }) ) ) + && ( !panel.searchFor || model.matchesAll( panel.searchFor ) ); }, /** @@ -312,11 +365,15 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( return view; }, + /** + */ _getItemViewClass : function( model ){ // override this return this.viewClass; }, + /** + */ _getItemViewOptions : function( model ){ // override this return { @@ -332,11 +389,12 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( /** */ _setUpItemViewListeners : function( view ){ - var list = this; + var panel = this; + // send all events to the panel, re-namspaceing them with the view prefix view.on( 'all', function(){ var args = Array.prototype.slice.call( arguments, 0 ); args[0] = 'view:' + args[0]; - list.trigger.apply( list, args ); + panel.trigger.apply( panel, args ); }); // debugging @@ -345,12 +403,21 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( // this.log( this + '(view)', arguments ); // }, this ); //} + return panel; + }, + + /** + */ + _attachItems : function( $whereTo ){ + this.$list( $whereTo ).append( this.views.map( function( view ){ + return view.$el; + })); return this; }, /** render the empty/none-found message */ _renderEmptyMessage : function( $whereTo ){ - //this.debug( '_renderEmptyMessage', $whereTo, this.searchFor ); + this.debug( '_renderEmptyMessage', $whereTo, this.searchFor ); var text = this.searchFor? this.noneFoundMsg : this.emptyMsg; return this.$emptyMessage( $whereTo ).text( text ); }, @@ -374,37 +441,45 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( */ addItemView : function( model, collection, options ){ this.log( this + '.addItemView:', model ); - var list = this; - if( !this._filterItem( model ) ){ return undefined; } + var panel = this; + if( !panel._filterItem( model ) ){ return undefined; } //TODO: sorted? position? - var view = list._createItemView( model ); - this.views.push( view ); + var view = panel._createItemView( model ); + panel.views.push( view ); $( view ).queue( 'fx', [ - function( next ){ list.$emptyMessage().fadeOut( list.fxSpeed, next ); }, + function( next ){ panel.$emptyMessage().fadeOut( panel.fxSpeed, next ); }, function( next ){ //TODO: auto render? - list.$list().append( view.render().$el ); +// slide down? + panel.$list().append( view.render().$el ); next(); } ]); return view; }, - /** - */ + /** */ removeItemView : function( model, collection, options ){ this.log( this + '.removeItemView:', model ); - var list = this, - view = list.viewFromModel( model ); + var panel = this, + view = panel.viewFromModel( model ); if( !view ){ return undefined; } - this.views = _.without( this.views, view ); - view.remove(); - if( !this.views.length ){ - list._renderEmptyMessage().fadeIn( list.fxSpeed ); - } + // use anonymous queue here - since remove can happen multiple times + $({}).queue( 'fx', [ + function( next ){ view.$el.fadeOut( panel.fxSpeed, next ); }, + function( next ){ + panel.views = _.without( panel.views, view ); + view.remove(); + if( !panel.views.length ){ + panel._renderEmptyMessage().fadeIn( panel.fxSpeed, next ); + } else { + next(); + } + } + ]); return view; }, @@ -420,6 +495,16 @@ var ListPanel = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend( return undefined; }, + /** get views based on model.id */ + viewFromModelId : function( id ){ + for( var i=0; i', - '
', - '
', - '
<%= model.name || view.title %>
', - '
', - '
<%= view.subtitle %>
', - '
', - '
', - - '', - - '
', - '
', - '', - '', - '
', - //'', - '
', - '
', + '
', '
', '
', '
' ]); + var controlsTemplate = BASE_MVC.wrapTemplate([ + '
', + '
', + '
<%= view.title %>
', + '
', + '
<%= view.subtitle %>
', + '
', + '
', + + '', + + '
', + '
', + '', + '', + '
', + //'', + '
', + '
' + ]); + return { - el : elTemplate + el : elTemplate, + controls : controlsTemplate }; }()); +//============================================================================= +/** View for a model that has a sub-collection (e.g. History, DatasetCollection) + * + * + */ +var ModelListPanel = ListPanel.extend({ -//============================================================================== + /** key of attribute in model to assign to this.collection */ + modelCollectionKey : 'contents', + + initialize : function( attributes ){ + ListPanel.prototype.initialize.call( this, attributes ); + this.selecting = ( attributes.selecting !== undefined )? attributes.selecting : false; + + // ---- instance vars + // don't render when setting the first time + this.setModel( this.model, attributes ); + }, + + /** release/free/shutdown old models and set up panel for new models + * @fires new-model with the panel as parameter + */ + setModel : function( model, attributes ){ + attributes = attributes || {}; + this.debug( this + '.setModel:', model, attributes ); + + this.freeModel(); + this.freeViews(); + + if( model ){ + // set up the new model with user, logger, storage, events + this.model = model; + if( this.logger ){ + this.model.logger = this.logger; + } + this._setUpModelListeners(); + +//TODO: relation btwn model, collection becoming tangled here + // free the collection, and assign the new collection to either + // the model[ modelCollectionKey ], attributes.collection, or an empty vanilla collection + this.collection.off(); + this.collection = ( this.model[ this.modelCollectionKey ] )? + this.model[ this.modelCollectionKey ]: + ( attributes.collection || ( new this.collectionClass([]) ) ); + this._setUpCollectionListeners(); + + this.trigger( 'new-model', this ); + } + return this; + }, + + /** free the current model and all listeners for it, free any views for the model */ + freeModel : function(){ + // stop/release the previous model, and clear cache to sub-views + if( this.model ){ + this.stopListening( this.model ); + //TODO: see base-mvc + //this.model.free(); + //this.model = null; + } + return this; + }, + + // ------------------------------------------------------------------------ listening + /** listening for model events */ + _setUpModelListeners : function(){ + // override + this.log( this + '._setUpModelListeners', this.model ); + // bounce model errors up to the panel + this.model.on( 'error', function(){ + //TODO: namespace? + //var args = Array.prototype.slice.call( arguments, 0 ); + //args[0] = 'model:' + args[0]; + this.trigger.apply( panel, arguments ); + }, this ); + return this; + }, + + /** Build a temp div containing the new children for the view's $el. + */ + _renderControls : function( $newRender ){ + this.debug( this + '(ListPanel)._renderControls' ); + var json = this.model? this.model.toJSON() : {}, + $controls = $( this.templates.controls( json, this ) ); + $newRender.find( '.controls' ).replaceWith( $controls ); + this.debug( '\t .controls:', this.$( '.controls' ) ); + this.debug( '\t $controls:', $controls ); + return $controls; + }, + + // ------------------------------------------------------------------------ misc + /** Return a string rep of the panel */ + toString : function(){ + return 'ModelListPanel(' + this.model + ')'; + } +}); + +// ............................................................................ TEMPLATES +/** underscore templates */ +ModelListPanel.prototype.templates = (function(){ +//TODO: move to require text! plugin + + var controlsTemplate = BASE_MVC.wrapTemplate([ + '
', + '
', + '
<%= model.name %>
', + '
', + '
<%= view.subtitle %>
', + '
', + '
', + + '', + + '
', + '
', + '', + '', + '
', + //'', + '
', + '
' + ]); + + return _.extend( _.clone( ListPanel.prototype.templates ), { + controls : controlsTemplate + }); +}()); + + +//============================================================================= return { - ListPanel: ListPanel + ListPanel : ListPanel, + ModelListPanel : ModelListPanel }; }); diff --git a/static/scripts/mvc/tools/tools-form.js b/static/scripts/mvc/tools/tools-form.js index 4b017209afe..bcd79081411 100644 --- a/static/scripts/mvc/tools/tools-form.js +++ b/static/scripts/mvc/tools/tools-form.js @@ -157,6 +157,7 @@ define(['mvc/ui/ui-portlet', 'mvc/ui/ui-misc', // trigger refresh self.refresh(); + self._submit(); } }); }, @@ -177,7 +178,7 @@ define(['mvc/ui/ui-portlet', 'mvc/ui/ui-misc', // submit _submit: function() { - console.log(this.tree.jobDictionary()); + console.log(this.tree.finalize()); } }); diff --git a/static/scripts/mvc/tools/tools-tree.js b/static/scripts/mvc/tools/tools-tree.js index db50641434d..c27d23e0442 100644 --- a/static/scripts/mvc/tools/tools-tree.js +++ b/static/scripts/mvc/tools/tools-tree.js @@ -11,24 +11,23 @@ return Backbone.Model.extend({ // creates tree structure refresh: function() { - // check if section is available - if (!this.app.section) { - return {}; - } - // create dictionary this.dict = {}; // create xml object this.xml = $('
'); + // check if section is available + if (!this.app.section) { + return {}; + } + // fill dictionary this._iterate(this.app.section.$el, this.dict, this.xml); }, - // convert to job dictionary - jobDictionary: function() { + finalize: function() { // link this var self = this; @@ -38,22 +37,48 @@ return Backbone.Model.extend({ // converter between raw dictionary and job dictionary function convert(identifier, head) { for (var i in head) { - if (head[i].input) { - var input = head[i].input; - var job_input_id = identifier + input.name; + var node = head[i]; + if (node.input) { + // get node + var input = node.input; - if (input.type == 'repeat') { - + // create identifier + var job_input_id = identifier; + if (identifier != '') { + job_input_id += '|'; + } + job_input_id += input.name; - } else { - //var value = self.app.field_list[input.id].value(); - switch(input.type) { - case 'repeat': - job_def[job_input_id] = value; - break; - default: - job_def[job_input_id] = value; - } + // process input type + switch (input.type) { + // handle repeats + case 'repeat': + var index = 0; + for (var j in node) { + if (j.indexOf('section') != -1) { + convert(job_input_id + '_' + index++, node[j]); + } + } + break; + // handle conditionals + case 'conditional': + // get conditional value + var value = self.app.field_list[input.id].value(); + + // find selected case + for (var j in input.cases) { + if (input.cases[j].value == value) { + convert(job_input_id, head[input.id + '-section-' + j]); + break; + } + } + + // break + break; + default: + // handle default value + var value = self.app.field_list[input.id].value(); + job_def[job_input_id] = value; } } } @@ -61,49 +86,10 @@ return Backbone.Model.extend({ // start conversion convert('', this.dict); - + // return result return job_def; }, - - // iterate - _iterate: function(parent, dict, xml) { - // get child nodes - var self = this; - var children = $(parent).children(); - children.each(function() { - // get child element - var child = this; - - // get id - var id = $(child).attr('id'); - - // create new branch - if ($(child).hasClass('section-row') || $(child).hasClass('tab-pane')) { - // create sub dictionary - dict[id] = {}; - - // add input element if it exists - var input = self.app.input_list[id]; - if (input) { - dict[id] = { - input : input - } - } - - // create xml element - var $el = $('
'); - - // append xml - xml.append($el); - - // fill sub dictionary - self._iterate(child, dict[id], $el); - } else { - self._iterate(child, dict, xml); - } - }); - }, // find referenced elements findReferences: function(identifier, type) { @@ -176,6 +162,45 @@ return Backbone.Model.extend({ // return return referenced; + }, + + // iterate + _iterate: function(parent, dict, xml) { + // get child nodes + var self = this; + var children = $(parent).children(); + children.each(function() { + // get child element + var child = this; + + // get id + var id = $(child).attr('id'); + + // create new branch + if ($(child).hasClass('section-row') || $(child).hasClass('tab-pane')) { + // create sub dictionary + dict[id] = {}; + + // add input element if it exists + var input = self.app.input_list[id]; + if (input) { + dict[id] = { + input : input + } + } + + // create xml element + var $el = $('
'); + + // append xml + xml.append($el); + + // fill sub dictionary + self._iterate(child, dict[id], $el); + } else { + self._iterate(child, dict, xml); + } + }); } }); diff --git a/static/scripts/mvc/ui.js b/static/scripts/mvc/ui.js index 3bd8859b15b..709923a2d64 100644 --- a/static/scripts/mvc/ui.js +++ b/static/scripts/mvc/ui.js @@ -612,7 +612,7 @@ function LoadingIndicator( $where, options ){ * of characters, a callback is called. Pressing ESC when the input * is focused will clear the input and call a separate callback. */ - var _l = window._l || function( s ){ return s; } + var _l = window._l || function( s ){ return s; }; // contructor function searchInput( parentNode, options ){ @@ -637,11 +637,9 @@ function LoadingIndicator( $where, options ){ // .................................................................... input rendering and events // visually clear the search, trigger an event, and call the callback function clearSearchInput( event ){ - //console.debug( this, 'clear' ); var $input = $( this ).parent().children( 'input' ); - //console.debug( 'input', $input ); - $input.val( '' ); - $input.trigger( 'clear:searchInput' ); + //console.debug( this, 'clear', $input ); + $input.focus().val( '' ).trigger( 'clear:searchInput' ); options.onclear(); } diff --git a/static/scripts/mvc/ui/ui-tabs.js b/static/scripts/mvc/ui/ui-tabs.js index 21d08563673..5b990791ee8 100644 --- a/static/scripts/mvc/ui/ui-tabs.js +++ b/static/scripts/mvc/ui/ui-tabs.js @@ -20,7 +20,8 @@ var View = Backbone.View.extend({ this.$nav = null; this.$content = null; this.first_tab = null; - + this.current_id = null; + // configure options this.options = Utils.merge(options, this.optionsDefault); @@ -139,6 +140,11 @@ var View = Backbone.View.extend({ self.show(id); } }); + + // initialize current id + if (!this.current_id) { + this.current_id = id; + } }, // delete tab @@ -183,10 +189,14 @@ var View = Backbone.View.extend({ // show selected tab if (id) { - this.$el.find('.tab-element').removeClass('active'); - this.$el.find('.tab-pane').removeClass('active'); + // reassign active class + this.$el.find('#tab-' + this.current_id).removeClass('active'); + this.$el.find('#' + this.current_id).removeClass('active'); this.$el.find('#tab-' + id).addClass('active'); this.$el.find('#' + id).addClass('active'); + + // update current id + this.current_id = id; } // change diff --git a/static/scripts/packed/mvc/base-mvc.js b/static/scripts/packed/mvc/base-mvc.js index 9e7d7ef7f64..3d4fc20f8b7 100644 --- a/static/scripts/packed/mvc/base-mvc.js +++ b/static/scripts/packed/mvc/base-mvc.js @@ -1 +1 @@ -define(["utils/add-logging","utils/localization"],function(j,b){var k={logger:null,_logNamespace:"?",log:function(){if(this.logger){var m=this.logger.log;if(typeof this.logger.log==="object"){m=Function.prototype.bind.call(this.logger.log,this.logger)}return m.apply(this.logger,arguments)}return undefined}};j(k);var a=Backbone.Model.extend({initialize:function(n){this._checkEnabledSessionStorage();if(!n.id){throw new Error("SessionStorageModel requires an id in the initial attributes")}this.id=n.id;var m=(!this.isNew())?(this._read(this)):({});this.clear({silent:true});this.save(_.extend({},this.defaults,m,n),{silent:true});this.on("change",function(){this.save()})},_checkEnabledSessionStorage:function(){try{return sessionStorage.length}catch(m){alert("Please enable cookies in your browser for this Galaxy site");return false}},sync:function(p,n,m){if(!m.silent){n.trigger("request",n,{},m)}var o;switch(p){case"create":o=this._create(n);break;case"read":o=this._read(n);break;case"update":o=this._update(n);break;case"delete":o=this._delete(n);break}if(o!==undefined||o!==null){if(m.success){m.success()}}else{if(m.error){m.error()}}return o},_create:function(m){var n=m.toJSON(),o=sessionStorage.setItem(m.id,JSON.stringify(n));return(o===null)?(o):(n)},_read:function(m){return JSON.parse(sessionStorage.getItem(m.id))},_update:function(m){return m._create(m)},_delete:function(m){return sessionStorage.removeItem(m.id)},isNew:function(){return !sessionStorage.hasOwnProperty(this.id)},_log:function(){return JSON.stringify(this.toJSON(),null," ")},toString:function(){return"SessionStorageModel("+this.id+")"}});(function(){a.prototype=_.omit(a.prototype,"url","urlRoot")}());var h={searchAttributes:[],searchAliases:{},searchAttribute:function(o,m){var n=this.get(o);if(!m||(n===undefined||n===null)){return false}if(_.isArray(n)){return this._searchArrayAttribute(n,m)}return(n.toString().toLowerCase().indexOf(m.toLowerCase())!==-1)},_searchArrayAttribute:function(n,m){m=m.toLowerCase();return _.any(n,function(o){return(o.toString().toLowerCase().indexOf(m.toLowerCase())!==-1)})},search:function(m){var n=this;return _.filter(this.searchAttributes,function(o){return n.searchAttribute(o,m)})},matches:function(n){var p="=",m=n.split(p);if(m.length>=2){var o=m[0];o=this.searchAliases[o]||o;return this.searchAttribute(o,m[1])}return !!this.search(n).length},matchesAll:function(n){var m=this;n=n.match(/(".*"|\w*=".*"|\S*)/g).filter(function(o){return !!o});return _.all(n,function(o){o=o.replace(/"/g,"");return m.matches(o)})}};var d={hiddenUntilActivated:function(m,o){o=o||{};this.HUAVOptions={$elementShown:this.$el,showFn:jQuery.prototype.toggle,showSpeed:"fast"};_.extend(this.HUAVOptions,o||{});this.HUAVOptions.hasBeenShown=this.HUAVOptions.$elementShown.is(":visible");this.hidden=this.isHidden();if(m){var n=this;m.on("click",function(p){n.toggle(n.HUAVOptions.showSpeed)})}},isHidden:function(){return(this.HUAVOptions.$elementShown.is(":hidden"))},toggle:function(){if(this.hidden){if(!this.HUAVOptions.hasBeenShown){if(_.isFunction(this.HUAVOptions.onshowFirstTime)){this.HUAVOptions.hasBeenShown=true;this.HUAVOptions.onshowFirstTime.call(this)}}if(_.isFunction(this.HUAVOptions.onshow)){this.HUAVOptions.onshow.call(this);this.trigger("hiddenUntilActivated:shown",this)}this.hidden=false}else{if(_.isFunction(this.HUAVOptions.onhide)){this.HUAVOptions.onhide.call(this);this.trigger("hiddenUntilActivated:hidden",this)}this.hidden=true}return this.HUAVOptions.showFn.apply(this.HUAVOptions.$elementShown,arguments)}};function l(p,o){var m=Array.prototype.slice.call(arguments,0),n=m.pop();m.unshift(n);return _.defaults.apply(_,m)}function f(n,m){m=m||"model";var o=_.template(n.join(""));return function(q,p){var r={view:p||{},_l:b};r[m]=q||{};return o(r)}}var c=Backbone.View.extend(k).extend({initialize:function(m){this.expanded=m.expanded||false;this.fxSpeed=m.fxSpeed||this.fxSpeed},fxSpeed:"fast",render:function(n){var m=this._buildNewRender();this._setUpBehaviors(m);this._queueNewRender(m,n);return this},_buildNewRender:function(){var m=$(this.templates.el(this.model.toJSON(),this));if(this.expanded){this.$details(m).replaceWith(this._renderDetails().show())}return m},_queueNewRender:function(n,o){o=(o===undefined)?(this.fxSpeed):(o);var m=this;$(m).queue("fx",[function(p){this.$el.fadeOut(o,p)},function(p){m._swapNewRender(n);p()},function(p){this.$el.fadeIn(o,p)},function(p){this.trigger("rendered",m);p()}])},_swapNewRender:function(m){return this.$el.empty().attr("class",this.className).append(m.children())},_setUpBehaviors:function(m){m=m||this.$el;m.find("[title]").tooltip({placement:"bottom"})},$details:function(m){m=m||this.$el;return m.find(".details")},_renderDetails:function(){var m=$(this.templates.details(this.model.toJSON(),this));this._setUpBehaviors(m);return m},toggleExpanded:function(m){m=(m===undefined)?(!this.expanded):(m);if(m){this.expand()}else{this.collapse()}return this},expand:function(){var m=this;return m._fetchModelDetails().always(function(){var n=m._renderDetails();m.$details().replaceWith(n);m.expanded=true;n.slideDown(m.fxSpeed,function(){m.trigger("expanded",m)})})},_fetchModelDetails:function(){if(!this.model.hasDetails()){return this.model.fetch()}return jQuery.when()},collapse:function(){var m=this;m.expanded=false;this.$details().slideUp(m.fxSpeed,function(){m.trigger("collapsed",m)})}});var g={initialize:function(m){this.draggable=m.draggable||false},$dragHandle:function(){return this.$(".title-bar")},toggleDraggable:function(){if(this.draggable){this.draggableOff()}else{this.draggableOn()}},draggableOn:function(){this.draggable=true;this.dragStartHandler=_.bind(this._dragStartHandler,this);this.dragEndHandler=_.bind(this._dragEndHandler,this);var m=this.$dragHandle().attr("draggable",true).get(0);m.addEventListener("dragstart",this.dragStartHandler,false);m.addEventListener("dragend",this.dragEndHandler,false)},draggableOff:function(){this.draggable=false;var m=this.$dragHandle().attr("draggable",false).get(0);m.removeEventListener("dragstart",this.dragStartHandler,false);m.removeEventListener("dragend",this.dragEndHandler,false)},_dragStartHandler:function(m){this.trigger("dragstart",this);m.dataTransfer.effectAllowed="move";m.dataTransfer.setData("text",JSON.stringify(this.model.toJSON()));return false},_dragEndHandler:function(m){this.trigger("dragend",this);return false}};var e={initialize:function(m){this.selectable=m.selectable||false;this.selected=m.selected||false},$selector:function(){return this.$(".selector")},_renderSelected:function(){this.$selector().find("span").toggleClass("fa-check-square-o",this.selected).toggleClass("fa-square-o",!this.selected)},toggleSelector:function(){if(!this.$selector().is(":visible")){this.showSelector()}else{this.hideSelector()}},showSelector:function(m){m=m!==undefined?m:this.fxSpeed;this.selectable=true;this.trigger("selectable",true,this);this._renderSelected();this.$selector().show(m)},hideSelector:function(m){m=m!==undefined?m:this.fxSpeed;this.selectable=false;this.trigger("selectable",false,this);this.$selector().hide(m)},toggleSelect:function(m){if(this.selected){this.deselect(m)}else{this.select(m)}},select:function(m){if(!this.selected){this.trigger("selected",this,m);this.selected=true;this._renderSelected()}return false},deselect:function(m){if(this.selected){this.trigger("de-selected",this,m);this.selected=false;this._renderSelected()}return false}};var i=c.extend(l(e,g,{tagName:"div",className:"list-item",initialize:function(m){c.prototype.initialize.call(this,m);e.initialize.call(this,m);g.initialize.call(this,m)},_buildNewRender:function(){var m=c.prototype._buildNewRender.call(this);m.find(".warnings").replaceWith(this._renderWarnings());m.find(".title-bar").replaceWith(this._renderTitleBar());m.find(".primary-actions").append(this._renderPrimaryActions());m.find(".subtitle").replaceWith(this._renderSubtitle());return m},_swapNewRender:function(m){c.prototype._swapNewRender.call(this,m);if(this.selectable){this.showSelector(0)}if(this.draggable){this.draggableOn()}return this.$el},_renderWarnings:function(){var m=this,o=$('
'),n=m.model.toJSON();_.each(m.templates.warnings,function(p){o.append($(p(n,m)))});return o},_renderTitleBar:function(){return $(this.templates.titleBar(this.model.toJSON(),this))},_renderPrimaryActions:function(){return[]},_renderSubtitle:function(){return $(this.templates.subtitle(this.model.toJSON(),this))},events:{"click .title-bar":"_clickTitleBar","keydown .title-bar":"_keyDownTitleBar","click .selector":"toggleSelect"},_clickTitleBar:function(m){m.stopPropagation();this.toggleExpanded()},_keyDownTitleBar:function(o){var m=32,n=13;if(o&&(o.type==="keydown")&&(o.keyCode===m||o.keyCode===n)){this.toggleExpanded();o.stopPropagation();return false}return true},toString:function(){var m=(this.model)?(this.model+""):("(no model)");return"ListItemView("+m+")"}}));i.prototype.templates=(function(){var o=f(['
','
','
','',"
",'
','
','
',"
"]);var m={};var p=f(['
','','
','<%- element.name %>',"
",'
',"
"],"element");var q=f(['
']);var n=f(['
']);return{el:o,warnings:m,titleBar:p,subtitle:q,details:n}}());return{LoggableMixin:k,SessionStorageModel:a,SearchableModelMixin:h,HiddenUntilActivatedViewMixin:d,mixin:l,wrapTemplate:f,ExpandableView:c,DraggableViewMixin:g,SelectableViewMixin:e,ListItemView:i}}); \ No newline at end of file +define(["utils/add-logging","utils/localization"],function(h,b){var i={logger:null,_logNamespace:"?",log:function(){if(this.logger){var k=this.logger.log;if(typeof this.logger.log==="object"){k=Function.prototype.bind.call(this.logger.log,this.logger)}return k.apply(this.logger,arguments)}return undefined}};h(i);var a=Backbone.Model.extend({initialize:function(l){this._checkEnabledSessionStorage();if(!l.id){throw new Error("SessionStorageModel requires an id in the initial attributes")}this.id=l.id;var k=(!this.isNew())?(this._read(this)):({});this.clear({silent:true});this.save(_.extend({},this.defaults,k,l),{silent:true});this.on("change",function(){this.save()})},_checkEnabledSessionStorage:function(){try{return sessionStorage.length}catch(k){alert("Please enable cookies in your browser for this Galaxy site");return false}},sync:function(n,l,k){if(!k.silent){l.trigger("request",l,{},k)}var m;switch(n){case"create":m=this._create(l);break;case"read":m=this._read(l);break;case"update":m=this._update(l);break;case"delete":m=this._delete(l);break}if(m!==undefined||m!==null){if(k.success){k.success()}}else{if(k.error){k.error()}}return m},_create:function(k){var l=k.toJSON(),m=sessionStorage.setItem(k.id,JSON.stringify(l));return(m===null)?(m):(l)},_read:function(k){return JSON.parse(sessionStorage.getItem(k.id))},_update:function(k){return k._create(k)},_delete:function(k){return sessionStorage.removeItem(k.id)},isNew:function(){return !sessionStorage.hasOwnProperty(this.id)},_log:function(){return JSON.stringify(this.toJSON(),null," ")},toString:function(){return"SessionStorageModel("+this.id+")"}});(function(){a.prototype=_.omit(a.prototype,"url","urlRoot")}());function j(n,m){var k=Array.prototype.slice.call(arguments,0),l=k.pop();k.unshift(l);return _.defaults.apply(_,k)}var g={searchAttributes:[],searchAliases:{},searchAttribute:function(m,k){var l=this.get(m);if(!k||(l===undefined||l===null)){return false}if(_.isArray(l)){return this._searchArrayAttribute(l,k)}return(l.toString().toLowerCase().indexOf(k.toLowerCase())!==-1)},_searchArrayAttribute:function(l,k){k=k.toLowerCase();return _.any(l,function(m){return(m.toString().toLowerCase().indexOf(k.toLowerCase())!==-1)})},search:function(k){var l=this;return _.filter(this.searchAttributes,function(m){return l.searchAttribute(m,k)})},matches:function(l){var n="=",k=l.split(n);if(k.length>=2){var m=k[0];m=this.searchAliases[m]||m;return this.searchAttribute(m,k[1])}return !!this.search(l).length},matchesAll:function(l){var k=this;l=l.match(/(".*"|\w*=".*"|\S*)/g).filter(function(m){return !!m});return _.all(l,function(m){m=m.replace(/"/g,"");return k.matches(m)})}};var c={hiddenUntilActivated:function(k,m){m=m||{};this.HUAVOptions={$elementShown:this.$el,showFn:jQuery.prototype.toggle,showSpeed:"fast"};_.extend(this.HUAVOptions,m||{});this.HUAVOptions.hasBeenShown=this.HUAVOptions.$elementShown.is(":visible");this.hidden=this.isHidden();if(k){var l=this;k.on("click",function(n){l.toggle(l.HUAVOptions.showSpeed)})}},isHidden:function(){return(this.HUAVOptions.$elementShown.is(":hidden"))},toggle:function(){if(this.hidden){if(!this.HUAVOptions.hasBeenShown){if(_.isFunction(this.HUAVOptions.onshowFirstTime)){this.HUAVOptions.hasBeenShown=true;this.HUAVOptions.onshowFirstTime.call(this)}}if(_.isFunction(this.HUAVOptions.onshow)){this.HUAVOptions.onshow.call(this);this.trigger("hiddenUntilActivated:shown",this)}this.hidden=false}else{if(_.isFunction(this.HUAVOptions.onhide)){this.HUAVOptions.onhide.call(this);this.trigger("hiddenUntilActivated:hidden",this)}this.hidden=true}return this.HUAVOptions.showFn.apply(this.HUAVOptions.$elementShown,arguments)}};var f={initialize:function(k){this.draggable=k.draggable||false},$dragHandle:function(){return this.$(".title-bar")},toggleDraggable:function(){if(this.draggable){this.draggableOff()}else{this.draggableOn()}},draggableOn:function(){this.draggable=true;this.dragStartHandler=_.bind(this._dragStartHandler,this);this.dragEndHandler=_.bind(this._dragEndHandler,this);var k=this.$dragHandle().attr("draggable",true).get(0);k.addEventListener("dragstart",this.dragStartHandler,false);k.addEventListener("dragend",this.dragEndHandler,false)},draggableOff:function(){this.draggable=false;var k=this.$dragHandle().attr("draggable",false).get(0);k.removeEventListener("dragstart",this.dragStartHandler,false);k.removeEventListener("dragend",this.dragEndHandler,false)},_dragStartHandler:function(k){this.trigger("dragstart",this);k.dataTransfer.effectAllowed="move";k.dataTransfer.setData("text",JSON.stringify(this.model.toJSON()));return false},_dragEndHandler:function(k){this.trigger("dragend",this);return false}};var d={initialize:function(k){this.selectable=k.selectable||false;this.selected=k.selected||false},$selector:function(){return this.$(".selector")},_renderSelected:function(){this.$selector().find("span").toggleClass("fa-check-square-o",this.selected).toggleClass("fa-square-o",!this.selected)},toggleSelector:function(){if(!this.$selector().is(":visible")){this.showSelector()}else{this.hideSelector()}},showSelector:function(k){k=k!==undefined?k:this.fxSpeed;this.selectable=true;this.trigger("selectable",true,this);this._renderSelected();this.$selector().show(k)},hideSelector:function(k){k=k!==undefined?k:this.fxSpeed;this.selectable=false;this.trigger("selectable",false,this);this.$selector().hide(k)},toggleSelect:function(k){if(this.selected){this.deselect(k)}else{this.select(k)}},select:function(k){if(!this.selected){this.trigger("selected",this,k);this.selected=true;this._renderSelected()}return false},deselect:function(k){if(this.selected){this.trigger("de-selected",this,k);this.selected=false;this._renderSelected()}return false}};function e(l,k){k=k||"model";var m=_.template(l.join(""));return function(o,n){var p={view:n||{},_l:b};p[k]=o||{};return m(p)}}return{LoggableMixin:i,SessionStorageModel:a,mixin:j,SearchableModelMixin:g,HiddenUntilActivatedViewMixin:c,DraggableViewMixin:f,SelectableViewMixin:d,wrapTemplate:e}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/collection/collection-li.js b/static/scripts/packed/mvc/collection/collection-li.js index f07aab4f9f9..18cba771760 100644 --- a/static/scripts/packed/mvc/collection/collection-li.js +++ b/static/scripts/packed/mvc/collection/collection-li.js @@ -1 +1 @@ -define(["mvc/dataset/dataset-li","mvc/base-mvc","utils/localization"],function(h,d,f){var c=d.ListItemView;var a=c.extend({className:c.prototype.className+" dataset-collection",id:function(){return["dataset_collection",this.model.get("id")].join("-")},fxSpeed:"fast",initialize:function(i){if(i.logger){this.logger=this.model.logger=i.logger}this.log(this+"(DCListItemView).initialize:",i);c.prototype.initialize.call(this,i)},expand:function(){var i=this;return i._fetchModelDetails().always(function(){i.trigger("expanded",i)})},_renderSubtitle:function(){var i=$('
');switch(this.model.get("collection_type")){case"list":return i.text(f("a list of datasets"));case"paired":return i.text(f("a pair of datasets"));case"list:paired":return i.text(f("a list of paired datasets"))}return i},toString:function(){var i=(this.model)?(this.model+""):("(no model)");return"DCListItemView("+i+")"}});a.prototype.templates=(function(){var i=d.wrapTemplate(['
','
','<%- collection.element_identifier || collection.name %>',"
",'
',"
"],"collection");return _.extend({},c.prototype.templates,{titleBar:i})}());var b=c.extend({logger:console,className:c.prototype.className+" dataset-collection-element",fxSpeed:"fast",initialize:function(i){if(i.logger){this.logger=this.model.logger=i.logger}this.log("DCEListItemView.initialize:",i);c.prototype.initialize.call(this,i)},toString:function(){var i=(this.model)?(this.model+""):("(no model)");return"DCEListItemView("+i+")"}});b.prototype.templates=(function(){var i=d.wrapTemplate(['
','
','<%- element.element_identifier %>',"
",'
',"
"],"element");return _.extend({},c.prototype.templates,{titleBar:i})}());var e=h.DatasetListItemView.extend({className:h.DatasetListItemView.prototype.className+" dataset-collection-element",initialize:function(i){if(i.logger){this.logger=this.model.logger=i.logger}this.log("DatasetDCEListItemView.initialize:",i);h.DatasetListItemView.prototype.initialize.call(this,i)},toString:function(){var i=(this.model)?(this.model+""):("(no model)");return"DatasetDCEListItemView("+i+")"}});e.prototype.templates=(function(){var i=d.wrapTemplate(['
','','
','<%- element.element_identifier %>',"
","
"],"element");return _.extend({},h.DatasetListItemView.prototype.templates,{titleBar:i})}());var g=a.extend({className:a.prototype.className+" dataset-collection-element",_swapNewRender:function(i){a.prototype._swapNewRender.call(this,i);var j=this.model.get("state")||"ok";this.$el.addClass("state-"+j);return this.$el},toString:function(){var i=(this.model)?(this.model+""):("(no model)");return"NestedDCDCEListItemView("+i+")"}});return{DCListItemView:a,DCEListItemView:b,DatasetDCEListItemView:e,NestedDCDCEListItemView:g}}); \ No newline at end of file +define(["mvc/list/list-item","mvc/dataset/dataset-li","mvc/base-mvc","utils/localization"],function(a,h,i,d){var e=a.ListItemView;var c=e.extend({className:e.prototype.className+" dataset-collection",id:function(){return["dataset_collection",this.model.get("id")].join("-")},fxSpeed:"fast",initialize:function(j){if(j.logger){this.logger=this.model.logger=j.logger}this.log(this+"(DCListItemView).initialize:",j);e.prototype.initialize.call(this,j)},expand:function(){var j=this;return j._fetchModelDetails().always(function(){j.trigger("expanded",j)})},_renderSubtitle:function(){var j=$('
');switch(this.model.get("collection_type")){case"list":return j.text(d("a list of datasets"));case"paired":return j.text(d("a pair of datasets"));case"list:paired":return j.text(d("a list of paired datasets"))}return j},toString:function(){var j=(this.model)?(this.model+""):("(no model)");return"DCListItemView("+j+")"}});c.prototype.templates=(function(){var j=i.wrapTemplate(['
','
','<%- collection.element_identifier || collection.name %>',"
",'
',"
"],"collection");return _.extend({},e.prototype.templates,{titleBar:j})}());var f=e.extend({className:e.prototype.className+" dataset-collection-element",fxSpeed:"fast",initialize:function(j){if(j.logger){this.logger=this.model.logger=j.logger}this.log("DCEListItemView.initialize:",j);e.prototype.initialize.call(this,j)},toString:function(){var j=(this.model)?(this.model+""):("(no model)");return"DCEListItemView("+j+")"}});f.prototype.templates=(function(){var j=i.wrapTemplate(['
','
','<%- element.element_identifier %>',"
",'
',"
"],"element");return _.extend({},e.prototype.templates,{titleBar:j})}());var b=h.DatasetListItemView.extend({className:h.DatasetListItemView.prototype.className+" dataset-collection-element",initialize:function(j){if(j.logger){this.logger=this.model.logger=j.logger}this.log("DatasetDCEListItemView.initialize:",j);h.DatasetListItemView.prototype.initialize.call(this,j)},toString:function(){var j=(this.model)?(this.model+""):("(no model)");return"DatasetDCEListItemView("+j+")"}});b.prototype.templates=(function(){var j=i.wrapTemplate(['
','','
','<%- element.element_identifier %>',"
","
"],"element");return _.extend({},h.DatasetListItemView.prototype.templates,{titleBar:j})}());var g=c.extend({className:c.prototype.className+" dataset-collection-element",_swapNewRender:function(j){c.prototype._swapNewRender.call(this,j);var k=this.model.get("state")||"ok";this.$el.addClass("state-"+k);return this.$el},toString:function(){var j=(this.model)?(this.model+""):("(no model)");return"NestedDCDCEListItemView("+j+")"}});return{DCListItemView:c,DCEListItemView:f,DatasetDCEListItemView:b,NestedDCDCEListItemView:g}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/collection/collection-model.js b/static/scripts/packed/mvc/collection/collection-model.js index 8f875877880..7879926cae7 100644 --- a/static/scripts/packed/mvc/collection/collection-model.js +++ b/static/scripts/packed/mvc/collection/collection-model.js @@ -1 +1 @@ -define(["mvc/dataset/dataset-model","mvc/base-mvc","utils/localization"],function(l,o,d){var e={defaults:{model_class:"DatasetCollectionElement",element_identifier:null,element_index:null,element_type:null},_mergeObject:function(q){_.extend(q,q.object);delete q.object;return q},constructor:function(q,r){this.debug("\t DatasetCollectionElement.constructor:",q,r);q=this._mergeObject(q);Backbone.Model.apply(this,arguments)},parse:function(r,s){var q=r;q=this._mergeObject(q);return q}};var p=Backbone.Model.extend(o.LoggableMixin).extend(e);var m=Backbone.Collection.extend(o.LoggableMixin).extend({model:p,initialize:function(q,r){this.debug(this+"(DCECollection).initialize:",q,r);r=r||{}},toString:function(){return(["DatasetCollectionElementCollection(",this.length,")"].join(""))}});var f=l.DatasetAssociation.extend(o.mixin(e,{defaults:_.extend({},l.DatasetAssociation.prototype.defaults,e.defaults),constructor:function(q,r){this.debug("\t DatasetDCE.constructor:",q,r);e.constructor.call(this,q,r)},initialize:function(q,r){this.debug(this+"(DatasetDCE).initialize:",q,r);l.DatasetAssociation.prototype.initialize.call(this,q,r)},toString:function(){var q=this.get("element_identifier");return(["DatasetDCE(",q,")"].join(""))}}));var k=m.extend({model:f,initialize:function(q,r){this.debug(this+"(DatasetDCECollection).initialize:",q,r);m.prototype.initialize.call(this,q,r)},toString:function(){return(["DatasetDCECollection(",this.length,")"].join(""))}});var h=Backbone.Model.extend(o.LoggableMixin).extend(o.SearchableModelMixin).extend({defaults:{collection_type:null,deleted:false},collectionClass:m,initialize:function(r,q){this.debug(this+"(DatasetCollection).initialize:",r,q,this);this.elements=this._createElementsModel();this.on("change:elements",function(){this.log("change:elements");this.elements=this._createElementsModel()})},_createElementsModel:function(){this.debug(this+"._createElementsModel",this.collectionClass,this.get("elements"),this.elements);var q=this.get("elements")||[];this.unset("elements",{silent:true});this.elements=new this.collectionClass(q);return this.elements},toJSON:function(){var q=Backbone.Model.prototype.toJSON.call(this);if(this.elements){q.elements=this.elements.toJSON()}return q},inReadyState:function(){return true},hasDetails:function(){this.debug("hasDetails:",this.elements.length);return this.elements.length!==0},getVisibleContents:function(q){return this.elements},"delete":function(q){if(this.get("deleted")){return jQuery.when()}return this.save({deleted:true},q)},undelete:function(q){if(!this.get("deleted")||this.get("purged")){return jQuery.when()}return this.save({deleted:false},q)},searchAttributes:["name"],toString:function(){var q=[this.get("id"),this.get("name")||this.get("element_identifier")];return"DatasetCollection("+(q.join(","))+")"}});var g=h.extend({collectionClass:k,initialize:function(r,q){this.debug(this+"(ListDatasetCollection).initialize:",r,q);h.prototype.initialize.call(this,r,q)},toString:function(){return(["ListDatasetCollection(",this.get("name"),")"].join(""))}});var c=g.extend({initialize:function(r,q){this.debug(this+"(PairDatasetCollection).initialize:",r,q);g.prototype.initialize.call(this,r,q)},toString:function(){return(["PairDatasetCollection(",this.get("name"),")"].join(""))}});var i=h.extend(o.mixin(e,{constructor:function(q,r){this.debug("\t NestedDCDCE.constructor:",q,r);e.constructor.call(this,q,r)},toString:function(){var q=(this.object)?(""+this.object):(this.get("element_identifier"));return(["NestedDCDCE(",q,")"].join(""))}}));var n=m.extend({model:i,initialize:function(r,q){this.debug(this+"(NestedDCDCECollection).initialize:",r,q);m.prototype.initialize.call(this,r,q)},toString:function(){return(["NestedDCDCECollection(",this.length,")"].join(""))}});var a=c.extend(o.mixin(e,{constructor:function(q,r){this.debug("\t NestedPairDCDCE.constructor:",q,r);e.constructor.call(this,q,r)},toString:function(){var q=(this.object)?(""+this.object):(this.get("element_identifier"));return(["NestedPairDCDCE(",q,")"].join(""))}}));var j=n.extend({model:a,initialize:function(r,q){this.debug(this+"(NestedPairDCDCECollection).initialize:",r,q);n.prototype.initialize.call(this,r,q)},toString:function(){return(["NestedPairDCDCECollection(",this.length,")"].join(""))}});var b=h.extend({collectionClass:j,initialize:function(q,r){this.debug(this+"(ListPairedDatasetCollection).initialize:",q,r);h.prototype.initialize.call(this,q,r)},toString:function(){return(["ListPairedDatasetCollection(",this.get("name"),")"].join(""))}});return{ListDatasetCollection:g,PairDatasetCollection:c,ListPairedDatasetCollection:b}}); \ No newline at end of file +define(["mvc/dataset/dataset-model","mvc/base-mvc","utils/localization"],function(l,o,d){var e={defaults:{model_class:"DatasetCollectionElement",element_identifier:null,element_index:null,element_type:null},_mergeObject:function(q){_.extend(q,q.object);delete q.object;return q},constructor:function(q,r){this.debug("\t DatasetCollectionElement.constructor:",q,r);q=this._mergeObject(q);Backbone.Model.apply(this,arguments)},parse:function(r,s){var q=r;q=this._mergeObject(q);return q}};var p=Backbone.Model.extend(o.LoggableMixin).extend(e);var m=Backbone.Collection.extend(o.LoggableMixin).extend({model:p,initialize:function(q,r){this.debug(this+"(DCECollection).initialize:",q,r);r=r||{}},toString:function(){return(["DatasetCollectionElementCollection(",this.length,")"].join(""))}});var f=l.DatasetAssociation.extend(o.mixin(e,{defaults:_.extend({},l.DatasetAssociation.prototype.defaults,e.defaults),constructor:function(q,r){this.debug("\t DatasetDCE.constructor:",q,r);e.constructor.call(this,q,r)},initialize:function(q,r){this.debug(this+"(DatasetDCE).initialize:",q,r);l.DatasetAssociation.prototype.initialize.call(this,q,r)},toString:function(){var q=this.get("element_identifier");return(["DatasetDCE(",q,")"].join(""))}}));var k=m.extend({model:f,initialize:function(q,r){this.debug(this+"(DatasetDCECollection).initialize:",q,r);m.prototype.initialize.call(this,q,r)},toString:function(){return(["DatasetDCECollection(",this.length,")"].join(""))}});var h=Backbone.Model.extend(o.LoggableMixin).extend(o.SearchableModelMixin).extend({defaults:{collection_type:null,deleted:false},collectionClass:m,initialize:function(r,q){this.debug(this+"(DatasetCollection).initialize:",r,q,this);this.elements=this._createElementsModel();this.on("change:elements",function(){this.log("change:elements");this.elements=this._createElementsModel()})},_createElementsModel:function(){this.debug(this+"._createElementsModel",this.collectionClass,this.get("elements"),this.elements);var q=this.get("elements")||[];this.unset("elements",{silent:true});this.elements=new this.collectionClass(q);return this.elements},toJSON:function(){var q=Backbone.Model.prototype.toJSON.call(this);if(this.elements){q.elements=this.elements.toJSON()}return q},inReadyState:function(){return true},hasDetails:function(){this.debug("hasDetails:",this.elements.length);return this.elements.length!==0},getVisibleContents:function(q){return this.elements},"delete":function(q){if(this.get("deleted")){return jQuery.when()}return this.save({deleted:true},q)},undelete:function(q){if(!this.get("deleted")||this.get("purged")){return jQuery.when()}return this.save({deleted:false},q)},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},searchAttributes:["name"],toString:function(){var q=[this.get("id"),this.get("name")||this.get("element_identifier")];return"DatasetCollection("+(q.join(","))+")"}});var g=h.extend({collectionClass:k,initialize:function(r,q){this.debug(this+"(ListDatasetCollection).initialize:",r,q);h.prototype.initialize.call(this,r,q)},toString:function(){return(["ListDatasetCollection(",this.get("name"),")"].join(""))}});var c=g.extend({initialize:function(r,q){this.debug(this+"(PairDatasetCollection).initialize:",r,q);g.prototype.initialize.call(this,r,q)},toString:function(){return(["PairDatasetCollection(",this.get("name"),")"].join(""))}});var i=h.extend(o.mixin(e,{constructor:function(q,r){this.debug("\t NestedDCDCE.constructor:",q,r);e.constructor.call(this,q,r)},toString:function(){var q=(this.object)?(""+this.object):(this.get("element_identifier"));return(["NestedDCDCE(",q,")"].join(""))}}));var n=m.extend({model:i,initialize:function(r,q){this.debug(this+"(NestedDCDCECollection).initialize:",r,q);m.prototype.initialize.call(this,r,q)},toString:function(){return(["NestedDCDCECollection(",this.length,")"].join(""))}});var a=c.extend(o.mixin(e,{constructor:function(q,r){this.debug("\t NestedPairDCDCE.constructor:",q,r);e.constructor.call(this,q,r)},toString:function(){var q=(this.object)?(""+this.object):(this.get("element_identifier"));return(["NestedPairDCDCE(",q,")"].join(""))}}));var j=n.extend({model:a,initialize:function(r,q){this.debug(this+"(NestedPairDCDCECollection).initialize:",r,q);n.prototype.initialize.call(this,r,q)},toString:function(){return(["NestedPairDCDCECollection(",this.length,")"].join(""))}});var b=h.extend({collectionClass:j,initialize:function(q,r){this.debug(this+"(ListPairedDatasetCollection).initialize:",q,r);h.prototype.initialize.call(this,q,r)},toString:function(){return(["ListPairedDatasetCollection(",this.get("name"),")"].join(""))}});return{ListDatasetCollection:g,PairDatasetCollection:c,ListPairedDatasetCollection:b}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/collection/collection-panel.js b/static/scripts/packed/mvc/collection/collection-panel.js index 3b4ac84315a..6a3f5838f3e 100644 --- a/static/scripts/packed/mvc/collection/collection-panel.js +++ b/static/scripts/packed/mvc/collection/collection-panel.js @@ -1 +1 @@ -define(["mvc/collection/collection-model","mvc/collection/collection-li","mvc/base-mvc","utils/localization"],function(h,f,a,e){var d=Backbone.View.extend(a.LoggableMixin).extend({tagName:"div",className:"dataset-collection-panel",fxSpeed:"fast",DatasetDCEViewClass:f.DatasetDCEListItemView,NestedDCDCEViewClass:f.NestedDCDCEListItemView,initialize:function(i){i=i||{};if(i.logger){this.logger=i.logger}this.log(this+".initialize:",i);this.linkTarget=i.linkTarget||"_blank";this.hasUser=i.hasUser;this.panelStack=[];this.parentName=i.parentName},_setUpListeners:function(){this.on("all",function(i){this.log(this+"",arguments)},this);return this},_setUpModelEventHandlers:function(){return this},render:function(k,l){this.log("render:",k,l);k=(k===undefined)?(this.fxSpeed):(k);var i=this,j;if(!this.model){return this}j=this.renderModel();$(i).queue("fx",[function(m){if(k&&i.$el.is(":visible")){i.$el.fadeOut(k,m)}else{m()}},function(m){i.$el.empty();if(j){i.$el.append(j.children())}m()},function(m){if(k&&!i.$el.is(":visible")){i.$el.fadeIn(k,m)}else{m()}},function(m){if(l){l.call(this)}i.trigger("rendered",this);m()}]);return this},renderModel:function(){var k=this.model.get("collection_type")||this.model.object.get("collection_type"),j=_.extend(this.model.toJSON(),{parentName:this.parentName,type:k}),i=$("
").append(this.templates.panel(j));this._setUpBehaviours(i);this.renderContents(i);return i},_setUpBehaviours:function(i){i=i||this.$el;i.find("[title]").tooltip({placement:"bottom"});return this},$container:function(){return(this.findContainerFn)?(this.findContainerFn.call(this)):(this.$el.parent())},$datasetsList:function(i){return(i||this.$el).find(".datasets-list")},renderContents:function(j){j=j||this.$el;this.warn(this+".renderContents:, model:",this.model);var i=this,l={},k=this.model.getVisibleContents();this.$datasetsList(j).empty();if(k&&k.length){k.each(function(n){var o=n.id,m=i._createContentView(n);l[o]=m;i._attachContentView(m.render(),j)})}this.contentViews=l;return this.contentViews},_createContentView:function(k){var j=null,i=this._getContentClass(k);j=new i({model:k,linkTarget:this.linkTarget,hasUser:this.hasUser,logger:this.logger});this._setUpContentListeners(j);return j},_getContentClass:function(i){switch(i.get("element_type")){case"hda":return this.DatasetDCEViewClass;case"dataset_collection":return this.NestedDCDCEViewClass}throw new TypeError("Unknown element type:",i.get("element_type"))},_setUpContentListeners:function(j){var i=this;if(j.model.get("element_type")==="dataset_collection"){j.on("expanded",function(k){i.info("expanded",k);i._addCollectionPanel(k)})}},_addCollectionPanel:function(k){var l=this,i=k.model;var j=new c({model:i,parentName:this.model.get("name"),linkTarget:this.linkTarget});l.panelStack.push(j);l.$(".controls").add(".datasets-list").hide();l.$el.append(j.$el);j.on("close",function(){l.render();k.collapse();l.panelStack.pop()});if(!j.model.hasDetails()){var m=j.model.fetch();m.done(function(){j.render()})}else{j.render()}},_attachContentView:function(k,j){j=j||this.$el;var i=this.$datasetsList(j);i.append(k.$el);return this},events:{"click .navigation .back":"close"},close:function(i){this.$el.remove();this.trigger("close")},toString:function(){return"CollectionPanel("+((this.model)?(this.model.get("name")):(""))+")"}});d.templates=d.prototype.templates=(function(){var i=_.template(['
','",'
','
<%- collection.name || collection.element_identifier %>
','
','<% if( collection.type === "list" ){ %>',e("a list of datasets"),'<% } else if( collection.type === "paired" ){ %>',e("a pair of datasets"),'<% } else if( collection.type === "list:paired" ){ %>',e("a list of paired datasets"),"<% } %>","
","
","
",'
'].join(""));return{panel:function(j){return i({_l:e,collection:j})}}}());var b=d.extend({DatasetDCEViewClass:f.DatasetDCEListItemView,toString:function(){return"ListCollectionPanel("+((this.model)?(this.model.get("name")):(""))+")"}});var c=b.extend({toString:function(){return"PairCollectionPanel("+((this.model)?(this.model.get("name")):(""))+")"}});var g=d.extend({NestedDCDCEViewClass:f.NestedDCDCEListItemView,toString:function(){return"ListOfPairsCollectionPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{CollectionPanel:d,ListCollectionPanel:b,PairCollectionPanel:c,ListOfPairsCollectionPanel:g}}); \ No newline at end of file +define(["mvc/list/list-panel","mvc/collection/collection-model","mvc/collection/collection-li","mvc/base-mvc","utils/localization"],function(a,d,f,j,b){var h=a.ModelListPanel;var g=h.extend({className:h.prototype.className+" dataset-collection-panel",DatasetDCEViewClass:f.DatasetDCEListItemView,NestedDCDCEViewClass:f.NestedDCDCEListItemView,modelCollectionKey:"elements",initialize:function(k){h.prototype.initialize.call(this,k);this.linkTarget=k.linkTarget||"_blank";this.hasUser=k.hasUser;this.panelStack=[];this.parentName=k.parentName},_filterCollection:function(){return this.model.getVisibleContents()},_getItemViewClass:function(k){switch(k.get("element_type")){case"hda":return this.DatasetDCEViewClass;case"dataset_collection":return this.NestedDCDCEViewClass}throw new TypeError("Unknown element type:",k.get("element_type"))},_getItemViewOptions:function(l){var k=h.prototype._getItemViewOptions.call(this,l);return _.extend(k,{linkTarget:this.linkTarget,hasUser:this.hasUser})},_setUpItemViewListeners:function(l){var k=this;h.prototype._setUpItemViewListeners.call(k,l);if(l.model.get("element_type")==="dataset_collection"){l.on("expanded",function(m){k.info("expanded",m);k._addCollectionPanel(m)})}return k},_addCollectionPanel:function(m){var n=this,k=m.model;var l=new i({model:k,parentName:this.model.get("name"),linkTarget:this.linkTarget});n.panelStack.push(l);n.$(".controls").add(".list-items").hide();n.$el.append(l.$el);l.on("close",function(){n.render();m.collapse();n.panelStack.pop()});if(!l.model.hasDetails()){var o=l.model.fetch();o.done(function(){l.render()})}else{l.render()}},events:{"click .navigation .back":"close"},close:function(k){this.$el.remove();this.trigger("close")},toString:function(){return"CollectionPanel("+((this.model)?(this.model.get("name")):(""))+")"}});g.prototype.templates=(function(){var k=j.wrapTemplate(['
','",'
','
<%- collection.name || collection.element_identifier %>
','
','<% if( collection.collection_type === "list" ){ %>',b("a list of datasets"),'<% } else if( collection.collection_type === "paired" ){ %>',b("a pair of datasets"),'<% } else if( collection.collection_type === "list:paired" ){ %>',b("a list of paired datasets"),"<% } %>","
","
","
"],"collection");return _.extend(_.clone(h.prototype.templates),{controls:k})}());var e=g.extend({DatasetDCEViewClass:f.DatasetDCEListItemView,toString:function(){return"ListCollectionPanel("+((this.model)?(this.model.get("name")):(""))+")"}});var i=e.extend({toString:function(){return"PairCollectionPanel("+((this.model)?(this.model.get("name")):(""))+")"}});var c=g.extend({NestedDCDCEViewClass:f.NestedDCDCEListItemView,toString:function(){return"ListOfPairsCollectionPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{CollectionPanel:g,ListCollectionPanel:e,PairCollectionPanel:i,ListOfPairsCollectionPanel:c}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/dataset/dataset-li.js b/static/scripts/packed/mvc/dataset/dataset-li.js index 6043b636048..ee7c34cec99 100644 --- a/static/scripts/packed/mvc/dataset/dataset-li.js +++ b/static/scripts/packed/mvc/dataset/dataset-li.js @@ -1 +1 @@ -define(["mvc/dataset/states","mvc/base-mvc","utils/localization"],function(a,b,c){var e=b.ListItemView;var d=e.extend({className:e.prototype.className+" dataset",id:function(){return["dataset",this.model.get("id")].join("-")},initialize:function(f){if(f.logger){this.logger=this.model.logger=f.logger}this.log(this+".initialize:",f);e.prototype.initialize.call(this,f);this.linkTarget=f.linkTarget||"_blank";this._setUpListeners()},_setUpListeners:function(){this.on("selectable",function(f){if(f){this.$(".primary-actions").hide()}else{this.$(".primary-actions").show()}},this);this.model.on("change",function(g,f){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this);this.on("all",function(f){this.log(f)},this)},_fetchModelDetails:function(){var f=this;if(f.model.inReadyState()&&!f.model.hasDetails()){return f.model.fetch({silent:true})}return jQuery.when()},remove:function(g,h){var f=this;g=g||this.fxSpeed;this.$el.fadeOut(g,function(){Backbone.View.prototype.remove.call(f);if(h){h.call(f)}})},render:function(f){return e.prototype.render.call(this,f)},_swapNewRender:function(f){e.prototype._swapNewRender.call(this,f);if(this.model.has("state")){this.$el.addClass("state-"+this.model.get("state"))}return this.$el},_renderPrimaryActions:function(){return[this._renderDisplayButton()]},_renderDisplayButton:function(){var h=this.model.get("state");if((h===a.NOT_VIEWABLE)||(h===a.DISCARDED)||(!this.model.get("accessible"))){return null}var g={target:this.linkTarget,classes:"display-btn"};if(this.model.get("purged")){g.disabled=true;g.title=c("Cannot display datasets removed from disk")}else{if(h===a.UPLOAD){g.disabled=true;g.title=c("This dataset must finish uploading before it can be viewed")}else{if(h===a.NEW){g.disabled=true;g.title=c("This dataset is not yet viewable")}else{g.title=c("View data");g.href=this.model.urls.display;var f=this;g.onclick=function(i){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Data Viewer: "+f.model.get("name"),type:"other",content:function(j){require(["mvc/data"],function(k){var l=new k.TabularDataset({id:f.model.get("id")});$.when(l.fetch()).then(function(){k.createTabularDatasetChunkedView({model:l,parent_elt:j,embedded:true,height:"100%"})})})}});i.preventDefault()}}}}}g.faIcon="fa-eye";return faIconButton(g)},_renderDetails:function(){if(this.model.get("state")===a.NOT_VIEWABLE){return $(this.templates.noAccess(this.model.toJSON(),this))}var f=e.prototype._renderDetails.call(this);f.find(".actions .left").empty().append(this._renderSecondaryActions());f.find(".summary").html(this._renderSummary()).prepend(this._renderDetailMessages());f.find(".display-applications").html(this._renderDisplayApplications());this._setUpBehaviors(f);return f},_renderSummary:function(){var f=this.model.toJSON(),g=this.templates.summaries[f.state];g=g||this.templates.summaries.unknown;return g(f,this)},_renderDetailMessages:function(){var f=this,h=$('
'),g=f.model.toJSON();_.each(f.templates.detailMessages,function(i){h.append($(i(g,f)))});return h},_renderDisplayApplications:function(){if(this.model.isDeletedOrPurged()){return""}return[this.templates.displayApplications(this.model.get("display_apps"),this),this.templates.displayApplications(this.model.get("display_types"),this)].join("")},_renderSecondaryActions:function(){this.debug("_renderSecondaryActions");switch(this.model.get("state")){case a.NOT_VIEWABLE:return[];case a.OK:case a.FAILED_METADATA:case a.ERROR:return[this._renderDownloadButton(),this._renderShowParamsButton()]}return[this._renderShowParamsButton()]},_renderShowParamsButton:function(){return faIconButton({title:c("View details"),classes:"params-btn",href:this.model.urls.show_params,target:this.linkTarget,faIcon:"fa-info-circle"})},_renderDownloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}if(!_.isEmpty(this.model.get("meta_files"))){return this._renderMetaFileDownloadButton()}return $(['','',""].join(""))},_renderMetaFileDownloadButton:function(){var f=this.model.urls;return $(['"].join("\n"))},toString:function(){var f=(this.model)?(this.model+""):("(no model)");return"DatasetListItemView("+f+")"}});d.prototype.templates=(function(){var h=_.extend({},e.prototype.templates.warnings,{failed_metadata:b.wrapTemplate(['<% if( model.state === "failed_metadata" ){ %>','
',c("An error occurred setting the metadata for this dataset"),"
","<% } %>"]),error:b.wrapTemplate(["<% if( model.error ){ %>",'
',c("There was an error getting the data for this dataset"),": <%- model.error %>","
","<% } %>"]),purged:b.wrapTemplate(["<% if( model.purged ){ %>",'
',c("This dataset has been deleted and removed from disk"),"
","<% } %>"]),deleted:b.wrapTemplate(["<% if( model.deleted && !model.purged ){ %>",'
',c("This dataset has been deleted"),"
","<% } %>"])});var i=b.wrapTemplate(['
','
','
','
','
',"
","<% if( !dataset.deleted && !dataset.purged ){ %>",'
','
','
',"<% if( dataset.peek ){ %>",'
<%= dataset.peek %>
',"<% } %>","<% } %>","
"],"dataset");var g=b.wrapTemplate(['
','
',c("You do not have permission to view this dataset"),"
","
"],"dataset");var j={};j[a.OK]=j[a.FAILED_METADATA]=b.wrapTemplate(["<% if( dataset.misc_blurb ){ %>",'
','<%- dataset.misc_blurb %>',"
","<% } %>","<% if( dataset.file_ext ){ %>",'
','",'<%- dataset.file_ext %>',"
","<% } %>","<% if( dataset.metadata_dbkey ){ %>",'
','",'',"<%- dataset.metadata_dbkey %>","","
","<% } %>","<% if( dataset.misc_info ){ %>",'
','<%- dataset.misc_info %>',"
","<% } %>"],"dataset");j[a.NEW]=b.wrapTemplate(["
",c("This is a new dataset and not all of its data are available yet"),"
"],"dataset");j[a.NOT_VIEWABLE]=b.wrapTemplate(["
",c("You do not have permission to view this dataset"),"
"],"dataset");j[a.DISCARDED]=b.wrapTemplate(["
",c("The job creating this dataset was cancelled before completion"),"
"],"dataset");j[a.QUEUED]=b.wrapTemplate(["
",c("This job is waiting to run"),"
"],"dataset");j[a.RUNNING]=b.wrapTemplate(["
",c("This job is currently running"),"
"],"dataset");j[a.UPLOAD]=b.wrapTemplate(["
",c("This dataset is currently uploading"),"
"],"dataset");j[a.SETTING_METADATA]=b.wrapTemplate(["
",c("Metadata is being auto-detected"),"
"],"dataset");j[a.PAUSED]=b.wrapTemplate(["
",c('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume'),"
"],"dataset");j[a.ERROR]=b.wrapTemplate(["<% if( !dataset.purged ){ %>","
<%- dataset.misc_blurb %>
","<% } %>",'',c("An error occurred with this dataset"),":",'
<%- dataset.misc_info %>
'],"dataset");j[a.EMPTY]=b.wrapTemplate(["
",c("No data"),": <%- dataset.misc_blurb %>
"],"dataset");j.unknown=b.wrapTemplate(['
Error: unknown dataset state: "<%- dataset.state %>"
'],"dataset");var k={resubmitted:b.wrapTemplate(["<% if( model.resubmitted ){ %>",'
',c("The job creating this dataset has been resubmitted"),"
","<% } %>"])};var f=b.wrapTemplate(["<% _.each( apps, function( app ){ %>",'
','<%- app.label %> ','',"<% _.each( app.links, function( link ){ %>",'',"<% print( _l( link.text ) ); %>"," ","<% }); %>","","
","<% }); %>"],"apps");return _.extend({},e.prototype.templates,{warnings:h,details:i,noAccess:g,summaries:j,detailMessages:k,displayApplications:f})}());return{DatasetListItemView:d}}); \ No newline at end of file +define(["mvc/list/list-item","mvc/dataset/states","mvc/base-mvc","utils/localization"],function(d,a,b,c){var f=d.ListItemView;var e=f.extend({className:f.prototype.className+" dataset",id:function(){return["dataset",this.model.get("id")].join("-")},initialize:function(g){if(g.logger){this.logger=this.model.logger=g.logger}this.log(this+".initialize:",g);f.prototype.initialize.call(this,g);this.linkTarget=g.linkTarget||"_blank";this._setUpListeners()},_setUpListeners:function(){this.on("selectable",function(g){if(g){this.$(".primary-actions").hide()}else{this.$(".primary-actions").show()}},this);this.model.on("change",function(h,g){if(this.model.changedAttributes().state&&this.model.inReadyState()&&this.expanded&&!this.model.hasDetails()){this.model.fetch()}else{this.render()}},this);this.on("all",function(g){this.log(g)},this)},_fetchModelDetails:function(){var g=this;if(g.model.inReadyState()&&!g.model.hasDetails()){return g.model.fetch({silent:true})}return jQuery.when()},remove:function(h,i){var g=this;h=h||this.fxSpeed;this.$el.fadeOut(h,function(){Backbone.View.prototype.remove.call(g);if(i){i.call(g)}})},render:function(g){return f.prototype.render.call(this,g)},_swapNewRender:function(g){f.prototype._swapNewRender.call(this,g);if(this.model.has("state")){this.$el.addClass("state-"+this.model.get("state"))}return this.$el},_renderPrimaryActions:function(){return[this._renderDisplayButton()]},_renderDisplayButton:function(){var i=this.model.get("state");if((i===a.NOT_VIEWABLE)||(i===a.DISCARDED)||(!this.model.get("accessible"))){return null}var h={target:this.linkTarget,classes:"display-btn"};if(this.model.get("purged")){h.disabled=true;h.title=c("Cannot display datasets removed from disk")}else{if(i===a.UPLOAD){h.disabled=true;h.title=c("This dataset must finish uploading before it can be viewed")}else{if(i===a.NEW){h.disabled=true;h.title=c("This dataset is not yet viewable")}else{h.title=c("View data");h.href=this.model.urls.display;var g=this;h.onclick=function(j){if(Galaxy.frame&&Galaxy.frame.active){Galaxy.frame.add({title:"Data Viewer: "+g.model.get("name"),type:"other",content:function(k){require(["mvc/data"],function(l){var m=new l.TabularDataset({id:g.model.get("id")});$.when(m.fetch()).then(function(){l.createTabularDatasetChunkedView({model:m,parent_elt:k,embedded:true,height:"100%"})})})}});j.preventDefault()}}}}}h.faIcon="fa-eye";return faIconButton(h)},_renderDetails:function(){if(this.model.get("state")===a.NOT_VIEWABLE){return $(this.templates.noAccess(this.model.toJSON(),this))}var g=f.prototype._renderDetails.call(this);g.find(".actions .left").empty().append(this._renderSecondaryActions());g.find(".summary").html(this._renderSummary()).prepend(this._renderDetailMessages());g.find(".display-applications").html(this._renderDisplayApplications());this._setUpBehaviors(g);return g},_renderSummary:function(){var g=this.model.toJSON(),h=this.templates.summaries[g.state];h=h||this.templates.summaries.unknown;return h(g,this)},_renderDetailMessages:function(){var g=this,i=$('
'),h=g.model.toJSON();_.each(g.templates.detailMessages,function(j){i.append($(j(h,g)))});return i},_renderDisplayApplications:function(){if(this.model.isDeletedOrPurged()){return""}return[this.templates.displayApplications(this.model.get("display_apps"),this),this.templates.displayApplications(this.model.get("display_types"),this)].join("")},_renderSecondaryActions:function(){this.debug("_renderSecondaryActions");switch(this.model.get("state")){case a.NOT_VIEWABLE:return[];case a.OK:case a.FAILED_METADATA:case a.ERROR:return[this._renderDownloadButton(),this._renderShowParamsButton()]}return[this._renderShowParamsButton()]},_renderShowParamsButton:function(){return faIconButton({title:c("View details"),classes:"params-btn",href:this.model.urls.show_params,target:this.linkTarget,faIcon:"fa-info-circle"})},_renderDownloadButton:function(){if(this.model.get("purged")||!this.model.hasData()){return null}if(!_.isEmpty(this.model.get("meta_files"))){return this._renderMetaFileDownloadButton()}return $(['','',""].join(""))},_renderMetaFileDownloadButton:function(){var g=this.model.urls;return $(['"].join("\n"))},toString:function(){var g=(this.model)?(this.model+""):("(no model)");return"DatasetListItemView("+g+")"}});e.prototype.templates=(function(){var i=_.extend({},f.prototype.templates.warnings,{failed_metadata:b.wrapTemplate(['<% if( model.state === "failed_metadata" ){ %>','
',c("An error occurred setting the metadata for this dataset"),"
","<% } %>"]),error:b.wrapTemplate(["<% if( model.error ){ %>",'
',c("There was an error getting the data for this dataset"),": <%- model.error %>","
","<% } %>"]),purged:b.wrapTemplate(["<% if( model.purged ){ %>",'
',c("This dataset has been deleted and removed from disk"),"
","<% } %>"]),deleted:b.wrapTemplate(["<% if( model.deleted && !model.purged ){ %>",'
',c("This dataset has been deleted"),"
","<% } %>"])});var j=b.wrapTemplate(['
','
','
','
','
',"
","<% if( !dataset.deleted && !dataset.purged ){ %>",'
','
','
',"<% if( dataset.peek ){ %>",'
<%= dataset.peek %>
',"<% } %>","<% } %>","
"],"dataset");var h=b.wrapTemplate(['
','
',c("You do not have permission to view this dataset"),"
","
"],"dataset");var k={};k[a.OK]=k[a.FAILED_METADATA]=b.wrapTemplate(["<% if( dataset.misc_blurb ){ %>",'
','<%- dataset.misc_blurb %>',"
","<% } %>","<% if( dataset.file_ext ){ %>",'
','",'<%- dataset.file_ext %>',"
","<% } %>","<% if( dataset.metadata_dbkey ){ %>",'
','",'',"<%- dataset.metadata_dbkey %>","","
","<% } %>","<% if( dataset.misc_info ){ %>",'
','<%- dataset.misc_info %>',"
","<% } %>"],"dataset");k[a.NEW]=b.wrapTemplate(["
",c("This is a new dataset and not all of its data are available yet"),"
"],"dataset");k[a.NOT_VIEWABLE]=b.wrapTemplate(["
",c("You do not have permission to view this dataset"),"
"],"dataset");k[a.DISCARDED]=b.wrapTemplate(["
",c("The job creating this dataset was cancelled before completion"),"
"],"dataset");k[a.QUEUED]=b.wrapTemplate(["
",c("This job is waiting to run"),"
"],"dataset");k[a.RUNNING]=b.wrapTemplate(["
",c("This job is currently running"),"
"],"dataset");k[a.UPLOAD]=b.wrapTemplate(["
",c("This dataset is currently uploading"),"
"],"dataset");k[a.SETTING_METADATA]=b.wrapTemplate(["
",c("Metadata is being auto-detected"),"
"],"dataset");k[a.PAUSED]=b.wrapTemplate(["
",c('This job is paused. Use the "Resume Paused Jobs" in the history menu to resume'),"
"],"dataset");k[a.ERROR]=b.wrapTemplate(["<% if( !dataset.purged ){ %>","
<%- dataset.misc_blurb %>
","<% } %>",'',c("An error occurred with this dataset"),":",'
<%- dataset.misc_info %>
'],"dataset");k[a.EMPTY]=b.wrapTemplate(["
",c("No data"),": <%- dataset.misc_blurb %>
"],"dataset");k.unknown=b.wrapTemplate(['
Error: unknown dataset state: "<%- dataset.state %>"
'],"dataset");var l={resubmitted:b.wrapTemplate(["<% if( model.resubmitted ){ %>",'
',c("The job creating this dataset has been resubmitted"),"
","<% } %>"])};var g=b.wrapTemplate(["<% _.each( apps, function( app ){ %>",'
','<%- app.label %> ','',"<% _.each( app.links, function( link ){ %>",'',"<% print( _l( link.text ) ); %>"," ","<% }); %>","","
","<% }); %>"],"apps");return _.extend({},f.prototype.templates,{warnings:i,details:j,noAccess:h,summaries:k,detailMessages:l,displayApplications:g})}());return{DatasetListItemView:e}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/dataset/dataset-list.js b/static/scripts/packed/mvc/dataset/dataset-list.js index 5d67e43e21b..745343827b0 100644 --- a/static/scripts/packed/mvc/dataset/dataset-list.js +++ b/static/scripts/packed/mvc/dataset/dataset-list.js @@ -1 +1 @@ -define(["mvc/dataset/list-panel","mvc/dataset/dataset-li","mvc/base-mvc","utils/localization"],function(c,f,a,b){var e=c.ListPanel;var d=e.extend({viewClass:f.DatasetListItemView,className:e.prototype.className+" dataset-list",noneFoundMsg:b("No matching datasets found"),initialize:function(g){e.prototype.initialize.call(this,g)},toString:function(){return"DatasetList("+this.collection+")"}});return{DatasetList:d}}); \ No newline at end of file +define(["mvc/list/list-panel","mvc/dataset/dataset-li","mvc/base-mvc","utils/localization"],function(c,f,a,b){var e=c.ListPanel;var d=e.extend({viewClass:f.DatasetListItemView,className:e.prototype.className+" dataset-list",noneFoundMsg:b("No matching datasets found"),initialize:function(g){e.prototype.initialize.call(this,g)},toString:function(){return"DatasetList("+this.collection+")"}});return{DatasetList:d}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/dataset/dataset-model.js b/static/scripts/packed/mvc/dataset/dataset-model.js index 0b9900eccde..5bf4b1e9afd 100644 --- a/static/scripts/packed/mvc/dataset/dataset-model.js +++ b/static/scripts/packed/mvc/dataset/dataset-model.js @@ -1 +1 @@ -define(["mvc/dataset/states","mvc/base-mvc","utils/localization"],function(a,b,d){var g=b.SearchableModelMixin;var f=Backbone.Model.extend(b.LoggableMixin).extend(b.mixin(g,{defaults:{state:a.NEW,deleted:false,purged:false,name:"(unnamed dataset)",accessible:true,data_type:"",file_ext:"",file_size:0,meta_files:[],misc_blurb:"",misc_info:"",tags:[]},initialize:function(h,i){this.debug(this+"(Dataset).initialize",h,i);if(!this.get("accessible")){this.set("state",a.NOT_VIEWABLE)}this.urls=this._generateUrls();this._setUpListeners()},_generateUrls:function(){var j=this.get("id");if(!j){return{}}var i={purge:"datasets/"+j+"/purge_async",display:"datasets/"+j+"/display/?preview=True",edit:"datasets/"+j+"/edit",download:"datasets/"+j+"/display?to_ext="+this.get("file_ext"),report_error:"dataset/errors?id="+j,rerun:"tool_runner/rerun?id="+j,show_params:"datasets/"+j+"/show_params",visualization:"visualization",meta_download:"dataset/get_metadata_file?hda_id="+j+"&metadata_name="};var h=(window.galaxy_config&&galaxy_config.root)?(galaxy_config.root):("/");_.each(i,function(l,k){i[k]=h+l});this.urls=i;return i},_setUpListeners:function(){this.on("change:state",function(i,h){this.log(this+" has changed state:",i,h);if(this.inReadyState()){this.trigger("state:ready",i,h,this.previous("state"))}});this.on("change:id change:file_ext",function(h){this._generateUrls()})},toJSON:function(){var h=Backbone.Model.prototype.toJSON.call(this);return _.extend(h,{urls:this.urls})},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},inReadyState:function(){var h=_.contains(a.READY_STATES,this.get("state"));return(this.isDeletedOrPurged()||h)},hasDetails:function(){return _.has(this.attributes,"genome_build")},hasData:function(){return(this.get("file_size")>0)},"delete":function(h){if(this.get("deleted")){return jQuery.when()}return this.save({deleted:true},h)},undelete:function(h){if(!this.get("deleted")||this.get("purged")){return jQuery.when()}return this.save({deleted:false},h)},purge:function e(h){if(this.get("purged")){return jQuery.when()}h=h||{};h.url=this.urls.purge;var i=this,j=jQuery.ajax(h);j.done(function(m,k,l){i.set({deleted:true,purged:true})});j.fail(function(o,k,n){var l=d("Unable to purge dataset");var m=("Removal of datasets by users is not allowed in this Galaxy instance");if(o.responseJSON&&o.responseJSON.error){l=o.responseJSON.error}else{if(o.responseText.indexOf(m)!==-1){l=m}}o.responseText=l;i.trigger("error",i,o,h,d(l),{error:l})});return j},searchAttributes:["name","file_ext","genome_build","misc_blurb","misc_info","annotation","tags"],searchAliases:{title:"name",format:"file_ext",database:"genome_build",blurb:"misc_blurb",description:"misc_blurb",info:"misc_info",tag:"tags"},toString:function(){var h=this.get("id")||"";if(this.get("name")){h='"'+this.get("name")+'",'+h}return"Dataset("+h+")"}}));var c=Backbone.Collection.extend(b.LoggableMixin).extend({model:f,urlRoot:((window.galaxy_config&&galaxy_config.root)?(galaxy_config.root):("/"))+"api/datasets",ids:function(){return this.map(function(h){return h.get("id")})},notReady:function(){return this.filter(function(h){return !h.inReadyState()})},haveDetails:function(){return this.all(function(h){return h.hasDetails()})},ajaxQueue:function(m,j){var i=jQuery.Deferred(),h=this.length,l=[];if(!h){i.resolve([]);return i}var k=this.chain().reverse().map(function(o,n){return function(){var p=m.call(o,j);p.done(function(q){i.notify({curr:n,total:h,response:q,model:o})});p.always(function(q){l.push(q);if(k.length){k.shift()()}else{i.resolve(l)}})}}).value();k.shift()();return i},matches:function(h){return this.filter(function(i){return i.matches(h)})},set:function(j,h){var i=this;j=_.map(j,function(l){if(!i.get(l.id)){return l}var k=existing.toJSON();_.extend(k,l);return k});Backbone.Collection.prototype.set.call(this,j,h)},toString:function(){return(["DatasetAssociationCollection(",this.length,")"].join(""))}});return{DatasetAssociation:f,DatasetAssociationCollection:c}}); \ No newline at end of file +define(["mvc/dataset/states","mvc/base-mvc","utils/localization"],function(a,b,d){var g=b.SearchableModelMixin;var f=Backbone.Model.extend(b.LoggableMixin).extend(b.mixin(g,{defaults:{state:a.NEW,deleted:false,purged:false,name:"(unnamed dataset)",accessible:true,data_type:"",file_ext:"",file_size:0,meta_files:[],misc_blurb:"",misc_info:"",tags:[]},initialize:function(h,i){this.debug(this+"(Dataset).initialize",h,i);if(!this.get("accessible")){this.set("state",a.NOT_VIEWABLE)}this.urls=this._generateUrls();this._setUpListeners()},_generateUrls:function(){var j=this.get("id");if(!j){return{}}var i={purge:"datasets/"+j+"/purge_async",display:"datasets/"+j+"/display/?preview=True",edit:"datasets/"+j+"/edit",download:"datasets/"+j+"/display?to_ext="+this.get("file_ext"),report_error:"dataset/errors?id="+j,rerun:"tool_runner/rerun?id="+j,show_params:"datasets/"+j+"/show_params",visualization:"visualization",meta_download:"dataset/get_metadata_file?hda_id="+j+"&metadata_name="};var h=(window.galaxy_config&&galaxy_config.root)?(galaxy_config.root):("/");_.each(i,function(l,k){i[k]=h+l});this.urls=i;return i},_setUpListeners:function(){this.on("change:state",function(i,h){this.log(this+" has changed state:",i,h);if(this.inReadyState()){this.trigger("state:ready",i,h,this.previous("state"))}});this.on("change:urls",function(){console.warn("change:urls",arguments)});this.on("change:id change:file_ext",function(h){this._generateUrls()})},toJSON:function(){var h=Backbone.Model.prototype.toJSON.call(this);return _.extend(h,{urls:this.urls})},isDeletedOrPurged:function(){return(this.get("deleted")||this.get("purged"))},inReadyState:function(){var h=_.contains(a.READY_STATES,this.get("state"));return(this.isDeletedOrPurged()||h)},hasDetails:function(){return _.has(this.attributes,"genome_build")},hasData:function(){return(this.get("file_size")>0)},"delete":function(h){if(this.get("deleted")){return jQuery.when()}return this.save({deleted:true},h)},undelete:function(h){if(!this.get("deleted")||this.get("purged")){return jQuery.when()}return this.save({deleted:false},h)},purge:function e(h){if(this.get("purged")){return jQuery.when()}h=h||{};h.url=this.urls.purge;var i=this,j=jQuery.ajax(h);j.done(function(m,k,l){i.set({deleted:true,purged:true})});j.fail(function(o,k,n){var l=d("Unable to purge dataset");var m=("Removal of datasets by users is not allowed in this Galaxy instance");if(o.responseJSON&&o.responseJSON.error){l=o.responseJSON.error}else{if(o.responseText.indexOf(m)!==-1){l=m}}o.responseText=l;i.trigger("error",i,o,h,d(l),{error:l})});return j},searchAttributes:["name","file_ext","genome_build","misc_blurb","misc_info","annotation","tags"],searchAliases:{title:"name",format:"file_ext",database:"genome_build",blurb:"misc_blurb",description:"misc_blurb",info:"misc_info",tag:"tags"},toString:function(){var h=this.get("id")||"";if(this.get("name")){h='"'+this.get("name")+'",'+h}return"Dataset("+h+")"}}));var c=Backbone.Collection.extend(b.LoggableMixin).extend({model:f,urlRoot:((window.galaxy_config&&galaxy_config.root)?(galaxy_config.root):("/"))+"api/datasets",ids:function(){return this.map(function(h){return h.get("id")})},notReady:function(){return this.filter(function(h){return !h.inReadyState()})},haveDetails:function(){return this.all(function(h){return h.hasDetails()})},ajaxQueue:function(m,j){var i=jQuery.Deferred(),h=this.length,l=[];if(!h){i.resolve([]);return i}var k=this.chain().reverse().map(function(o,n){return function(){var p=m.call(o,j);p.done(function(q){i.notify({curr:n,total:h,response:q,model:o})});p.always(function(q){l.push(q);if(k.length){k.shift()()}else{i.resolve(l)}})}}).value();k.shift()();return i},matches:function(h){return this.filter(function(i){return i.matches(h)})},set:function(j,h){var i=this;j=_.map(j,function(l){if(!i.get(l.id)){return l}var k=existing.toJSON();_.extend(k,l);return k});Backbone.Collection.prototype.set.call(this,j,h)},toString:function(){return(["DatasetAssociationCollection(",this.length,")"].join(""))}});return{DatasetAssociation:f,DatasetAssociationCollection:c}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/dataset/list-panel.js b/static/scripts/packed/mvc/dataset/list-panel.js deleted file mode 100644 index f75a25623f4..00000000000 --- a/static/scripts/packed/mvc/dataset/list-panel.js +++ /dev/null @@ -1 +0,0 @@ -define(["mvc/base-mvc","utils/localization"],function(a,b){var c=Backbone.View.extend(a.LoggableMixin).extend({viewClass:a.ListItemView,tagName:"div",className:"list-panel",fxSpeed:"fast",emptyMsg:b("This list is empty"),noneFoundMsg:b("No matching items found"),initialize:function(d,e){d=d||{};if(d.logger){this.logger=d.logger}this.log(this+".initialize:",d);this.fxSpeed=_.has(d,"fxSpeed")?(d.fxSpeed):(this.fxSpeed);this.filters=[];this.searchFor=d.searchFor||"";this.indicator=new LoadingIndicator(this.$el);this.selecting=(d.selecting!==undefined)?d.selecting:true;this.selected=d.selected||[];this.lastSelected=null;this.viewClass=d.viewClass||this.viewClass;this.views=[];this.collection=d.collection||(new Backbone.Collection([]));this.filters=d.filters||[];this.title=d.title||"";this.subtitle=d.subtitle||"";this._setUpListeners()},_setUpListeners:function(){this.on("error",function(e,h,d,g,f){console.error(e,h,d,g,f)},this);this.on("loading",function(){this._showLoadingIndicator("loading...",40)},this);this.on("loading-done",function(){this._hideLoadingIndicator(40)},this);this.once("rendered",function(){this.trigger("rendered:initial",this)},this);if(this.logger){this.on("all",function(d){this.log(this+"",arguments)},this)}this._setUpCollectionListeners();this._setUpViewListeners();return this},freeViews:function(){this.views=[];return this},_setUpCollectionListeners:function(){this.collection.on("reset",function(){this.renderItems()},this);this.collection.on("add",this.addItemView,this);this.collection.on("remove",this.removeItemView,this);if(this.logger){this.collection.on("all",function(d){this.info(this+"(collection)",arguments)},this)}return this},_setUpViewListeners:function(){this.on("view:selected",function(d,e){if(e&&e.shiftKey&&this.lastSelected){var f=_.find(this.views,function(g){return g.model.id===this.lastSelected});if(f){this.selectRange(d,f)}}this.selected.push(d.model.id);this.lastSelected=d.model.id},this)},render:function(e){var d=this._buildNewRender();this._setUpBehaviors(d);this._queueNewRender(d,e);return this},_buildNewRender:function(){var e=this.model?this.model.toJSON():{},d=$(this.templates.el(e,this));this._renderTitle(d);this._renderSubtitle(d);this._renderSearch(d);this.renderItems(d);return d},_renderTitle:function(d){},_renderSubtitle:function(d){},_queueNewRender:function(e,f){f=(f===undefined)?(this.fxSpeed):(f);var d=this;$(d).queue("fx",[function(g){this.$el.fadeOut(f,g)},function(g){d._swapNewRender(e);g()},function(g){this.$el.fadeIn(f,g)},function(g){d.trigger("rendered",d);g()}])},_swapNewRender:function(d){this.$el.empty().attr("class",this.className).append(d.children());if(this.selecting){this.showSelectors(0)}return this},_setUpBehaviors:function(d){d=d||this.$el;d.find(".controls [title]").tooltip({placement:"bottom"});return this},$scrollContainer:function(){return this.$el.parent().parent()},$list:function(d){return(d||this.$el).find(".list-items")},$messages:function(d){return(d||this.$el).find(".message-container")},$emptyMessage:function(d){return(d||this.$el).find(".empty-message")},renderItems:function(g){g=g||this.$el;var h=this,e=[];var f=this.$list(g),d=this._filterCollection().map(function(j){var i=h._createItemView(j);e.push(i);return i.render(0).$el});this.debug(d);this.debug(e);f.empty();if(d.length){f.append(d);this.$emptyMessage(g).hide()}else{this._renderEmptyMessage(g).show()}this.views=e;return e},_filterCollection:function(){var d=this;return d.collection.filter(_.bind(d._filterItem,d))},_filterItem:function(d){var e=this;return(_.every(e.filters.map(function(f){return f.call(d)})))&&(!e.searchFor||d.matchesAll(e.searchFor))},_createItemView:function(f){var g=this._getItemViewClass(f),e=_.extend(this._getItemViewOptions(f),{model:f}),d=new g(e);this._setUpItemViewListeners(d);return d},_getItemViewClass:function(d){return this.viewClass},_getItemViewOptions:function(d){return{fxSpeed:this.fxSpeed,expanded:false,selectable:this.selecting,selected:_.contains(this.selected,d.id),draggable:this.dragging}},_setUpItemViewListeners:function(d){var e=this;d.on("all",function(){var f=Array.prototype.slice.call(arguments,0);f[0]="view:"+f[0];e.trigger.apply(e,f)});return this},_renderEmptyMessage:function(d){var e=this.searchFor?this.noneFoundMsg:this.emptyMsg;return this.$emptyMessage(d).text(e)},expandAll:function(){_.each(this.views,function(d){d.expand()})},collapseAll:function(){_.each(this.views,function(d){d.collapse()})},addItemView:function(f,h,e){this.log(this+".addItemView:",f);var g=this;if(!this._filterItem(f)){return undefined}var d=g._createItemView(f);this.views.push(d);$(d).queue("fx",[function(i){g.$emptyMessage().fadeOut(g.fxSpeed,i)},function(i){g.$list().append(d.render().$el);i()}]);return d},removeItemView:function(f,h,e){this.log(this+".removeItemView:",f);var g=this,d=g.viewFromModel(f);if(!d){return undefined}this.views=_.without(this.views,d);d.remove();if(!this.views.length){g._renderEmptyMessage().fadeIn(g.fxSpeed)}return d},viewFromModel:function(e){for(var f=0;f",'
','
','
<%= model.name || view.title %>
',"
",'
<%= view.subtitle %>
','
','
','",'
','
','",'","
","
","
",'
','
',"
"]);return{el:d}}());return{ListPanel:c}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/history/hda-li.js b/static/scripts/packed/mvc/history/hda-li.js index 319f90d6e14..158a7832cb6 100644 --- a/static/scripts/packed/mvc/history/hda-li.js +++ b/static/scripts/packed/mvc/history/hda-li.js @@ -1 +1 @@ -define(["mvc/dataset/dataset-li","mvc/base-mvc","utils/localization"],function(e,a,c){var d=e.DatasetListItemView;var b=d.extend({initialize:function(f,g){d.prototype.initialize.call(this,f,g)},className:d.prototype.className+" history-content",toString:function(){var f=(this.model)?(this.model+""):("(no model)");return"HDAListItemView("+f+")"}});b.prototype.templates=(function(){var g=a.wrapTemplate(['
','','
','<%- dataset.hid %> ','<%- dataset.name %>',"
","
"],"dataset");var f=_.extend({},d.prototype.templates.warnings,{hidden:a.wrapTemplate(["<% if( !dataset.visible ){ %>",'
',c("This dataset has been hidden"),"
","<% } %>"],"dataset")});return _.extend({},d.prototype.templates,{titleBar:g,warnings:f})}());return{HDAListItemView:b}}); \ No newline at end of file +define(["mvc/dataset/dataset-li","mvc/base-mvc","utils/localization"],function(e,a,c){var d=e.DatasetListItemView;var b=d.extend({className:d.prototype.className+" history-content",initialize:function(f,g){d.prototype.initialize.call(this,f,g)},toString:function(){var f=(this.model)?(this.model+""):("(no model)");return"HDAListItemView("+f+")"}});b.prototype.templates=(function(){var g=a.wrapTemplate(['
','','
','<%- dataset.hid %> ','<%- dataset.name %>',"
","
"],"dataset");var f=_.extend({},d.prototype.templates.warnings,{hidden:a.wrapTemplate(["<% if( !dataset.visible ){ %>",'
',c("This dataset has been hidden"),"
","<% } %>"],"dataset")});return _.extend({},d.prototype.templates,{titleBar:g,warnings:f})}());return{HDAListItemView:b}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/history/history-contents.js b/static/scripts/packed/mvc/history/history-contents.js index dd3ad5bfe96..1f8f81b6827 100644 --- a/static/scripts/packed/mvc/history/history-contents.js +++ b/static/scripts/packed/mvc/history/history-contents.js @@ -1 +1 @@ -define(["mvc/history/history-content-model","mvc/history/hda-model","mvc/history/hdca-model","mvc/base-mvc","utils/localization"],function(g,e,b,a,c){var f=Backbone.Collection.extend(a.LoggableMixin).extend({model:function(i,h){if(i.history_content_type==="dataset"){return new e.HistoryDatasetAssociation(i,h)}else{if(i.history_content_type==="dataset_collection"){switch(i.collection_type){case"list":return new b.HistoryListDatasetCollection(i,h);case"paired":return new b.HistoryPairDatasetCollection(i,h);case"list:paired":return new b.HistoryListPairedDatasetCollection(i,h)}throw new TypeError("Unknown collection_type: "+i.collection_type)}}throw new TypeError("Unknown history_content_type: "+i.history_content_type)},initialize:function(i,h){h=h||{};this.historyId=h.historyId;this.on("all",function(){this.debug(this+".event:",arguments)})},urlRoot:galaxy_config.root+"api/histories",url:function(){return this.urlRoot+"/"+this.historyId+"/contents"},ids:function(){return this.map(function(h){return h.get("id")})},notReady:function(){return this.filter(function(h){return !h.inReadyState()})},running:function(){var h=[];this.each(function(j){var i=!j.inReadyState();if(i){h.push(j.get("id"))}});return h},getByHid:function(h){return _.first(this.filter(function(i){return i.get("hid")===h}))},getVisible:function(h,k,j){j=j||[];this.debug("checking isVisible");var i=new f(this.filter(function(l){return l.isVisible(h,k)}));_.each(j,function(l){if(!_.isFunction(l)){return}i=new f(i.filter(l))});return i},haveDetails:function(){return this.all(function(h){return h.hasDetails()})},fetchAllDetails:function(i){i=i||{};var h={details:"all"};i.data=(i.data)?(_.extend(i.data,h)):(h);return this.fetch(i)},ajaxQueue:function(m,j){var i=jQuery.Deferred(),h=this.length,l=[];if(!h){i.resolve([]);return i}var k=this.chain().reverse().map(function(o,n){return function(){var p=m.call(o,j);p.done(function(q){i.notify({curr:n,total:h,response:q,model:o})});p.always(function(q){l.push(q);if(k.length){k.shift()()}else{i.resolve(l)}})}}).value();k.shift()();return i},copy:function(j){var i=this,h=jQuery.post(this.url(),{source:"hda",content:j});h.done(function(k){i.add([k])});h.fail(function(l,k,m){i.trigger("error",i,h,{},"Error copying dataset")});return h},matches:function(h){return this.filter(function(i){return i.matches(h)})},set:function(j,h){this.debug("set:",j);var i=this;j=_.map(j,function(m){var l=m.attributes||m,n=g.typeIdStr(l.history_content_type,l.id),o=i.get(n);if(!o){return m}var k=o.toJSON();_.extend(k,m);return k});Backbone.Collection.prototype.set.call(this,j,h)},promoteToHistoryDatasetCollection:function d(m,k,i){i=i||{};i.url=this.url();i.type="POST";var o=k;var l=[],h=null;if(k==="list"){this.chain().each(function(r){var p=r.attributes.name;var s=r.get("id");var q=r.attributes.history_content_type;if(q==="dataset"){if(o!=="list"){this.log("Invalid collection type")}l.push({name:p,src:"hda",id:s})}else{if(o==="list"){o="list:"+r.attributes.collection_type}else{if(o!=="list:"+r.attributes.collection_type){this.log("Invalid collection type")}}l.push({name:p,src:"hdca",id:s})}});h="New Dataset List"}else{if(k==="paired"){var j=this.ids();if(j.length!==2){}l.push({name:"forward",src:"hda",id:j[0]});l.push({name:"reverse",src:"hda",id:j[1]});h="New Dataset Pair"}}i.data={type:"dataset_collection",name:h,collection_type:o,element_identifiers:JSON.stringify(l)};var n=jQuery.ajax(i);n.done(function(r,p,q){m.refresh()});n.fail(function(r,p,q){if(r.responseJSON&&r.responseJSON.error){error=r.responseJSON.error}else{error=r.responseJSON}r.responseText=error});return n},print:function(){var h=this;h.each(function(i){h.debug(i);if(i.elements){h.debug("\t elements:",i.elements)}})},toString:function(){return(["HistoryContents(",[this.historyId,this.length].join(),")"].join(""))}});return{HistoryContents:f}}); \ No newline at end of file +define(["mvc/history/history-content-model","mvc/history/hda-model","mvc/history/hdca-model","mvc/base-mvc","utils/localization"],function(g,e,b,a,c){var f=Backbone.Collection.extend(a.LoggableMixin).extend({model:function(i,h){if(i.history_content_type==="dataset"){return new e.HistoryDatasetAssociation(i,h)}else{if(i.history_content_type==="dataset_collection"){switch(i.collection_type){case"list":return new b.HistoryListDatasetCollection(i,h);case"paired":return new b.HistoryPairDatasetCollection(i,h);case"list:paired":return new b.HistoryListPairedDatasetCollection(i,h)}throw new TypeError("Unknown collection_type: "+i.collection_type)}}throw new TypeError("Unknown history_content_type: "+i.history_content_type)},initialize:function(i,h){h=h||{};this.historyId=h.historyId;this.on("all",function(){this.debug(this+".event:",arguments)})},urlRoot:galaxy_config.root+"api/histories",url:function(){return this.urlRoot+"/"+this.historyId+"/contents"},ids:function(){return this.map(function(h){return h.get("id")})},notReady:function(){return this.filter(function(h){return !h.inReadyState()})},running:function(){var h=[];this.each(function(j){var i=!j.inReadyState();if(i){h.push(j.get("id"))}});return h},getByHid:function(h){return _.first(this.filter(function(i){return i.get("hid")===h}))},getVisible:function(h,k,j){j=j||[];this.debug("checking isVisible");var i=new f(this.filter(function(l){return l.isVisible(h,k)}));_.each(j,function(l){if(!_.isFunction(l)){return}i=new f(i.filter(l))});return i},haveDetails:function(){return this.all(function(h){return h.hasDetails()})},fetchAllDetails:function(i){i=i||{};var h={details:"all"};i.data=(i.data)?(_.extend(i.data,h)):(h);return this.fetch(i)},ajaxQueue:function(m,j){var i=jQuery.Deferred(),h=this.length,l=[];if(!h){i.resolve([]);return i}var k=this.chain().reverse().map(function(o,n){return function(){var p=m.call(o,j);p.done(function(q){i.notify({curr:n,total:h,response:q,model:o})});p.always(function(q){l.push(q);if(k.length){k.shift()()}else{i.resolve(l)}})}}).value();k.shift()();return i},copy:function(j){var i=this,h=jQuery.post(this.url(),{source:"hda",content:j});h.done(function(k){i.add([k])});h.fail(function(l,k,m){i.trigger("error",i,h,{},"Error copying dataset")});return h},matches:function(h){return this.filter(function(i){return i.matches(h)})},set:function(j,h){this.debug("set:",j);var i=this;j=_.map(j,function(m){var l=m.attributes||m,n=g.typeIdStr(l.history_content_type,l.id),o=i.get(n);if(!o){return m}var k=_.clone(o.attributes);_.extend(k,m);return k});Backbone.Collection.prototype.set.call(this,j,h)},promoteToHistoryDatasetCollection:function d(m,k,i){i=i||{};i.url=this.url();i.type="POST";var o=k;var l=[],h=null;if(k==="list"){this.chain().each(function(r){var p=r.attributes.name;var s=r.get("id");var q=r.attributes.history_content_type;if(q==="dataset"){if(o!=="list"){this.log("Invalid collection type")}l.push({name:p,src:"hda",id:s})}else{if(o==="list"){o="list:"+r.attributes.collection_type}else{if(o!=="list:"+r.attributes.collection_type){this.log("Invalid collection type")}}l.push({name:p,src:"hdca",id:s})}});h="New Dataset List"}else{if(k==="paired"){var j=this.ids();if(j.length!==2){}l.push({name:"forward",src:"hda",id:j[0]});l.push({name:"reverse",src:"hda",id:j[1]});h="New Dataset Pair"}}i.data={type:"dataset_collection",name:h,collection_type:o,element_identifiers:JSON.stringify(l)};var n=jQuery.ajax(i);n.done(function(r,p,q){m.refresh()});n.fail(function(r,p,q){if(r.responseJSON&&r.responseJSON.error){error=r.responseJSON.error}else{error=r.responseJSON}r.responseText=error});return n},clone:function(){var h=Backbone.Collection.prototype.clone.call(this);h.historyId=this.historyId;return h},print:function(){var h=this;h.each(function(i){h.debug(i);if(i.elements){h.debug("\t elements:",i.elements)}})},toString:function(){return(["HistoryContents(",[this.historyId,this.length].join(),")"].join(""))}});return{HistoryContents:f}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/history/history-model.js b/static/scripts/packed/mvc/history/history-model.js index 7d225c2721c..6e7168d0916 100644 --- a/static/scripts/packed/mvc/history/history-model.js +++ b/static/scripts/packed/mvc/history/history-model.js @@ -1 +1 @@ -define(["mvc/history/history-contents","mvc/base-mvc","utils/localization"],function(h,i,d){var e=Backbone.Model.extend(i.LoggableMixin).extend({defaults:{model_class:"History",id:null,name:"Unnamed History",state:"new",diskSize:0,deleted:false},urlRoot:galaxy_config.root+"api/histories",initialize:function(k,l,j){j=j||{};this.logger=j.logger||null;this.log(this+".initialize:",k,l,j);this.log("creating history contents:",l);this.contents=new h.HistoryContents(l||[],{historyId:this.get("id")});this._setUpListeners();this.updateTimeoutId=null},_setUpListeners:function(){this.on("error",function(k,n,j,m,l){this.errorHandler(k,n,j,m,l)});if(this.contents){this.listenTo(this.contents,"error",function(){this.trigger.apply(this,["error:contents"].concat(jQuery.makeArray(arguments)))})}this.on("change:id",function(k,j){if(this.contents){this.contents.historyId=j}},this)},errorHandler:function(k,n,j,m,l){this.clearUpdateTimeout()},ownedByCurrUser:function(){if(!Galaxy||!Galaxy.currUser){return false}if(Galaxy.currUser.isAnonymous()||Galaxy.currUser.id!==this.get("user_id")){return false}return true},contentsCount:function(){return _.reduce(_.values(this.get("state_details")),function(j,k){return j+k},0)},checkForUpdates:function(j){if(this.contents.running().length){this.setUpdateTimeout()}else{this.trigger("ready");if(_.isFunction(j)){j.call(this)}}return this},setUpdateTimeout:function(j){j=j||e.UPDATE_DELAY;var k=this;this.clearUpdateTimeout();this.updateTimeoutId=setTimeout(function(){k.refresh()},j);return this.updateTimeoutId},clearUpdateTimeout:function(){if(this.updateTimeoutId){clearTimeout(this.updateTimeoutId);this.updateTimeoutId=null}},refresh:function(k,j){k=k||[];j=j||{};var l=this;j.data=j.data||{};if(k.length){j.data.details=k.join(",")}var m=this.contents.fetch(j);m.done(function(n){l.checkForUpdates(function(){this.fetch()})});return m},copy:function(m,k){m=(m!==undefined)?(m):(true);if(!this.id){throw new Error("You must set the history ID before copying it.")}var j={history_id:this.id};if(m){j.current=true}if(k){j.name=k}var l=this,n=jQuery.post(this.urlRoot,j);n.done(function(o){l.trigger("copied",l,o)});return n},toString:function(){return"History("+this.get("id")+","+this.get("name")+")"}});e.UPDATE_DELAY=4000;e.getHistoryData=function c(j,v){v=v||{};var p=v.hdaDetailIds||[];var l=v.hdcaDetailIds||[];var r=jQuery.Deferred(),q=null;function k(w){if(j==="current"){return jQuery.getJSON(galaxy_config.root+"history/current_history_json")}return jQuery.ajax(galaxy_config.root+"api/histories/"+j)}function o(w){return w&&w.empty}function u(x){if(o(x)){return[]}if(_.isFunction(p)){p=p(x)}if(_.isFunction(l)){l=l(x)}var w={};if(p.length){w.dataset_details=p.join(",")}if(l.length){w.dataset_collection_details=l.join(",")}return jQuery.ajax(galaxy_config.root+"api/histories/"+x.id+"/contents",{data:w})}var t=v.historyFn||k,s=v.hdaFn||u;var n=t(j);n.done(function(w){q=w;r.notify({status:"history data retrieved",historyJSON:q})});n.fail(function(y,w,x){r.reject(y,"loading the history")});var m=n.then(s);m.then(function(w){r.notify({status:"dataset data retrieved",historyJSON:q,hdaJSON:w});r.resolve(q,w)});m.fail(function(y,w,x){r.reject(y,"loading the datasets",{history:q})});return r};var f=Backbone.Collection.extend(i.LoggableMixin).extend({model:e,urlRoot:(window.galaxy_config?galaxy_config.root:"/")+"api/histories",initialize:function(k,j){j=j||{};this.log("HistoryCollection.initialize",arguments);this.includeDeleted=j.includeDeleted||false;this.setUpListeners()},setUpListeners:function a(){var j=this;this.on("change:deleted",function(k){this.debug("change:deleted",j.includeDeleted,k.get("deleted"));if(!j.includeDeleted&&k.get("deleted")){j.remove(k)}});this.on("copied",function(k,l){this.unshift(new e(l,[]))})},create:function g(m,k,j,l){var o=this,n=new e(m||{},k||[],j||{});return n.save(l).done(function(p){o.unshift(n)})},toString:function b(){return"HistoryCollection("+this.length+")"}});return{History:e,HistoryCollection:f}}); \ No newline at end of file +define(["mvc/history/history-contents","mvc/base-mvc","utils/localization"],function(h,i,d){var e=Backbone.Model.extend(i.LoggableMixin).extend({defaults:{model_class:"History",id:null,name:"Unnamed History",state:"new",diskSize:0,deleted:false},urlRoot:galaxy_config.root+"api/histories",initialize:function(k,l,j){j=j||{};this.logger=j.logger||null;this.log(this+".initialize:",k,l,j);this.log("creating history contents:",l);this.contents=new h.HistoryContents(l||[],{historyId:this.get("id")});this._setUpListeners();this.updateTimeoutId=null},_setUpListeners:function(){this.on("error",function(k,n,j,m,l){this.errorHandler(k,n,j,m,l)});if(this.contents){this.listenTo(this.contents,"error",function(){this.trigger.apply(this,["error:contents"].concat(jQuery.makeArray(arguments)))})}this.on("change:id",function(k,j){if(this.contents){this.contents.historyId=j}},this)},errorHandler:function(k,n,j,m,l){this.clearUpdateTimeout()},ownedByCurrUser:function(){if(!Galaxy||!Galaxy.currUser){return false}if(Galaxy.currUser.isAnonymous()||Galaxy.currUser.id!==this.get("user_id")){return false}return true},contentsCount:function(){return _.reduce(_.values(this.get("state_details")),function(j,k){return j+k},0)},checkForUpdates:function(j){if(this.contents.running().length){this.setUpdateTimeout()}else{this.trigger("ready");if(_.isFunction(j)){j.call(this)}}return this},setUpdateTimeout:function(j){j=j||e.UPDATE_DELAY;var k=this;this.clearUpdateTimeout();this.updateTimeoutId=setTimeout(function(){k.refresh()},j);return this.updateTimeoutId},clearUpdateTimeout:function(){if(this.updateTimeoutId){clearTimeout(this.updateTimeoutId);this.updateTimeoutId=null}},refresh:function(k,j){k=k||[];j=j||{};var l=this;j.data=j.data||{};if(k.length){j.data.details=k.join(",")}var m=this.contents.fetch(j);m.done(function(n){l.checkForUpdates(function(){this.fetch()})});return m},copy:function(m,k){m=(m!==undefined)?(m):(true);if(!this.id){throw new Error("You must set the history ID before copying it.")}var j={history_id:this.id};if(m){j.current=true}if(k){j.name=k}var l=this,n=jQuery.post(this.urlRoot,j);n.done(function(o){l.trigger("copied",l,o)});return n},toString:function(){return"History("+this.get("id")+","+this.get("name")+")"}});e.UPDATE_DELAY=4000;e.getHistoryData=function c(j,v){v=v||{};var r=v.detailIdsFn||[];var m=v.hdcaDetailIds||[];var s=jQuery.Deferred(),p=null;function k(w){if(j==="current"){return jQuery.getJSON(galaxy_config.root+"history/current_history_json")}return jQuery.ajax(galaxy_config.root+"api/histories/"+j)}function o(w){return w&&w.empty}function q(x){if(o(x)){return[]}if(_.isFunction(r)){r=r(x)}if(_.isFunction(m)){m=m(x)}var w={};if(r.length){w.dataset_details=r.join(",")}if(m.length){w.dataset_collection_details=m.join(",")}return jQuery.ajax(galaxy_config.root+"api/histories/"+x.id+"/contents",{data:w})}var t=v.historyFn||k,u=v.contentsFn||q;var n=t(j);n.done(function(w){p=w;s.notify({status:"history data retrieved",historyJSON:p})});n.fail(function(y,w,x){s.reject(y,"loading the history")});var l=n.then(u);l.then(function(w){s.notify({status:"contents data retrieved",historyJSON:p,contentsJSON:w});s.resolve(p,w)});l.fail(function(y,w,x){s.reject(y,"loading the contents",{history:p})});return s};var f=Backbone.Collection.extend(i.LoggableMixin).extend({model:e,urlRoot:(window.galaxy_config?galaxy_config.root:"/")+"api/histories",initialize:function(k,j){j=j||{};this.log("HistoryCollection.initialize",arguments);this.includeDeleted=j.includeDeleted||false;this.setUpListeners()},setUpListeners:function a(){var j=this;this.on("change:deleted",function(k){this.debug("change:deleted",j.includeDeleted,k.get("deleted"));if(!j.includeDeleted&&k.get("deleted")){j.remove(k)}});this.on("copied",function(k,l){this.unshift(new e(l,[]))})},create:function g(m,k,j,l){var o=this,n=new e(m||{},k||[],j||{});return n.save(l).done(function(p){o.unshift(n)})},toString:function b(){return"HistoryCollection("+this.length+")"}});return{History:e,HistoryCollection:f}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/history/history-panel-annotated.js b/static/scripts/packed/mvc/history/history-panel-annotated.js index 227e78989ac..2e02bcf4b5d 100644 --- a/static/scripts/packed/mvc/history/history-panel-annotated.js +++ b/static/scripts/packed/mvc/history/history-panel-annotated.js @@ -1 +1 @@ -define(["mvc/history/history-panel","mvc/history/hda-li","utils/localization"],function(e,b,a){var d=e.ReadOnlyHistoryPanel;var c=d.extend({className:"annotated-history-panel",renderModel:function(){this.$el.addClass(this.className);var h=d.prototype.renderModel.call(this),f=this.$datasetsList(h),g=$("").addClass("datasets-list datasets-table");g.append(f.children());f.replaceWith(g);h.find(".history-subtitle").after(this.renderHistoryAnnotation());h.find(".history-search-btn").hide();h.find(".history-controls").after(h.find(".history-search-controls").show());return h},renderHistoryAnnotation:function(){var f=this.model.get("annotation");if(!f){return null}return $(['
',f,"
"].join(""))},renderHdas:function(g){g=g||this.$el;var f=d.prototype.renderHdas.call(this,g);this.$datasetsList(g).prepend($("").addClass("headers").append([$("").addClass("dataset-row").append([$("
").text(a("Dataset")),$("").text(a("Annotation"))]));return f},attachContentView:function(i,g){g=g||this.$el;var j=_.find(i.el.classList,function(k){return(/^state\-/).test(k)}),f=i.model.get("annotation")||"",h=$("
").addClass("dataset-container").append(i.$el).addClass(j?j.replace("-","-color-"):""),$("").addClass("additional-info").text(f)]);this.$datasetsList(g).append(h)},events:_.extend(_.clone(d.prototype.events),{"click tr":function(f){$(f.currentTarget).find(".dataset-title-bar").click()},"click .icon-btn":function(f){f.stopPropagation()}}),toString:function(){return"AnnotatedHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{AnnotatedHistoryPanel:c}}); \ No newline at end of file +define(["mvc/history/history-panel","mvc/history/hda-li","mvc/history/hdca-li","mvc/base-mvc","utils/localization"],function(f,c,g,a,b){var e=f.HistoryPanel;var d=e.extend({className:e.prototype.className+" annotated-history-panel",_buildNewRender:function(){var h=e.prototype._buildNewRender.call(this);this.renderHistoryAnnotation(h);return h},renderHistoryAnnotation:function(i){var h=this.model.get("annotation");if(!h){return}i.find(".controls .annotation-display").text(h)},renderItems:function(i){i=i||this.$el;i.find(".list-items").replaceWith($("").addClass("list-items"));var h=e.prototype.renderItems.call(this,i);this.$list(i).prepend($("").addClass("headers").append([$("").append([$("
").text(b("Dataset")),$("").text(b("Annotation"))]));return h},_attachItems:function(h){this.$list(h).append(this.views.map(function(j){var l=_.find(j.el.classList,function(m){return(/^state\-/).test(m)}),i=j.model.get("annotation")||"",k=$("
").addClass("contents-container").append(j.$el).addClass(l?l.replace("-","-color-"):""),$("").addClass("additional-info").text(i)]);return k}));return this},events:_.extend(_.clone(e.prototype.events),{"click tr":function(h){$(h.currentTarget).find(".title-bar").click()},"click .icon-btn":function(h){h.stopPropagation()}}),toString:function(){return"AnnotatedHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{AnnotatedHistoryPanel:d}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/history/history-panel-edit-current.js b/static/scripts/packed/mvc/history/history-panel-edit-current.js index 214b31fdac3..1ea9ce9eca3 100644 --- a/static/scripts/packed/mvc/history/history-panel-edit-current.js +++ b/static/scripts/packed/mvc/history/history-panel-edit-current.js @@ -1 +1 @@ -define(["mvc/history/history-model","mvc/history/history-panel-edit","mvc/collection/collection-panel","mvc/base-mvc","utils/localization"],function(d,c,a,h,b){var e=h.SessionStorageModel.extend({defaults:{searching:false,tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});e.storageKey=function f(){return("history-panel")};var g=c.HistoryPanel;var i=g.extend({emptyMsg:b("This history is empty. Click 'Get Data' on the left tool menu to start"),noneFoundMsg:b("No matching datasets found"),initialize:function(j){j=j||{};this.preferences=new e(_.extend({id:e.storageKey()},_.pick(j,_.keys(e.prototype.defaults))));g.prototype.initialize.call(this,j);this.panelStack=[]},loadCurrentHistory:function(k){this.debug(this+".loadCurrentHistory");var j=this;return this.loadHistoryWithHDADetails("current",k).then(function(m,l){j.trigger("current-history",j)})},switchToHistory:function(m,l){var j=this,k=function(){return jQuery.getJSON(galaxy_config.root+"history/set_as_current?id="+m)};return this.loadHistoryWithHDADetails(m,l,k).then(function(o,n){j.trigger("switched-history",j)})},createNewHistory:function(l){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",b("You must be logged in to create histories"));return $.when()}var j=this,k=function(){return jQuery.getJSON(galaxy_config.root+"history/create_new_current")};return this.loadHistory(undefined,l,k).then(function(n,m){j.trigger("new-history",j)})},setModel:function(k,j,l){g.prototype.setModel.call(this,k,j,l);if(this.model){this.log("checking for updates");this.model.checkForUpdates()}return this},_setUpModelEventHandlers:function(){g.prototype._setUpModelEventHandlers.call(this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}this.model.contents.on("state:ready",function(k,l,j){if((!k.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeHdaView(this.hdaViews[k.id])}},this)},render:function(l,m){this.log("render:",l,m);l=(l===undefined)?(this.fxSpeed):(l);var j=this,k;if(this.model){k=this.renderModel()}else{k=this.renderWithoutModel()}$(j).queue("fx",[function(n){if(l&&j.$el.is(":visible")){j.$el.fadeOut(l,n)}else{n()}},function(n){j.$el.empty();if(k){j.$el.append(k.children());j.renderBasedOnPrefs()}n()},function(n){if(l&&!j.$el.is(":visible")){j.$el.fadeIn(l,n)}else{n()}},function(n){if(m){m.call(this)}j.trigger("rendered",this);n()}]);return this},renderBasedOnPrefs:function(){if(this.preferences.get("searching")){this.toggleSearchControls(0,true)}},_renderEmptyMsg:function(l){var k=this,j=k.$emptyMessage(l),m=$(".toolMenuContainer");if((_.isEmpty(k.hdaViews)&&!k.searchFor)&&(Galaxy&&Galaxy.upload&&m.size())){j.empty();j.html([b("This history is empty"),". ",b("You can "),'',b("load your own data"),"",b(" or "),'',b("get data from an external source"),""].join(""));j.find(".uploader-link").click(function(n){Galaxy.upload._eventShow(n)});j.find(".get-data-link").click(function(n){m.parent().scrollTop(0);m.find('span:contains("Get Data")').click()});j.show()}else{g.prototype._renderEmptyMsg.call(this,l)}return this},toggleSearchControls:function(k,j){var l=g.prototype.toggleSearchControls.call(this,k,j);this.preferences.set("searching",l)},_renderTags:function(j){var k=this;g.prototype._renderTags.call(this,j);if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}this.tagsEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(l){k.preferences.set("tagsEditorShown",l.hidden)})},_renderAnnotation:function(j){var k=this;g.prototype._renderAnnotation.call(this,j);if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}this.annotationEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(l){k.preferences.set("annotationEditorShown",l.hidden)})},_setUpHdaListeners:function(k){g.prototype._setUpHdaListeners.call(this,k);var j=this;if(k instanceof j.HDCAViewClass){k.off("expanded");k.on("expanded",function(l){this.info("expanded",l);j._addCollectionPanel(l)});k.off("collapsed")}},_addCollectionPanel:function(l){var m=this,j=l.model;this.debug("history panel (stack), collectionModel:",j);var k=new (this._getCollectionPanelClass(j))({model:j,HDAViewClass:this.HDAViewClass,parentName:this.model.get("name"),linkTarget:this.linkTarget});m.panelStack.push(k);m.$(".history-controls").add(".datasets-list").hide();m.$el.append(k.$el);k.on("close",function(){m.render();l.collapse();m.panelStack.pop()});if(!k.model.hasDetails()){var n=k.model.fetch();n.done(function(){k.render()})}else{k.render()}},_getCollectionPanelClass:function(j){switch(j.get("collection_type")){case"list":return a.ListCollectionPanel;case"paired":return a.PairCollectionPanel;case"list:paired":return a.ListOfPairsCollectionPanel}throw new TypeError("Uknown collection_type: "+j.get("collection_type"))},addContentView:function(j){if(this.panelStack.length){return this}return g.prototype.addContentView.call(this,j)},connectToQuotaMeter:function(j){if(!j){return this}this.listenTo(j,"quota:over",this.showQuotaMessage);this.listenTo(j,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(j&&j.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var j=this.$el.find(".quota-message");if(j.is(":hidden")){j.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var j=this.$el.find(".quota-message");if(!j.is(":hidden")){j.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(j){if(!j){return this}this.on("new-storage",function(l,k){if(j&&l){j.findItemByHtml(b("Include Deleted Datasets")).checked=l.get("show_deleted");j.findItemByHtml(b("Include Hidden Datasets")).checked=l.get("show_hidden")}});return this},toString:function(){return"CurrentHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{CurrentHistoryPanel:i}}); \ No newline at end of file +define(["mvc/history/history-model","mvc/history/history-panel-edit","mvc/collection/collection-panel","mvc/base-mvc","utils/localization"],function(d,c,a,h,b){var e=h.SessionStorageModel.extend({defaults:{tagsEditorShown:false,annotationEditorShown:false},toString:function(){return"HistoryPanelPrefs("+JSON.stringify(this.toJSON())+")"}});e.storageKey=function f(){return("history-panel")};var g=c.HistoryPanelEdit;var i=g.extend({className:g.prototype.className+" current-history-panel",emptyMsg:b("This history is empty. Click 'Get Data' on the left tool menu to start"),noneFoundMsg:b("No matching datasets found"),initialize:function(j){j=j||{};this.preferences=new e(_.extend({id:e.storageKey()},_.pick(j,_.keys(e.prototype.defaults))));g.prototype.initialize.call(this,j);this.panelStack=[]},loadCurrentHistory:function(k){this.debug(this+".loadCurrentHistory");var j=this;return this.loadHistoryWithDetails("current",k).then(function(m,l){j.trigger("current-history",j)})},switchToHistory:function(m,l){var j=this,k=function(){return jQuery.getJSON(galaxy_config.root+"history/set_as_current?id="+m)};return this.loadHistoryWithDetails(m,l,k).then(function(o,n){j.trigger("switched-history",j)})},createNewHistory:function(l){if(!Galaxy||!Galaxy.currUser||Galaxy.currUser.isAnonymous()){this.displayMessage("error",b("You must be logged in to create histories"));return $.when()}var j=this,k=function(){return jQuery.getJSON(galaxy_config.root+"history/create_new_current")};return this.loadHistory(undefined,l,k).then(function(n,m){j.trigger("new-history",j)})},setModel:function(k,j,l){g.prototype.setModel.call(this,k,j,l);if(this.model){this.log("checking for updates");this.model.checkForUpdates()}return this},_setUpCollectionListeners:function(){g.prototype._setUpCollectionListeners.call(this);this.collection.on("state:ready",function(k,l,j){if((!k.get("visible"))&&(!this.storage.get("show_hidden"))){this.removeItemView(this.viewFromModel(k))}},this)},_setUpModelListeners:function(){g.prototype._setUpModelListeners.call(this);if(Galaxy&&Galaxy.quotaMeter){this.listenTo(this.model,"change:nice_size",function(){Galaxy.quotaMeter.update()})}},_buildNewRender:function(){if(!this.model){return $()}var j=g.prototype._buildNewRender.call(this);j.find(".search").prependTo(j.find(".controls"));this._renderQuotaMessage(j);return j},_renderQuotaMessage:function(j){j=j||this.$el;return $(this.templates.quotaMsg({},this)).prependTo(j.find(".messages"))},_renderEmptyMessage:function(l){var k=this,j=k.$emptyMessage(l),m=$(".toolMenuContainer");if((_.isEmpty(k.views)&&!k.searchFor)&&(Galaxy&&Galaxy.upload&&m.size())){j.empty();j.html([b("This history is empty"),". ",b("You can "),'',b("load your own data"),"",b(" or "),'',b("get data from an external source"),""].join(""));j.find(".uploader-link").click(function(n){Galaxy.upload._eventShow(n)});j.find(".get-data-link").click(function(n){m.parent().scrollTop(0);m.find('span:contains("Get Data")').click()});return j.show()}return g.prototype._renderEmptyMessage.call(this,l)},_renderTags:function(j){var k=this;g.prototype._renderTags.call(this,j);if(this.preferences.get("tagsEditorShown")){this.tagsEditor.toggle(true)}this.tagsEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(l){k.preferences.set("tagsEditorShown",l.hidden)})},_renderAnnotation:function(j){var k=this;g.prototype._renderAnnotation.call(this,j);if(this.preferences.get("annotationEditorShown")){this.annotationEditor.toggle(true)}this.annotationEditor.on("hiddenUntilActivated:shown hiddenUntilActivated:hidden",function(l){k.preferences.set("annotationEditorShown",l.hidden)})},_attachItems:function(j){this.$list(j).append(this.views.reverse().map(function(k){return k.$el}));return this},_setUpItemViewListeners:function(k){var j=this;g.prototype._setUpItemViewListeners.call(j,k);if(k instanceof j.HDCAViewClass){k.off("expanded");k.on("expanded",function(l){j.info("expanded",l);j._addCollectionPanel(l)});k.off("collapsed")}},_addCollectionPanel:function(l){var k=this,j=l.model;this.debug("history panel (stack), collectionModel:",j);var n=new (this._getCollectionPanelClass(j))({model:j,HDAViewClass:this.HDAViewClass,parentName:this.model.get("name"),linkTarget:this.linkTarget});k.panelStack.push(n);k.$(".controls").add(k.$list()).hide();k.$el.append(n.$el);n.on("close",function(){k.render();l.collapse();k.panelStack.pop()});if(!n.model.hasDetails()){var m=n.model.fetch();m.done(function(){n.render()})}else{n.render()}},_getCollectionPanelClass:function(j){switch(j.get("collection_type")){case"list":return a.ListCollectionPanel;case"paired":return a.PairCollectionPanel;case"list:paired":return a.ListOfPairsCollectionPanel}throw new TypeError("Uknown collection_type: "+j.get("collection_type"))},addItemView:function(o,p,n){this.log(this+".addItemView:",o);if(!this._filterItem(o)){return undefined}var l=this;if(this.panelStack.length){return l}var k=l._createItemView(o);this.views.unshift(k);l.scrollToTop();$({}).queue([function m(r){var q=l.$emptyMessage();if(q.is(":visible")){q.fadeOut(l.fxSpeed,r)}else{r()}},function j(q){l.scrollToTop();l.$list().prepend(k.render(0).$el.hide());k.$el.slideDown(l.fxSpeed)}]);return k},connectToQuotaMeter:function(j){if(!j){return this}this.listenTo(j,"quota:over",this.showQuotaMessage);this.listenTo(j,"quota:under",this.hideQuotaMessage);this.on("rendered rendered:initial",function(){if(j&&j.isOverQuota()){this.showQuotaMessage()}});return this},showQuotaMessage:function(){var j=this.$el.find(".quota-message");if(j.is(":hidden")){j.slideDown(this.fxSpeed)}},hideQuotaMessage:function(){var j=this.$el.find(".quota-message");if(!j.is(":hidden")){j.slideUp(this.fxSpeed)}},connectToOptionsMenu:function(j){if(!j){return this}this.on("new-storage",function(l,k){if(j&&l){j.findItemByHtml(b("Include Deleted Datasets")).checked=l.get("show_deleted");j.findItemByHtml(b("Include Hidden Datasets")).checked=l.get("show_hidden")}});return this},toString:function(){return"CurrentHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});i.prototype.templates=(function(){var j=h.wrapTemplate(['
',b("You are over your disk quota"),". ",b("Tool execution is on hold until your disk usage drops below your allocated quota"),".","
"],"history");return _.extend(_.clone(g.prototype.templates),{quotaMsg:j})}());return{CurrentHistoryPanel:i}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/history/history-panel-edit.js b/static/scripts/packed/mvc/history/history-panel-edit.js index 7371256cdb1..1dbeda9c813 100644 --- a/static/scripts/packed/mvc/history/history-panel-edit.js +++ b/static/scripts/packed/mvc/history/history-panel-edit.js @@ -1 +1 @@ -define(["mvc/history/history-panel","mvc/history/history-contents","mvc/dataset/states","mvc/history/hda-model","mvc/history/hda-li-edit","mvc/history/hdca-li-edit","mvc/tags","mvc/annotations","utils/localization"],function(f,h,k,d,c,g,j,a,b){var i=f.ReadOnlyHistoryPanel;var e=i.extend({HDAViewClass:c.HDAListItemEdit,HDCAViewClass:g.HDCAListItemEdit,initialize:function(l){l=l||{};this.selectedHdaIds=[];this.lastSelectedViewId=null;this.tagsEditor=null;this.annotationEditor=null;this.purgeAllowed=l.purgeAllowed||false;this.selecting=l.selecting||false;this.annotationEditorShown=l.annotationEditorShown||false;this.tagsEditorShown=l.tagsEditorShown||false;i.prototype.initialize.call(this,l)},_setUpModelEventHandlers:function(){i.prototype._setUpModelEventHandlers.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);this.model.contents.on("change:deleted",this._handleHdaDeletionChange,this);this.model.contents.on("change:visible",this._handleHdaVisibleChange,this);this.model.contents.on("change:purged",function(l){this.model.fetch()},this)},renderModel:function(){var l=$("
");l.append(e.templates.historyPanel(this.model.toJSON()));this.$emptyMessage(l).text(this.emptyMsg);if(Galaxy&&Galaxy.currUser&&Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(l);this._renderAnnotation(l)}l.find(".history-secondary-actions").prepend(this._renderSelectButton());l.find(".history-dataset-actions").toggle(this.selecting);l.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(l);this.renderHdas(l);return l},_renderTags:function(l){var m=this;this.tagsEditor=new j.TagsEditor({model:this.model,el:l.find(".history-controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){m.toggleHDATagEditors(true,m.fxSpeed)},onhide:function(){m.toggleHDATagEditors(false,m.fxSpeed)},$activator:faIconButton({title:b("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(l.find(".history-secondary-actions"))})},_renderAnnotation:function(l){var m=this;this.annotationEditor=new a.AnnotationEditor({model:this.model,el:l.find(".history-controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){m.toggleHDAAnnotationEditors(true,m.fxSpeed)},onhide:function(){m.toggleHDAAnnotationEditors(false,m.fxSpeed)},$activator:faIconButton({title:b("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(l.find(".history-secondary-actions"))})},_renderSelectButton:function(l){return faIconButton({title:b("Operations on multiple datasets"),classes:"history-select-btn",faIcon:"fa-check-square-o"})},_setUpBehaviours:function(l){l=l||this.$el;i.prototype._setUpBehaviours.call(this,l);if(!this.model){return}this.actionsPopup=this._setUpDatasetActionsPopup(l);if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var m=this;l.find(".history-name").attr("title",b("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(n){var o=m.model.get("name");if(n&&n!==o){m.$el.find(".history-name").text(n);m.model.save({name:n}).fail(function(){m.$el.find(".history-name").text(m.model.previous("name"))})}else{m.$el.find(".history-name").text(o)}}})},_setUpDatasetActionsPopup:function(l){var m=this,n=[{html:b("Hide datasets"),func:function(){var o=d.HistoryDatasetAssociation.prototype.hide;m.getSelectedHdaCollection().ajaxQueue(o)}},{html:b("Unhide datasets"),func:function(){var o=d.HistoryDatasetAssociation.prototype.unhide;m.getSelectedHdaCollection().ajaxQueue(o)}},{html:b("Delete datasets"),func:function(){var o=d.HistoryDatasetAssociation.prototype["delete"];m.getSelectedHdaCollection().ajaxQueue(o)}},{html:b("Undelete datasets"),func:function(){var o=d.HistoryDatasetAssociation.prototype.undelete;m.getSelectedHdaCollection().ajaxQueue(o)}}];if(m.purgeAllowed){n.push({html:b("Permanently delete datasets"),func:function(){if(confirm(b("This will permanently remove the data in your datasets. Are you sure?"))){var o=d.HistoryDatasetAssociation.prototype.purge;m.getSelectedHdaCollection().ajaxQueue(o)}}})}n.push({html:b("Build Dataset List (Experimental)"),func:function(){m.getSelectedHdaCollection().promoteToHistoryDatasetCollection(m.model,"list")}});n.push({html:b("Build Dataset Pair (Experimental)"),func:function(){m.getSelectedHdaCollection().promoteToHistoryDatasetCollection(m.model,"paired")}});n.push({html:b("Build List of Dataset Pairs (Experimental)"),func:_.bind(m._showPairedCollectionModal,m)});return new PopupMenu(l.find(".history-dataset-action-popup-btn"),n)},_showPairedCollectionModal:function(){var l=this,m=l.getSelectedHdaCollection().toJSON().filter(function(n){return n.history_content_type==="dataset"&&n.state===k.OK});if(m.length){require(["mvc/collection/paired-collection-creator"],function(n){window.creator=n.pairedCollectionCreatorModal(m,{historyId:l.model.id})})}else{}},_handleHdaDeletionChange:function(l){if(l.get("deleted")&&!this.storage.get("show_deleted")){this.removeHdaView(this.hdaViews[l.id])}},_handleHdaVisibleChange:function(l){if(l.hidden()&&!this.storage.get("show_hidden")){this.removeHdaView(this.hdaViews[l.id])}},_getContentOptions:function(m){var l=i.prototype._getContentOptions.call(this,m);_.extend(l,{selectable:this.selecting,purgeAllowed:this.purgeAllowed,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});return l},_setUpHdaListeners:function(m){var l=this;i.prototype._setUpHdaListeners.call(this,m);m.on("selected",function(p,o){if(!o){return}var n=[];if((o.shiftKey)&&(l.lastSelectedViewId&&_.has(l.hdaViews,l.lastSelectedViewId))){var r=l.hdaViews[l.lastSelectedViewId];n=l.selectDatasetRange(p,r).map(function(s){return s.model.id})}else{var q=p.model.id;l.lastSelectedViewId=q;n=[q]}l.selectedHdaIds=_.union(l.selectedHdaIds,n)});m.on("de-selected",function(o,n){var p=o.model.id;l.selectedHdaIds=_.without(l.selectedHdaIds,p)})},toggleHDATagEditors:function(l){var m=arguments;_.each(this.hdaViews,function(n){if(n.tagsEditor){n.tagsEditor.toggle.apply(n.tagsEditor,m)}})},toggleHDAAnnotationEditors:function(l){var m=arguments;_.each(this.hdaViews,function(n){if(n.annotationEditor){n.annotationEditor.toggle.apply(n.annotationEditor,m)}})},removeHdaView:function(m){if(!m){return}var l=this;m.$el.fadeOut(l.fxSpeed,function(){m.off();m.remove();delete l.hdaViews[m.model.id];if(_.isEmpty(l.hdaViews)){l.trigger("empty-history",l);l._renderEmptyMsg()}})},events:_.extend(_.clone(i.prototype.events),{"click .history-select-btn":"toggleSelectors","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(l){l=(l!==undefined)?(l):(this.fxSpeed);this.selecting=true;this.$(".history-dataset-actions").slideDown(l);_.each(this.hdaViews,function(m){m.showSelector()});this.selectedHdaIds=[];this.lastSelectedViewId=null},hideSelectors:function(l){l=(l!==undefined)?(l):(this.fxSpeed);this.selecting=false;this.$(".history-dataset-actions").slideUp(l);_.each(this.hdaViews,function(m){m.hideSelector()});this.selectedHdaIds=[];this.lastSelectedViewId=null},toggleSelectors:function(){if(!this.selecting){this.showSelectors()}else{this.hideSelectors()}},selectAllDatasets:function(l){_.each(this.hdaViews,function(m){m.select(l)})},deselectAllDatasets:function(l){this.lastSelectedViewId=null;_.each(this.hdaViews,function(m){m.deselect(l)})},selectDatasetRange:function(n,m){var l=this.hdaViewRange(n,m);_.each(l,function(o){o.select()});return l},getSelectedHdaViews:function(){return _.filter(this.hdaViews,function(l){return l.selected})},getSelectedHdaCollection:function(){return new h.HistoryContents(_.map(this.getSelectedHdaViews(),function(l){return l.model}),{historyId:this.model.id})},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanel:e}}); \ No newline at end of file +define(["mvc/history/history-panel","mvc/history/history-contents","mvc/dataset/states","mvc/history/hda-model","mvc/history/hda-li-edit","mvc/history/hdca-li-edit","mvc/tags","mvc/annotations","utils/localization"],function(f,h,k,d,c,g,j,a,b){var i=f.HistoryPanel;var e=i.extend({HDAViewClass:c.HDAListItemEdit,HDCAViewClass:g.HDCAListItemEdit,initialize:function(l){l=l||{};i.prototype.initialize.call(this,l);this.tagsEditor=null;this.annotationEditor=null;this.purgeAllowed=l.purgeAllowed||false;this.annotationEditorShown=l.annotationEditorShown||false;this.tagsEditorShown=l.tagsEditorShown||false;this.multiselectActions=l.multiselectActions||this._getActions()},_setUpCollectionListeners:function(){i.prototype._setUpCollectionListeners.call(this);this.collection.on("change:deleted",this._handleHdaDeletionChange,this);this.collection.on("change:visible",this._handleHdaVisibleChange,this);this.collection.on("change:purged",function(l){this.model.fetch()},this);return this},_setUpModelListeners:function(){i.prototype._setUpModelListeners.call(this);this.model.on("change:nice_size",this.updateHistoryDiskSize,this);return this},_buildNewRender:function(){var l=i.prototype._buildNewRender.call(this);if(!this.model){return l}if(Galaxy&&Galaxy.currUser&&Galaxy.currUser.id&&Galaxy.currUser.id===this.model.get("user_id")){this._renderTags(l);this._renderAnnotation(l)}return l},_renderTags:function(l){var m=this;this.tagsEditor=new j.TagsEditor({model:this.model,el:l.find(".controls .tags-display"),onshowFirstTime:function(){this.render()},onshow:function(){m.toggleHDATagEditors(true,m.fxSpeed)},onhide:function(){m.toggleHDATagEditors(false,m.fxSpeed)},$activator:faIconButton({title:b("Edit history tags"),classes:"history-tag-btn",faIcon:"fa-tags"}).appendTo(l.find(".controls .actions"))})},_renderAnnotation:function(l){var m=this;this.annotationEditor=new a.AnnotationEditor({model:this.model,el:l.find(".controls .annotation-display"),onshowFirstTime:function(){this.render()},onshow:function(){m.toggleHDAAnnotationEditors(true,m.fxSpeed)},onhide:function(){m.toggleHDAAnnotationEditors(false,m.fxSpeed)},$activator:faIconButton({title:b("Edit history annotation"),classes:"history-annotate-btn",faIcon:"fa-comment"}).appendTo(l.find(".controls .actions"))})},_setUpBehaviors:function(l){l=l||this.$el;i.prototype._setUpBehaviors.call(this,l);if(!this.model){return}if(this.multiselectActions.length){this.actionsPopup=new PopupMenu(l.find(".list-action-popup-btn"),this.multiselectActions)}if((!Galaxy.currUser||Galaxy.currUser.isAnonymous())||(Galaxy.currUser.id!==this.model.get("user_id"))){return}var m=this,n=".controls .name";l.find(n).attr("title",b("Click to rename history")).tooltip({placement:"bottom"}).make_text_editable({on_finish:function(o){var p=m.model.get("name");if(o&&o!==p){m.$el.find(n).text(o);m.model.save({name:o}).fail(function(){m.$el.find(n).text(m.model.previous("name"))})}else{m.$el.find(n).text(p)}}})},_getActions:function(){var l=this,m=[{html:b("Hide datasets"),func:function(){var n=d.HistoryDatasetAssociation.prototype.hide;l.getSelectedModels().ajaxQueue(n)}},{html:b("Unhide datasets"),func:function(){var n=d.HistoryDatasetAssociation.prototype.unhide;l.getSelectedModels().ajaxQueue(n)}},{html:b("Delete datasets"),func:function(){var n=d.HistoryDatasetAssociation.prototype["delete"];l.getSelectedModels().ajaxQueue(n)}},{html:b("Undelete datasets"),func:function(){var n=d.HistoryDatasetAssociation.prototype.undelete;l.getSelectedModels().ajaxQueue(n)}}];if(l.purgeAllowed){m.push({html:b("Permanently delete datasets"),func:function(){if(confirm(b("This will permanently remove the data in your datasets. Are you sure?"))){var n=d.HistoryDatasetAssociation.prototype.purge;l.getSelectedModels().ajaxQueue(n)}}})}m.push({html:b("Build Dataset List"),func:function(){l.getSelectedModels().promoteToHistoryDatasetCollection(l.model,"list")}});m.push({html:b("Build Dataset Pair"),func:function(){l.getSelectedModels().promoteToHistoryDatasetCollection(l.model,"paired")}});m.push({html:b("Build List of Dataset Pairs"),func:_.bind(l._showPairedCollectionModal,l)});return m},_showPairedCollectionModal:function(){var l=this,m=l.getSelectedModels().toJSON().filter(function(n){return n.history_content_type==="dataset"&&n.state===k.OK});if(m.length){require(["mvc/collection/paired-collection-creator"],function(n){window.creator=n.pairedCollectionCreatorModal(m,{historyId:l.model.id})})}else{}},_getItemViewOptions:function(m){var l=i.prototype._getItemViewOptions.call(this,m);_.extend(l,{purgeAllowed:this.purgeAllowed,tagsEditorShown:(this.tagsEditor&&!this.tagsEditor.hidden),annotationEditorShown:(this.annotationEditor&&!this.annotationEditor.hidden)});return l},_handleHdaDeletionChange:function(l){if(l.get("deleted")&&!this.storage.get("show_deleted")){this.removeItemView(l)}},_handleHdaVisibleChange:function(l){if(l.hidden()&&!this.storage.get("show_hidden")){this.removeItemView(l)}},toggleHDATagEditors:function(l){var m=Array.prototype.slice.call(arguments,1);_.each(this.views,function(n){if(n.tagsEditor){n.tagsEditor.toggle.apply(n.tagsEditor,m)}})},toggleHDAAnnotationEditors:function(l){var m=Array.prototype.slice.call(arguments,1);_.each(this.views,function(n){if(n.annotationEditor){n.annotationEditor.toggle.apply(n.annotationEditor,m)}})},events:_.extend(_.clone(i.prototype.events),{"click .show-selectors-btn":"toggleSelectors"}),updateHistoryDiskSize:function(){this.$el.find(".history-size").text(this.model.get("nice_size"))},toString:function(){return"HistoryPanelEdit("+((this.model)?(this.model.get("name")):(""))+")"}});return{HistoryPanelEdit:e}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/history/history-panel.js b/static/scripts/packed/mvc/history/history-panel.js index b94a54abe64..e278f6d5058 100644 --- a/static/scripts/packed/mvc/history/history-panel.js +++ b/static/scripts/packed/mvc/history/history-panel.js @@ -1 +1 @@ -define(["mvc/history/history-model","mvc/history/hda-li","mvc/history/hdca-li","mvc/user/user-model","mvc/base-mvc","utils/localization"],function(e,b,a,f,k,d){var i=k.SessionStorageModel.extend({defaults:{expandedHdas:{},show_deleted:false,show_hidden:false},addExpandedHda:function(m){var n="expandedHdas";this.save(n,_.extend(this.get(n),_.object([m.id],[m.get("id")])))},removeExpandedHda:function(m){var n="expandedHdas";this.save(n,_.omit(this.get(n),m.id))},toString:function(){return"HistoryPrefs("+this.id+")"}});i.storageKeyPrefix="history:";i.historyStorageKey=function g(m){if(!m){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+m)}return(i.storageKeyPrefix+m)};i.get=function c(m){return new i({id:i.historyStorageKey(m)})};i.clearAll=function h(n){for(var m in sessionStorage){if(m.indexOf(i.storageKeyPrefix)===0){sessionStorage.removeItem(m)}}};var j=Backbone.View.extend(k.LoggableMixin).extend({HDAViewClass:b.HDAListItemView,HDCAViewClass:a.HDCAListItemView,tagName:"div",className:"history-panel",fxSpeed:"fast",emptyMsg:d("This history is empty"),noneFoundMsg:d("No matching datasets found"),initialize:function(m){m=m||{};if(m.logger){this.logger=m.logger}this.log(this+".initialize:",m);this.linkTarget=m.linkTarget||"_blank";this.fxSpeed=_.has(m,"fxSpeed")?(m.fxSpeed):(this.fxSpeed);this.filters=[];this.searchFor="";this.findContainerFn=m.findContainerFn;this.hdaViews={};this.indicator=new LoadingIndicator(this.$el);this._setUpListeners();var n=_.pick(m,"initiallyExpanded","show_deleted","show_hidden");this.setModel(this.model,n,false);if(m.onready){m.onready.call(this)}},_setUpListeners:function(){this.on("error",function(n,q,m,p,o){this.errorHandler(n,q,m,p,o)});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(m){this.log(this+"",arguments)},this)}return this},errorHandler:function(o,r,n,q,p){this.error(o,r,n,q,p);window.xhr=r;if(r&&r.status===0&&r.readyState===0){}else{if(r&&r.status===502){}else{var m=this._parseErrorMessage(o,r,n,q,p);if(!this.$messages().is(":visible")){this.once("rendered",function(){this.displayMessage("error",m.message,m.details)})}else{this.displayMessage("error",m.message,m.details)}}}},_parseErrorMessage:function(q,t,u,o,m,r){var p=Galaxy.currUser,s={message:this._bePolite(o),details:{message:o,raven:(window.Raven)?(Raven.lastEventId()):(undefined),agent:navigator.userAgent,url:(window.Galaxy)?(Galaxy.lastAjax.url):(undefined),data:(window.Galaxy)?(Galaxy.lastAjax.data):(undefined),options:(t)?(_.omit(u,"xhr")):(u),xhr:t,source:(_.isFunction(q.toJSON))?(q.toJSON()):(q+""),user:(p instanceof f.User)?(p.toJSON()):(p+"")}};_.extend(s.details,m||{});if(t&&_.isFunction(t.getAllResponseHeaders)){var n=t.getAllResponseHeaders();n=_.compact(n.split("\n"));n=_.map(n,function(v){return v.split(": ")});s.details.xhr.responseHeaders=_.object(n)}return s},_bePolite:function(m){m=m||d("An error occurred while getting updates from the server");return m+". "+d("Please contact a Galaxy administrator if the problem persists")+"."},loadHistoryWithHDADetails:function(o,n,m,q){this.info("loadHistoryWithHDADetails:",o,n,m,q);var p=function(r){return _.values(i.get(r.id).get("expandedHdas"))};return this.loadHistory(o,n,m,q,p)},loadHistory:function(p,o,n,s,q){this.info("loadHistory:",p,o,n,s,q);var m=this;o=o||{};m.trigger("loading-history",m);var r=e.History.getHistoryData(p,{historyFn:n,hdaFn:s,hdaDetailIds:o.initiallyExpanded||q});return m._loadHistoryFromXHR(r,o).fail(function(v,t,u){m.trigger("error",m,v,o,d("An error was encountered while "+t),{historyId:p,history:u||{}})}).always(function(){m.trigger("loading-done",m)})},_loadHistoryFromXHR:function(o,n){var m=this;o.then(function(p,q){m.JSONToModel(p,q,n)});o.fail(function(q,p){m.render()});return o},JSONToModel:function(p,m,n){this.log("JSONToModel:",p,m,n);n=n||{};var o=new e.History(p,m,n);this.setModel(o);return this},setModel:function(n,m,o){m=m||{};o=(o!==undefined)?(o):(true);this.log("setModel:",n,m,o);this.freeModel();this.selectedHdaIds=[];if(n){this.model=n;if(this.logger){this.model.logger=this.logger}this._setUpWebStorage(m.initiallyExpanded,m.show_deleted,m.show_hidden);this._setUpModelEventHandlers();this.trigger("new-model",this)}if(o){this.render()}return this},freeModel:function(){if(this.model){this.model.clearUpdateTimeout();this.stopListening(this.model);this.stopListening(this.model.contents)}this.freeHdaViews();return this},freeHdaViews:function(){this.hdaViews={};return this},_setUpWebStorage:function(n,m,o){this.storage=new i({id:i.historyStorageKey(this.model.get("id"))});if(_.isObject(n)){this.storage.set("exandedHdas",n)}if(_.isBoolean(m)){this.storage.set("show_deleted",m)}if(_.isBoolean(o)){this.storage.set("show_hidden",o)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get());return this},_setUpModelEventHandlers:function(){this.model.contents.on("add",this.addContentView,this);this.model.on("error error:contents",function(n,p,m,o){this.errorHandler(n,p,m,o)},this);return this},render:function(o,p){this.log("render:",o,p);o=(o===undefined)?(this.fxSpeed):(o);var m=this,n;if(this.model){n=this.renderModel()}else{n=this.renderWithoutModel()}$(m).queue("fx",[function(q){if(o&&m.$el.is(":visible")){m.$el.fadeOut(o,q)}else{q()}},function(q){m.$el.empty();if(n){m.$el.append(n.children())}q()},function(q){if(o&&!m.$el.is(":visible")){m.$el.fadeIn(o,q)}else{q()}},function(q){if(p){p.call(this)}m.trigger("rendered",this);q()}]);return this},renderWithoutModel:function(){var m=$("
"),n=$("
").addClass("message-container").css({margin:"4px"});return m.append(n)},renderModel:function(){var m=$("
");m.append(j.templates.historyPanel(this.model.toJSON()));this.$emptyMessage(m).text(this.emptyMsg);m.find(".history-secondary-actions").prepend(this._renderSearchButton());this._setUpBehaviours(m);this.renderHdas(m);return m},_renderEmptyMsg:function(o){var n=this,m=n.$emptyMessage(o);if(!_.isEmpty(n.hdaViews)){m.hide()}else{if(n.searchFor){m.text(n.noneFoundMsg).show()}else{m.text(n.emptyMsg).show()}}return this},_renderSearchButton:function(m){return faIconButton({title:d("Search datasets"),classes:"history-search-btn",faIcon:"fa-search"})},_setUpBehaviours:function(m){m=m||this.$el;m.find("[title]").tooltip({placement:"bottom"});this._setUpSearchInput(m.find(".history-search-controls .history-search-input"));return this},$container:function(){return(this.findContainerFn)?(this.findContainerFn.call(this)):(this.$el.parent())},$datasetsList:function(m){return(m||this.$el).find(".datasets-list")},$messages:function(m){return(m||this.$el).find(".message-container")},$emptyMessage:function(m){return(m||this.$el).find(".empty-history-message")},renderHdas:function(n){n=n||this.$el;var m=this,p={},o=this.model.contents.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);this.$datasetsList(n).empty();if(o.length){o.each(function(r){var q=r.id,s=m._createContentView(r);p[q]=s;if(_.contains(m.selectedHdaIds,q)){s.selected=true}m.attachContentView(s.render(),n)})}this.hdaViews=p;this._renderEmptyMsg(n);return this.hdaViews},_createContentView:function(p){var n=this._getContentClass(p),m=_.extend(this._getContentOptions(p),{model:p}),o=new n(m);this._setUpHdaListeners(o);return o},_getContentClass:function(m){var n=m.get("history_content_type");switch(n){case"dataset":return this.HDAViewClass;case"dataset_collection":return this.HDCAViewClass}throw new TypeError("Unknown history_content_type: "+n)},_getContentOptions:function(m){return{linkTarget:this.linkTarget,expanded:!!this.storage.get("expandedHdas")[m.id],hasUser:this.model.ownedByCurrUser(),logger:this.logger}},_setUpHdaListeners:function(n){var m=this;n.on("error",function(p,r,o,q){m.errorHandler(p,r,o,q)});n.on("expanded",function(o){m.storage.addExpandedHda(o.model)});n.on("collapsed",function(o){m.storage.removeExpandedHda(o.model)});return this},attachContentView:function(o,n){n=n||this.$el;var m=this.$datasetsList(n);m.prepend(o.$el);return this},addContentView:function(p){this.log("add."+this,p);var n=this;if(!p.isVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"))){return n}$({}).queue([function o(r){var q=n.$emptyMessage();if(q.is(":visible")){q.fadeOut(n.fxSpeed,r)}else{r()}},function m(q){var r=n._createContentView(p);n.hdaViews[p.id]=r;r.render().$el.hide();n.scrollToTop();n.attachContentView(r);r.$el.slideDown(n.fxSpeed)}]);return n},views:function(m){var n=this,o=this.model.contents.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters);if(m!==undefined){return n.hdaViews[o.at(m).id]}return o.map(function(p){return n.hdaViews[p.id]})},refreshContents:function(n,m){if(this.model){return this.model.refresh(n,m)}return $.when()},hdaViewRange:function(p,o){if(p===o){return[p]}var m=this,n=false,q=[];this.model.contents.getVisible(this.storage.get("show_deleted"),this.storage.get("show_hidden"),this.filters).each(function(r){if(n){q.push(m.hdaViews[r.id]);if(r===p.model||r===o.model){n=false}}else{if(r===p.model||r===o.model){n=true;q.push(m.hdaViews[r.id])}}});return q},events:{"click .message-container":"clearMessages","click .history-search-btn":"toggleSearchControls"},collapseAllHdaBodies:function(){_.each(this.hdaViews,function(m){m.collapse()});this.storage.set("expandedHdas",{});return this},toggleShowDeleted:function(m){m=(m!==undefined)?(m):(!this.storage.get("show_deleted"));this.storage.set("show_deleted",m);this.renderHdas();return this.storage.get("show_deleted")},toggleShowHidden:function(m){m=(m!==undefined)?(m):(!this.storage.get("show_hidden"));this.storage.set("show_hidden",m);this.renderHdas();return this.storage.get("show_hidden")},_setUpSearchInput:function(n){var o=this,p=".history-search-input";function m(q){if(o.model.contents.haveDetails()){o.searchHdas(q);return}o.$el.find(p).searchInput("toggle-loading");o.model.contents.fetchAllDetails({silent:true}).always(function(){o.$el.find(p).searchInput("toggle-loading")}).done(function(){o.searchHdas(q)})}n.searchInput({initialVal:o.searchFor,name:"history-search",placeholder:"search datasets",classes:"history-search",onfirstsearch:m,onsearch:_.bind(this.searchHdas,this),onclear:_.bind(this.clearHdaSearch,this)});return n},toggleSearchControls:function(o,m){var n=this.$el.find(".history-search-controls"),p=(jQuery.type(o)==="number")?(o):(this.fxSpeed);m=(m!==undefined)?(m):(!n.is(":visible"));if(m){n.slideDown(p,function(){$(this).find("input").focus()})}else{n.slideUp(p)}return m},searchHdas:function(m){var n=this;this.searchFor=m;this.filters=[function(o){return o.matchesAll(n.searchFor)}];this.trigger("search:searching",m,this);this.renderHdas();return this},clearHdaSearch:function(m){this.searchFor="";this.filters=[];this.trigger("search:clear",this);this.renderHdas();return this},_showLoadingIndicator:function(n,m,o){m=(m!==undefined)?(m):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent())}if(!this.$el.is(":visible")){this.indicator.show(0,o)}else{this.$el.fadeOut(m);this.indicator.show(n,m,o)}},_hideLoadingIndicator:function(m,n){m=(m!==undefined)?(m):(this.fxSpeed);if(this.indicator){this.indicator.hide(m,n)}},displayMessage:function(r,s,q){var o=this;this.scrollToTop();var p=this.$messages(),m=$("
").addClass(r+"message").html(s);if(!_.isEmpty(q)){var n=$('Details').click(function(){Galaxy.modal.show(o._messageToModalOptions(r,s,q));return false});m.append(" ",n)}return p.html(m)},_messageToModalOptions:function(q,s,p){var m=this,r=$("
"),o={title:"Details"};function n(t){t=_.omit(t,_.functions(t));return["",_.map(t,function(v,u){v=(_.isObject(v))?(n(v)):(v);return'"}).join(""),"
'+u+''+v+"
"].join("")}if(_.isObject(p)){o.body=r.append(n(p))}else{o.body=r.html(p)}o.buttons={Ok:function(){Galaxy.modal.hide();m.clearMessages()}};return o},clearMessages:function(){this.$messages().empty();return this},scrollPosition:function(){return this.$container().scrollTop()},scrollTo:function(m){this.$container().scrollTop(m);return this},scrollToTop:function(){this.$container().scrollTop(0);return this},scrollToId:function(n){if((!n)||(!this.hdaViews[n])){return this}var m=this.hdaViews[n];this.scrollTo(m.el.offsetTop);return this},scrollToHid:function(m){var n=this.model.contents.getByHid(m);if(!n){return this}return this.scrollToId(n.id)},print:function(){var m=this;m.debug(this);_.each(this.hdaViews,function(n,o){m.debug("\t "+o,n)})},toString:function(){return"ReadOnlyHistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});var l=['
','
','
',"
",'
',"<% if( history.name ){ %>",'
<%= history.name %>
',"<% } %>","
",'
',"<% if( history.nice_size ){ %>",'
<%= history.nice_size %>
',"<% } %>",'
',"
","<% if( history.deleted ){ %>",'
',d("You are currently viewing a deleted history!"),"
","<% } %>",'
',"<% if( history.message ){ %>",'
<%= history.message %>
',"<% } %>","
",'
',d("You are over your disk quota"),". ",d("Tool execution is on hold until your disk usage drops below your allocated quota"),".","
",'
','
','
','
','",'","
",'","
","
",'
','
',d("This history is empty"),"
"].join("");j.templates={historyPanel:function(m){return _.template(l,m,{variable:"history"})}};return{ReadOnlyHistoryPanel:j}}); \ No newline at end of file +define(["mvc/list/list-panel","mvc/history/history-model","mvc/history/history-contents","mvc/history/hda-li","mvc/history/hdca-li","mvc/user/user-model","mvc/base-mvc","utils/localization"],function(d,f,l,b,a,g,n,e){var j=n.SessionStorageModel.extend({defaults:{expandedIds:{},show_deleted:false,show_hidden:false},addExpandedHda:function(o){var p="expandedIds";this.save(p,_.extend(this.get(p),_.object([o.id],[o.get("id")])))},removeExpandedHda:function(o){var p="expandedIds";this.save(p,_.omit(this.get(p),o.id))},toString:function(){return"HistoryPrefs("+this.id+")"}});j.storageKeyPrefix="history:";j.historyStorageKey=function h(o){if(!o){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+o)}return(j.storageKeyPrefix+o)};j.get=function c(o){return new j({id:j.historyStorageKey(o)})};j.clearAll=function i(p){for(var o in sessionStorage){if(o.indexOf(j.storageKeyPrefix)===0){sessionStorage.removeItem(o)}}};var m=d.ModelListPanel;var k=m.extend({HDAViewClass:b.HDAListItemView,HDCAViewClass:a.HDCAListItemView,collectionClass:l.HistoryContents,modelCollectionKey:"contents",tagName:"div",className:m.prototype.className+" history-panel",emptyMsg:e("This history is empty"),noneFoundMsg:e("No matching datasets found"),searchPlaceholder:e("search datasets"),initialize:function(o){m.prototype.initialize.call(this,o);this.linkTarget=o.linkTarget||"_blank"},freeModel:function(){m.prototype.freeModel.call(this);if(this.model){this.model.clearUpdateTimeout()}return this},_setUpListeners:function(){m.prototype._setUpListeners.call(this);this.on("error",function(p,s,o,r,q){this.errorHandler(p,s,o,r,q)});this.on("loading-done",function(){if(!this.views.length){this.trigger("empty-history",this)}})},loadHistoryWithDetails:function(r,q,p,s){this.info("loadHistoryWithDetails:",r,q,p,s);var o=function(t){return _.values(j.get(t.id).get("expandedIds"))};return this.loadHistory(r,q,p,s,o)},loadHistory:function(s,r,q,t,o){this.info("loadHistory:",s,r,q,t,o);var p=this;r=r||{};p.trigger("loading",p);var u=f.History.getHistoryData(s,{historyFn:q,contentsFn:t,detailIdsFn:r.initiallyExpanded||o});return p._loadHistoryFromXHR(u,r).fail(function(x,v,w){p.trigger("error",p,x,r,e("An error was encountered while "+v),{historyId:s,history:w||{}})}).always(function(){p.trigger("loading-done",p)})},_loadHistoryFromXHR:function(q,p){var o=this;q.then(function(r,s){o.JSONToModel(r,s,p);o.render()});q.fail(function(s,r){o.render()});return q},JSONToModel:function(r,o,p){this.log("JSONToModel:",r,o,p);p=p||{};var q=new f.History(r,o,p);this.setModel(q);return q},setModel:function(p,o){o=o||{};m.prototype.setModel.call(this,p,o);if(this.model){this._setUpWebStorage(o.initiallyExpanded,o.show_deleted,o.show_hidden)}},_setUpWebStorage:function(p,o,q){this.storage=new j({id:j.historyStorageKey(this.model.get("id"))});if(_.isObject(p)){this.storage.set("expandedIds",p)}if(_.isBoolean(o)){this.storage.set("show_deleted",o)}if(_.isBoolean(q)){this.storage.set("show_hidden",q)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get());return this},_buildNewRender:function(){var o=m.prototype._buildNewRender.call(this);if(this.multiselectActions.length){o.find(".controls .actions").prepend(this._renderSelectButton())}return o},_renderSelectButton:function(o){return faIconButton({title:e("Operations on multiple datasets"),classes:"show-selectors-btn",faIcon:"fa-check-square-o"})},_getItemViewClass:function(o){var p=o.get("history_content_type");switch(p){case"dataset":return this.HDAViewClass;case"dataset_collection":return this.HDCAViewClass}throw new TypeError("Unknown history_content_type: "+p)},_filterItem:function(p){var o=this;return(m.prototype._filterItem.call(o,p)&&(!p.hidden()||o.storage.get("show_hidden"))&&(!p.isDeletedOrPurged()||o.storage.get("show_deleted")))},_getItemViewOptions:function(p){var o=m.prototype._getItemViewOptions.call(this,p);return _.extend(o,{linkTarget:this.linkTarget,expanded:!!this.storage.get("expandedIds")[p.id],hasUser:this.model.ownedByCurrUser()})},_setUpItemViewListeners:function(p){var o=this;m.prototype._setUpItemViewListeners.call(o,p);p.on("expanded",function(q){o.storage.addExpandedHda(q.model)});p.on("collapsed",function(q){o.storage.removeExpandedHda(q.model)});return this},refreshContents:function(p,o){if(this.model){return this.model.refresh(p,o)}return $.when()},getSelectedModels:function(){var o=m.prototype.getSelectedModels.call(this);o.historyId=this.collection.historyId;return o},events:_.extend(_.clone(m.prototype.events),{"click .show-selectors-btn":"toggleSelectors"}),toggleShowDeleted:function(o){o=(o!==undefined)?(o):(!this.storage.get("show_deleted"));this.storage.set("show_deleted",o);this.renderItems();return this.storage.get("show_deleted")},toggleShowHidden:function(o){o=(o!==undefined)?(o):(!this.storage.get("show_hidden"));this.storage.set("show_hidden",o);this.renderItems();return this.storage.get("show_hidden")},_firstSearch:function(o){var p=this,q=".history-search-input";this.log("onFirstSearch",o);if(p.model.contents.haveDetails()){p.searchItems(o);return}p.$el.find(q).searchInput("toggle-loading");p.model.contents.fetchAllDetails({silent:true}).always(function(){p.$el.find(q).searchInput("toggle-loading")}).done(function(){p.searchItems(o)})},errorHandler:function(q,t,p,s,r){this.error(q,t,p,s,r);if(t&&t.status===0&&t.readyState===0){}else{if(t&&t.status===502){}else{var o=this._parseErrorMessage(q,t,p,s,r);if(!this.$messages().is(":visible")){this.once("rendered",function(){this.displayMessage("error",o.message,o.details)})}else{this.displayMessage("error",o.message,o.details)}}}},_parseErrorMessage:function(s,v,w,q,o,t){var r=Galaxy.currUser,u={message:this._bePolite(q),details:{message:q,raven:(window.Raven&&_.isFunction(Raven.lastEventId))?(Raven.lastEventId()):(undefined),agent:navigator.userAgent,url:(window.Galaxy)?(Galaxy.lastAjax.url):(undefined),data:(window.Galaxy)?(Galaxy.lastAjax.data):(undefined),options:(v)?(_.omit(w,"xhr")):(w),xhr:v,source:(_.isFunction(s.toJSON))?(s.toJSON()):(s+""),user:(r instanceof g.User)?(r.toJSON()):(r+"")}};_.extend(u.details,o||{});if(v&&_.isFunction(v.getAllResponseHeaders)){var p=v.getAllResponseHeaders();p=_.compact(p.split("\n"));p=_.map(p,function(x){return x.split(": ")});u.details.xhr.responseHeaders=_.object(p)}return u},_bePolite:function(o){o=o||e("An error occurred while getting updates from the server");return o+". "+e("Please contact a Galaxy administrator if the problem persists")+"."},displayMessage:function(t,u,s){var q=this;this.scrollToTop();var r=this.$messages(),o=$("
").addClass(t+"message").html(u);if(!_.isEmpty(s)){var p=$('Details').click(function(){Galaxy.modal.show(q._messageToModalOptions(t,u,s));return false});o.append(" ",p)}return r.html(o)},_messageToModalOptions:function(r,u,q){var o=this,p={title:"Details"};if(_.isObject(q)){q=_.omit(q,_.functions(q));var t=JSON.stringify(q,null," "),s=$("
").text(t);p.body=$("
").append(s)}else{p.body=$("
").html(q)}p.buttons={Ok:function(){Galaxy.modal.hide();o.clearMessages()}};return p},clearMessages:function(o){$(o.currentTarget).fadeOut(this.fxSpeed,function(){$(this).remove()});return this},scrollToHid:function(o){return this.scrollToItem(_.first(this.viewsWhereModel({hid:o})))},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});k.prototype.templates=(function(){var o=n.wrapTemplate(['
','
','
<%= history.name %>
',"
",'
',"
",'
<%= history.nice_size %>
','
','
',"<% if( history.deleted ){ %>",'
',e("This history has been deleted"),"
","<% } %>","<% if( history.message ){ %>",'
messagesmall">',"<%= history.message.text %>","
","<% } %>","
",'
','
','",'
','
','",'","
",'","
","
"],"history");return _.extend(_.clone(m.prototype.templates),{controls:o})}());return{HistoryPanel:k}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/list/list-item.js b/static/scripts/packed/mvc/list/list-item.js new file mode 100644 index 00000000000..f69e5d26b8d --- /dev/null +++ b/static/scripts/packed/mvc/list/list-item.js @@ -0,0 +1 @@ +define(["mvc/base-mvc","utils/localization"],function(b,c){var d=Backbone.View.extend(b.LoggableMixin).extend({initialize:function(e){this.expanded=e.expanded||false;this.fxSpeed=e.fxSpeed||this.fxSpeed},fxSpeed:"fast",render:function(f){var e=this._buildNewRender();this._setUpBehaviors(e);this._queueNewRender(e,f);return this},_buildNewRender:function(){var e=$(this.templates.el(this.model.toJSON(),this));if(this.expanded){this.$details(e).replaceWith(this._renderDetails().show())}return e},_queueNewRender:function(f,g){g=(g===undefined)?(this.fxSpeed):(g);var e=this;$(e).queue("fx",[function(h){this.$el.fadeOut(g,h)},function(h){e._swapNewRender(f);h()},function(h){this.$el.fadeIn(g,h)},function(h){this.trigger("rendered",e);h()}])},_swapNewRender:function(e){return this.$el.empty().attr("class",this.className).append(e.children())},_setUpBehaviors:function(e){e=e||this.$el;e.find("[title]").tooltip({placement:"bottom"})},$details:function(e){e=e||this.$el;return e.find(".details")},_renderDetails:function(){var e=$(this.templates.details(this.model.toJSON(),this));this._setUpBehaviors(e);return e},toggleExpanded:function(e){e=(e===undefined)?(!this.expanded):(e);if(e){this.expand()}else{this.collapse()}return this},expand:function(){var e=this;return e._fetchModelDetails().always(function(){var f=e._renderDetails();e.$details().replaceWith(f);e.expanded=true;f.slideDown(e.fxSpeed,function(){e.trigger("expanded",e)})})},_fetchModelDetails:function(){if(!this.model.hasDetails()){return this.model.fetch()}return jQuery.when()},collapse:function(){var e=this;e.expanded=false;this.$details().slideUp(e.fxSpeed,function(){e.trigger("collapsed",e)})}});var a=d.extend(b.mixin(b.SelectableViewMixin,b.DraggableViewMixin,{tagName:"div",className:"list-item",initialize:function(e){d.prototype.initialize.call(this,e);b.SelectableViewMixin.initialize.call(this,e);b.DraggableViewMixin.initialize.call(this,e)},_buildNewRender:function(){var e=d.prototype._buildNewRender.call(this);e.find(".warnings").replaceWith(this._renderWarnings());e.find(".title-bar").replaceWith(this._renderTitleBar());e.find(".primary-actions").append(this._renderPrimaryActions());e.find(".subtitle").replaceWith(this._renderSubtitle());return e},_swapNewRender:function(e){d.prototype._swapNewRender.call(this,e);if(this.selectable){this.showSelector(0)}if(this.draggable){this.draggableOn()}return this.$el},_renderWarnings:function(){var e=this,g=$('
'),f=e.model.toJSON();_.each(e.templates.warnings,function(h){g.append($(h(f,e)))});return g},_renderTitleBar:function(){return $(this.templates.titleBar(this.model.toJSON(),this))},_renderPrimaryActions:function(){return[]},_renderSubtitle:function(){return $(this.templates.subtitle(this.model.toJSON(),this))},events:{"click .title-bar":"_clickTitleBar","keydown .title-bar":"_keyDownTitleBar","click .selector":"toggleSelect"},_clickTitleBar:function(e){e.stopPropagation();this.toggleExpanded()},_keyDownTitleBar:function(g){var e=32,f=13;if(g&&(g.type==="keydown")&&(g.keyCode===e||g.keyCode===f)){this.toggleExpanded();g.stopPropagation();return false}return true},toString:function(){var e=(this.model)?(this.model+""):("(no model)");return"ListItemView("+e+")"}}));a.prototype.templates=(function(){var g=b.wrapTemplate(['
','
','
','',"
",'
','
','
',"
"]);var e={};var h=b.wrapTemplate(['
','','
','<%- element.name %>',"
",'
',"
"],"element");var i=b.wrapTemplate(['
']);var f=b.wrapTemplate(['
']);return{el:g,warnings:e,titleBar:h,subtitle:i,details:f}}());return{ExpandableView:d,ListItemView:a}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/list/list-panel.js b/static/scripts/packed/mvc/list/list-panel.js new file mode 100644 index 00000000000..68e0ef5623d --- /dev/null +++ b/static/scripts/packed/mvc/list/list-panel.js @@ -0,0 +1 @@ +define(["mvc/list/list-item","mvc/base-mvc","utils/localization"],function(d,b,c){var e=Backbone.View.extend(b.LoggableMixin).extend({viewClass:d.ListItemView,collectionClass:Backbone.Collection,tagName:"div",className:"list-panel",fxSpeed:"fast",emptyMsg:c("This list is empty"),noneFoundMsg:c("No matching items found"),searchPlaceholder:c("search"),multiselectActions:[],initialize:function(f,g){f=f||{};if(f.logger){this.logger=f.logger}this.log(this+".initialize:",f);this.fxSpeed=_.has(f,"fxSpeed")?(f.fxSpeed):(this.fxSpeed);this.filters=[];this.searchFor=f.searchFor||"";this.indicator=new LoadingIndicator(this.$el);this.selecting=(f.selecting!==undefined)?f.selecting:true;this.selected=f.selected||[];this.lastSelected=null;this.viewClass=f.viewClass||this.viewClass;this.views=[];this.collection=f.collection||(new this.collectionClass([]));this.filters=f.filters||[];this.$scrollContainer=f.$scrollContainer||this.$scrollContainer;this.title=f.title||"";this.subtitle=f.subtitle||"";this.multiselectActions=f.multiselectActions||this.multiselectActions;this.actionsPopup=null;this._setUpListeners()},freeViews:function(){_.each(this.views,function(f){f.off()});this.views=[];return this},_setUpListeners:function(){this.off();this.on("error",function(g,j,f,i,h){console.error(g,j,f,i,h)},this);this.on("loading",function(){this._showLoadingIndicator("loading...",40)},this);this.on("loading-done",function(){this._hideLoadingIndicator(40)},this);this.once("rendered",function(){this.trigger("rendered:initial",this)},this);if(this.logger){this.on("all",function(f){this.log(this+"",arguments)},this)}this._setUpCollectionListeners();this._setUpViewListeners();return this},_setUpCollectionListeners:function(){this.log(this+"._setUpCollectionListeners",this.collection);this.collection.off();this.collection.on("error",function(g,j,f,i,h){this.trigger("error",g,j,f,i,h)},this);this.collection.on("reset",function(){this.renderItems()},this);this.collection.on("add",this.addItemView,this);this.collection.on("remove",this.removeItemView,this);if(this.logger){this.collection.on("all",function(f){this.info(this+"(collection)",arguments)},this)}return this},_setUpViewListeners:function(){this.log(this+"._setUpViewListeners");this.on("view:selected",function(f,g){if(g&&g.shiftKey&&this.lastSelected){var h=_.find(this.views,function(i){return i.model.id===this.lastSelected});if(h){this.selectRange(f,h)}}this.selected.push(f.model.id);this.lastSelected=f.model.id},this);this.on("view:de-selected",function(f,g){this.selected=_.without(this.selected,f.model.id)},this)},render:function(g){this.log(this+".render",g);var f=this._buildNewRender();this._setUpBehaviors(f);this._queueNewRender(f,g);return this},_buildNewRender:function(){this.debug(this+"(ListPanel)._buildNewRender");var f=$(this.templates.el({},this));this._renderControls(f);this._renderTitle(f);this._renderSubtitle(f);this._renderSearch(f);this.renderItems(f);return f},_renderControls:function(g){this.debug(this+"(ListPanel)._renderControls");var f=$(this.templates.controls({},this));g.find(".controls").replaceWith(f);return f},_renderTitle:function(f){},_renderSubtitle:function(f){},_queueNewRender:function(g,h){h=(h===undefined)?(this.fxSpeed):(h);var f=this;f.log("_queueNewRender:",g,h);$(f).queue("fx",[function(i){this.$el.fadeOut(h,i)},function(i){f._swapNewRender(g);i()},function(i){this.$el.fadeIn(h,i)},function(i){f.trigger("rendered",f);i()}])},_swapNewRender:function(f){this.$el.empty().attr("class",this.className).append(f.children());if(this.selecting){this.showSelectors(0)}return this},_setUpBehaviors:function(f){f=f||this.$el;f.find(".controls [title]").tooltip({placement:"bottom"});return this},$scrollContainer:function(){return this.$el.parent().parent()},$list:function(f){return(f||this.$el).find(".list-items")},$messages:function(f){return(f||this.$el).find(".messages")},$emptyMessage:function(f){return(f||this.$el).find(".empty-message")},renderItems:function(h){h=h||this.$el;var f=this;f.log(this+".renderItems",h);var g=f.$list(h);f.views=f._filterCollection().map(function(i){return f._createItemView(i).render(0)});g.empty();if(f.views.length){f._attachItems(h);f.$emptyMessage(h).hide()}else{f._renderEmptyMessage(h).show()}return f.views},_filterCollection:function(){var f=this;return f.collection.filter(_.bind(f._filterItem,f))},_filterItem:function(g){var f=this;return(_.every(f.filters.map(function(h){return h.call(g)})))&&(!f.searchFor||g.matchesAll(f.searchFor))},_createItemView:function(h){var i=this._getItemViewClass(h),g=_.extend(this._getItemViewOptions(h),{model:h}),f=new i(g);this._setUpItemViewListeners(f);return f},_getItemViewClass:function(f){return this.viewClass},_getItemViewOptions:function(f){return{fxSpeed:this.fxSpeed,expanded:false,selectable:this.selecting,selected:_.contains(this.selected,f.id),draggable:this.dragging}},_setUpItemViewListeners:function(g){var f=this;g.on("all",function(){var h=Array.prototype.slice.call(arguments,0);h[0]="view:"+h[0];f.trigger.apply(f,h)});return f},_attachItems:function(f){this.$list(f).append(this.views.map(function(g){return g.$el}));return this},_renderEmptyMessage:function(f){this.debug("_renderEmptyMessage",f,this.searchFor);var g=this.searchFor?this.noneFoundMsg:this.emptyMsg;return this.$emptyMessage(f).text(g)},expandAll:function(){_.each(this.views,function(f){f.expand()})},collapseAll:function(){_.each(this.views,function(f){f.collapse()})},addItemView:function(i,j,h){this.log(this+".addItemView:",i);var g=this;if(!g._filterItem(i)){return undefined}var f=g._createItemView(i);g.views.push(f);$(f).queue("fx",[function(k){g.$emptyMessage().fadeOut(g.fxSpeed,k)},function(k){g.$list().append(f.render().$el);k()}]);return f},removeItemView:function(i,j,h){this.log(this+".removeItemView:",i);var g=this,f=g.viewFromModel(i);if(!f){return undefined}$({}).queue("fx",[function(k){f.$el.fadeOut(g.fxSpeed,k)},function(k){g.views=_.without(g.views,f);f.remove();if(!g.views.length){g._renderEmptyMessage().fadeIn(g.fxSpeed,k)}else{k()}}]);return f},viewFromModel:function(g){for(var h=0;h",'
','
','
',"
"]);var f=b.wrapTemplate(['
','
','
<%= view.title %>
',"
",'
<%= view.subtitle %>
','
','
','",'
','
','",'","
","
","
"]);return{el:g,controls:f}}());var a=e.extend({modelCollectionKey:"contents",initialize:function(f){e.prototype.initialize.call(this,f);this.selecting=(f.selecting!==undefined)?f.selecting:false;this.setModel(this.model,f)},setModel:function(g,f){f=f||{};this.debug(this+".setModel:",g,f);this.freeModel();this.freeViews();if(g){this.model=g;if(this.logger){this.model.logger=this.logger}this._setUpModelListeners();this.collection.off();this.collection=(this.model[this.modelCollectionKey])?this.model[this.modelCollectionKey]:(f.collection||(new this.collectionClass([])));this._setUpCollectionListeners();this.trigger("new-model",this)}return this},freeModel:function(){if(this.model){this.stopListening(this.model)}return this},_setUpModelListeners:function(){this.log(this+"._setUpModelListeners",this.model);this.model.on("error",function(){this.trigger.apply(panel,arguments)},this);return this},_renderControls:function(g){this.debug(this+"(ListPanel)._renderControls");var h=this.model?this.model.toJSON():{},f=$(this.templates.controls(h,this));g.find(".controls").replaceWith(f);this.debug("\t .controls:",this.$(".controls"));this.debug("\t $controls:",f);return f},toString:function(){return"ModelListPanel("+this.model+")"}});a.prototype.templates=(function(){var f=b.wrapTemplate(['
','
','
<%= model.name %>
',"
",'
<%= view.subtitle %>
','
','
','",'
','
','",'","
","
","
"]);return _.extend(_.clone(e.prototype.templates),{controls:f})}());return{ListPanel:e,ModelListPanel:a}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/tools/tools-form.js b/static/scripts/packed/mvc/tools/tools-form.js index 1ea9961ac41..5308c575108 100644 --- a/static/scripts/packed/mvc/tools/tools-form.js +++ b/static/scripts/packed/mvc/tools/tools-form.js @@ -1 +1 @@ -define(["mvc/ui/ui-portlet","mvc/ui/ui-misc","mvc/citation/citation-model","mvc/citation/citation-view","mvc/tools","mvc/tools/tools-template","mvc/tools/tools-datasets","mvc/tools/tools-section","mvc/tools/tools-tree"],function(g,k,i,a,f,d,h,j,c){var e=Backbone.Model.extend({initialize:function(l){this.url=galaxy_config.root+"api/tools/"+l.id+"?io_details=true"}});var b=Backbone.View.extend({main_el:"body",initialize:function(m){var l=this;this.options=m;this.model=new e({id:m.id});this.tree=new c(this);this.field_list={};this.input_list={};this.datasets=new h({history_id:this.options.history_id,success:function(){l._initializeToolForm()}})},_initializeToolForm:function(){var m=this;var n=new k.ButtonIcon({icon:"fa-question-circle",title:"Question?",tooltip:"Ask a question about this tool (Biostar)",onclick:function(){window.open(m.options.biostar_url+"/p/new/post/")}});var o=new k.ButtonIcon({icon:"fa-search",title:"Search",tooltip:"Search help for this tool (Biostar)",onclick:function(){window.open(m.options.biostar_url+"/t/"+m.options.id+"/")}});var l=new k.ButtonIcon({icon:"fa-share",title:"Share",tooltip:"Share this tool",onclick:function(){prompt("Copy to clipboard: Ctrl+C, Enter",galaxy_config.root+"root?tool_id="+m.options.id)}});this.model.fetch({error:function(p){console.debug("tools-form::_initializeToolForm() : Attempt to fetch tool model failed.")},success:function(){m.inputs=m.model.get("inputs");m.portlet=new g.View({icon:"fa-wrench",title:""+m.model.get("name")+" "+m.model.get("description"),buttons:{execute:new k.ButtonIcon({icon:"fa-check",tooltip:"Execute the tool",title:"Execute",floating:"clear",onclick:function(){m._submit()}})},operations:{button_question:n,button_search:o,button_share:l}});if(!m.options.biostar_url){n.$el.hide();o.$el.hide()}m.message=new k.Message();m.portlet.append(m.message.$el);$(m.main_el).append(m.portlet.$el);if(m.options.help!=""){$(m.main_el).append(d.help(m.options.help))}if(m.options.citations){$(m.main_el).append(d.citations());var p=new i.ToolCitationCollection();p.tool_id=m.options.id;var q=new a.CitationListView({collection:p});q.render();p.fetch()}m.setElement(m.portlet.content());m.section=new j.View(m,{inputs:m.model.get("inputs")});m.portlet.append(m.section.$el);m.refresh()}})},refresh:function(){this.tree.refresh();for(var l in this.field_list){this.field_list[l].trigger("change")}console.debug("tools-form::refresh() - Recreated tree structure. Refresh.")},_submit:function(){console.log(this.tree.jobDictionary())}});return{View:b}}); \ No newline at end of file +define(["mvc/ui/ui-portlet","mvc/ui/ui-misc","mvc/citation/citation-model","mvc/citation/citation-view","mvc/tools","mvc/tools/tools-template","mvc/tools/tools-datasets","mvc/tools/tools-section","mvc/tools/tools-tree"],function(g,k,i,a,f,d,h,j,c){var e=Backbone.Model.extend({initialize:function(l){this.url=galaxy_config.root+"api/tools/"+l.id+"?io_details=true"}});var b=Backbone.View.extend({main_el:"body",initialize:function(m){var l=this;this.options=m;this.model=new e({id:m.id});this.tree=new c(this);this.field_list={};this.input_list={};this.datasets=new h({history_id:this.options.history_id,success:function(){l._initializeToolForm()}})},_initializeToolForm:function(){var m=this;var n=new k.ButtonIcon({icon:"fa-question-circle",title:"Question?",tooltip:"Ask a question about this tool (Biostar)",onclick:function(){window.open(m.options.biostar_url+"/p/new/post/")}});var o=new k.ButtonIcon({icon:"fa-search",title:"Search",tooltip:"Search help for this tool (Biostar)",onclick:function(){window.open(m.options.biostar_url+"/t/"+m.options.id+"/")}});var l=new k.ButtonIcon({icon:"fa-share",title:"Share",tooltip:"Share this tool",onclick:function(){prompt("Copy to clipboard: Ctrl+C, Enter",galaxy_config.root+"root?tool_id="+m.options.id)}});this.model.fetch({error:function(p){console.debug("tools-form::_initializeToolForm() : Attempt to fetch tool model failed.")},success:function(){m.inputs=m.model.get("inputs");m.portlet=new g.View({icon:"fa-wrench",title:""+m.model.get("name")+" "+m.model.get("description"),buttons:{execute:new k.ButtonIcon({icon:"fa-check",tooltip:"Execute the tool",title:"Execute",floating:"clear",onclick:function(){m._submit()}})},operations:{button_question:n,button_search:o,button_share:l}});if(!m.options.biostar_url){n.$el.hide();o.$el.hide()}m.message=new k.Message();m.portlet.append(m.message.$el);$(m.main_el).append(m.portlet.$el);if(m.options.help!=""){$(m.main_el).append(d.help(m.options.help))}if(m.options.citations){$(m.main_el).append(d.citations());var p=new i.ToolCitationCollection();p.tool_id=m.options.id;var q=new a.CitationListView({collection:p});q.render();p.fetch()}m.setElement(m.portlet.content());m.section=new j.View(m,{inputs:m.model.get("inputs")});m.portlet.append(m.section.$el);m.refresh();m._submit()}})},refresh:function(){this.tree.refresh();for(var l in this.field_list){this.field_list[l].trigger("change")}console.debug("tools-form::refresh() - Recreated tree structure. Refresh.")},_submit:function(){console.log(this.tree.finalize())}});return{View:b}}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/tools/tools-tree.js b/static/scripts/packed/mvc/tools/tools-tree.js index dbed81ff07d..0eb9021032a 100644 --- a/static/scripts/packed/mvc/tools/tools-tree.js +++ b/static/scripts/packed/mvc/tools/tools-tree.js @@ -1 +1 @@ -define([],function(){return Backbone.Model.extend({initialize:function(a){this.app=a},refresh:function(){if(!this.app.section){return{}}this.dict={};this.xml=$("
");this._iterate(this.app.section.$el,this.dict,this.xml)},jobDictionary:function(){var a=this;var b={};function c(e,g){for(var f in g){if(g[f].input){var d=g[f].input;var h=e+d.name;if(d.type=="repeat"){}else{switch(d.type){case"repeat":b[h]=value;break;default:b[h]=value}}}}}c("",this.dict);return b},_iterate:function(d,e,b){var a=this;var c=$(d).children();c.each(function(){var i=this;var h=$(i).attr("id");if($(i).hasClass("section-row")||$(i).hasClass("tab-pane")){e[h]={};var f=a.app.input_list[h];if(f){e[h]={input:f}}var g=$('
');b.append(g);a._iterate(i,e[h],g)}else{a._iterate(i,e,b)}})},findReferences:function(c,e){var g=[];var b=this;function d(h,j){var i=$(j).children();var l=[];var k=false;i.each(function(){var o=this;var n=$(o).attr("id");if(n!==c){var m=b.app.input_list[n];if(m){if(m.name==h){k=true;return false}if(m.data_ref==h&&m.type==e){l.push(n)}}}});if(!k){g=g.concat(l);i.each(function(){d(h,this)})}}var f=this.xml.find("#"+c);if(f.length>0){var a=this.app.input_list[c];if(a){d(a.name,f.parent())}}return g}})}); \ No newline at end of file +define([],function(){return Backbone.Model.extend({initialize:function(a){this.app=a},refresh:function(){this.dict={};this.xml=$("
");if(!this.app.section){return{}}this._iterate(this.app.section.$el,this.dict,this.xml)},finalize:function(){var a=this;var b={};function c(k,l){for(var f in l){var d=l[f];if(d.input){var m=d.input;var h=k;if(k!=""){h+="|"}h+=m.name;switch(m.type){case"repeat":var g=0;for(var e in d){if(e.indexOf("section")!=-1){c(h+"_"+g++,d[e])}}break;case"conditional":var n=a.app.field_list[m.id].value();for(var e in m.cases){if(m.cases[e].value==n){c(h,l[m.id+"-section-"+e]);break}}break;default:var n=a.app.field_list[m.id].value();b[h]=n}}}}c("",this.dict);return b},findReferences:function(c,e){var g=[];var b=this;function d(h,j){var i=$(j).children();var l=[];var k=false;i.each(function(){var o=this;var n=$(o).attr("id");if(n!==c){var m=b.app.input_list[n];if(m){if(m.name==h){k=true;return false}if(m.data_ref==h&&m.type==e){l.push(n)}}}});if(!k){g=g.concat(l);i.each(function(){d(h,this)})}}var f=this.xml.find("#"+c);if(f.length>0){var a=this.app.input_list[c];if(a){d(a.name,f.parent())}}return g},_iterate:function(d,e,b){var a=this;var c=$(d).children();c.each(function(){var i=this;var h=$(i).attr("id");if($(i).hasClass("section-row")||$(i).hasClass("tab-pane")){e[h]={};var f=a.app.input_list[h];if(f){e[h]={input:f}}var g=$('
');b.append(g);a._iterate(i,e[h],g)}else{a._iterate(i,e,b)}})}})}); \ No newline at end of file diff --git a/static/scripts/packed/mvc/ui.js b/static/scripts/packed/mvc/ui.js index 433aa09bbb4..60fcf202d27 100644 --- a/static/scripts/packed/mvc/ui.js +++ b/static/scripts/packed/mvc/ui.js @@ -1 +1 @@ -var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a=""}else{a=""}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("
")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['"].join("")},_templateOptions:function(a){if(!a.length){return"
  • (no options)
  • "}return _.map(a,function(d){if(d.divider){return'
  • '}else{if(d.header){return['
  • ',d.html,"
  • "].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?(''):("");return['
  • ",b,d.html,"
  • "].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a','',""].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['
    ','
    ','','loading...',"
    ","
    "].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";a.parent().find(".loading-indicator").remove();b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b}(function(){var b=window._l||function(d){return d};function a(k,q){var e=27,n=13,d=$(k),f=true,h={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(r){},minSearchLen:0,escWillClear:true,oninit:function(){}};function j(r){var s=$(this).parent().children("input");s.val("");s.trigger("clear:searchInput");q.onclear()}function p(s,r){$(this).trigger("search:searchInput",r);if(typeof q.onfirstsearch==="function"&&f){f=false;q.onfirstsearch(r)}else{q.onsearch(r)}}function g(){return['"].join("")}function m(){return $(g()).focus(function(r){$(this).select()}).keyup(function(s){s.preventDefault();s.stopPropagation();if(!$(this).val()){$(this).blur()}if(s.which===e&&q.escWillClear){j.call(this,s)}else{var r=$(this).val();if((s.which===n)||(q.minSearchLen&&r.length>=q.minSearchLen)){p.call(this,s,r)}else{if(!r.length){j.call(this,s)}}}}).val(q.initialVal)}function l(){return $([''].join("")).tooltip({placement:"bottom"}).click(function(r){j.call(this,r)})}function o(){return $([''].join("")).hide().tooltip({placement:"bottom"})}function i(){d.find(".search-loading").toggle();d.find(".search-clear").toggle()}if(jQuery.type(q)==="string"){if(q==="toggle-loading"){i()}return d}if(jQuery.type(q)==="object"){q=jQuery.extend(true,{},h,q)}return d.addClass("search-input").prepend([m(),l(),o()])}jQuery.fn.extend({searchInput:function c(d){return this.each(function(){return a(this,d)})}})}());(function(){function c(o,n){this.currModeIndex=0;return this._init(o,n)}c.prototype.DATA_KEY="mode-button";c.prototype.defaults={switchModesOnClick:true};c.prototype._init=function j(o,n){n=n||{};this.$element=$(o);this.options=jQuery.extend(true,{},this.defaults,n);if(!n.modes){throw new Error('ModeButton requires a "modes" array')}var q=this;this.$element.click(function p(r){q.callModeFn();if(q.options.switchModesOnClick){q._incModeIndex()}$(this).html(q.options.modes[q.currModeIndex].html)});return this.reset()};c.prototype._incModeIndex=function l(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};c.prototype._getModeIndex=function f(n){for(var o=0;o','","
    "].join("\n"));if(b&&b.length>1){a.find("button").addClass("dropdown-toggle").attr("data-toggle","dropdown").append(' ');a.append(['"].join("\n"))}function d(g){var h=$(this),f=h.parents(".dropdown-select"),e=h.text();f.find(".dropdown-select-selected").text(e);f.trigger("change.dropdown-select",e)}a.find("a").click(d);return a}(function(){function e(k,j){return this.init(k,j)}e.prototype.DATA_KEY="filter-control";e.prototype.init=function g(k,j){j=j||{filters:[]};this.$element=$(k).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,j);this.currFilter=this.options.filters[0];return this.render()};e.prototype.render=function d(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};e.prototype._renderKeySelect=function a(){var j=this;var k=this.options.filters.map(function(l){return l.key});this.$keySelect=dropDownSelect(k,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(m,l){j.currFilter=_.findWhere(j.options.filters,{key:l});j.render()._triggerChange()});return this.$keySelect};e.prototype._renderOpSelect=function i(){var j=this,k=this.currFilter.ops;this.$opSelect=dropDownSelect(k,k[0]).addClass("filter-control-op").on("change.dropdown-select",function(m,l){j._triggerChange()});return this.$opSelect};e.prototype._renderValueInput=function c(){var j=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(l,k){j._triggerChange()})}else{this.$valueSelect=$("").addClass("form-control").on("change",function(k,l){j._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};e.prototype.val=function b(){var k=this.$element.find(".filter-control-key .dropdown-select-selected").text(),m=this.$element.find(".filter-control-op .dropdown-select-selected").text(),j=this.$element.find(".filter-control-value"),l=(j.hasClass("dropdown-select"))?(j.find(".dropdown-select-selected").text()):(j.val());return{key:k,op:m,value:l}};e.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function f(k){var j=jQuery.makeArray(arguments).slice(1);return this.map(function(){var n=$(this),m=n.data(e.prototype.DATA_KEY);if(jQuery.type(k)==="object"){m=new e(n,k);n.data(e.prototype.DATA_KEY,m)}if(m&&jQuery.type(k)==="string"){var l=m[k];if(jQuery.type(l)==="function"){return l.apply(m,j)}}return this})}})}());(function(){function i(o,n){this.numPages=null;this.currPage=0;return this.init(o,n)}i.prototype.DATA_KEY="pagination";i.prototype.defaults={startingPage:0,perPage:20,totalDataSize:null,currDataSize:null};i.prototype.init=function g(n,o){o=o||{};this.$element=n;this.options=jQuery.extend(true,{},this.defaults,o);this.currPage=this.options.startingPage;if(this.options.totalDataSize!==null){this.numPages=Math.ceil(this.options.totalDataSize/this.options.perPage);if(this.currPage>=this.numPages){this.currPage=this.numPages-1}}this.$element.data(i.prototype.DATA_KEY,this);this._render();return this};function m(n){return $(['
  • ',n,"
  • "].join(""))}i.prototype._render=function e(){if(this.options.totalDataSize===0){return this}if(this.numPages===1){return this}if(this.numPages>0){this._renderPages();this._scrollToActivePage()}else{this._renderPrevNext()}return this};i.prototype._renderPrevNext=function b(){var o=this,p=m("Prev"),n=m("Next"),q=$("
      ").addClass("pagination pagination-prev-next");if(this.currPage===0){p.addClass("disabled")}else{p.click(function(){o.prevPage()})}if((this.numPages&&this.currPage===(this.numPages-1))||(this.options.currDataSize&&this.options.currDataSize").addClass("pagination-scroll-container"),s=$("
        ").addClass("pagination pagination-page-list"),r=function(t){n.goToPage($(this).data("page"))};for(var o=0;o=this.numPages){n=this.numPages-1}if(n===this.currPage){return this}this.currPage=n;this.$element.trigger("pagination.page-change",this.currPage);this._render();return this};i.prototype.prevPage=function c(){return this.goToPage(this.currPage-1)};i.prototype.nextPage=function h(){return this.goToPage(this.currPage+1)};i.prototype.page=function f(){return this.currPage};i.create=function k(n,o){return new i(n,o)};jQuery.fn.extend({pagination:function d(o){var n=jQuery.makeArray(arguments).slice(1);if(jQuery.type(o)==="object"){return this.map(function(){i.create($(this),o);return this})}var q=$(this[0]),r=q.data(i.prototype.DATA_KEY);if(r){if(jQuery.type(o)==="string"){var p=r[o];if(jQuery.type(p)==="function"){return p.apply(r,n)}}else{return r}}return undefined}})}());(function(){var g={renameColumns:false,columnNames:[],commentChar:"#",hideCommentRows:false,includePrompts:true,topLeftContent:"Columns:"},s="peek-control.change",t="peek-control.rename",l="peek-control",v="control",f="control-prompt",c="selected",n="disabled",u="button",q="renamable-header",p="column-index",a="column-name";function i(w){if(w.disabled&&jQuery.type(w.disabled)!=="array"){throw new Error('"disabled" must be defined as an array of indeces: '+JSON.stringify(w))}if(w.multiselect&&w.selected&&jQuery.type(w.selected)!=="array"){throw new Error('Mulitselect rows need an array for "selected": '+JSON.stringify(w))}if(!w.label||!w.id){throw new Error("Peek controls need a label and id for each control row: "+JSON.stringify(w))}if(w.disabled&&w.disabled.indexOf(w.selected)!==-1){throw new Error("Selected column is in the list of disabled columns: "+JSON.stringify(w))}return w}function o(x,w){return $("
        ").addClass(u).text(x.label)}function k(x,w){var y=$("
    ").html(o(x,w)).attr("data-"+p,w);if(x.disabled&&x.disabled.indexOf(w)!==-1){y.addClass(n)}return y}function e(y,z,w){var x=y.children("."+u);if(y.hasClass(c)){x.html((z.selectedText!==undefined)?(z.selectedText):(z.label))}else{x.html((z.unselectedText!==undefined)?(z.unselectedText):(z.label))}}function h(z,x){var y=k(z,x);if(z.selected===x){y.addClass(c)}e(y,z,x);if(!y.hasClass(n)){y.click(function w(D){var E=$(this);if(!E.hasClass(c)){var A=E.parent().children("."+c).removeClass(c);A.each(function(){e($(this),z,x)});E.addClass(c);e(E,z,x);var C={},B=E.parent().attr("id"),F=E.data(p);C[B]=F;E.parents(".peek").trigger(s,C)}})}return y}function m(z,x){var y=k(z,x);if(z.selected&&z.selected.indexOf(x)!==-1){y.addClass(c)}e(y,z,x);if(!y.hasClass(n)){y.click(function w(D){var E=$(this);E.toggleClass(c);e(E,z,x);var C=E.parent().find("."+c).map(function(G,H){return $(H).data(p)});var B={},A=E.parent().attr("id"),F=jQuery.makeArray(C);B[A]=F;E.parents(".peek").trigger(s,B)})}return y}function j(y,z){var w=[];for(var x=0;x").attr("id",z.id).addClass(v);if(x){var w=$("").addClass(f).text(z.label+":");A.append(w)}A.append(j(y,z));return A}function b(E){E=jQuery.extend(true,{},g,E);var D=$(this).addClass(l),A=D.find("table"),z=A.find("th").size(),C=A.find("tr").size(),w=A.find("td[colspan]").map(function(H,F){var G=$(this);if(G.text()&&G.text().match(new RegExp("^"+E.commentChar))){return $(this).css("color","grey").parent().get(0)}return null});if(E.hideCommentRows){w.hide();C-=w.size()}if(E.includePrompts){var y=$("").addClass("top-left").text(E.topLeftContent).attr("rowspan",C);A.find("tr").first().prepend(y)}var B=A.find("th:not(.top-left)").each(function(G,I){var H=$(this),J=H.text().replace(/^\d+\.*/,""),F=E.columnNames[G]||J;H.attr("data-"+a,F).text((G+1)+((F)?("."+F):("")))});if(E.renameColumns){B.addClass(q).click(function x(){var G=$(this),F=G.index()+(E.includePrompts?0:1),I=G.data(a),H=prompt("New column name:",I);if(H!==null&&H!==I){G.text(F+(H?("."+H):"")).data(a,H).attr("data-",a,H);var J=jQuery.makeArray(G.parent().children("th:not(.top-left)").map(function(){return $(this).data(a)}));G.parents(".peek").trigger(t,J)}})}E.controls.forEach(function(G,F){i(G);var H=r(z,G,E.includePrompts);A.find("tbody").append(H)});return this}jQuery.fn.extend({peekControl:function d(w){return this.map(function(){return b.call(this,w)})}})}()); \ No newline at end of file +var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a=""}else{a=""}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("
    ")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['"].join("")},_templateOptions:function(a){if(!a.length){return"
  • (no options)
  • "}return _.map(a,function(d){if(d.divider){return'
  • '}else{if(d.header){return['
  • ',d.html,"
  • "].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?(''):("");return['
  • ",b,d.html,"
  • "].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a','',""].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['
    ','
    ','','loading...',"
    ","
    "].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";a.parent().find(".loading-indicator").remove();b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b}(function(){var b=window._l||function(d){return d};function a(k,q){var e=27,n=13,d=$(k),f=true,h={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(r){},minSearchLen:0,escWillClear:true,oninit:function(){}};function j(r){var s=$(this).parent().children("input");s.focus().val("").trigger("clear:searchInput");q.onclear()}function p(s,r){$(this).trigger("search:searchInput",r);if(typeof q.onfirstsearch==="function"&&f){f=false;q.onfirstsearch(r)}else{q.onsearch(r)}}function g(){return['"].join("")}function m(){return $(g()).focus(function(r){$(this).select()}).keyup(function(s){s.preventDefault();s.stopPropagation();if(!$(this).val()){$(this).blur()}if(s.which===e&&q.escWillClear){j.call(this,s)}else{var r=$(this).val();if((s.which===n)||(q.minSearchLen&&r.length>=q.minSearchLen)){p.call(this,s,r)}else{if(!r.length){j.call(this,s)}}}}).val(q.initialVal)}function l(){return $([''].join("")).tooltip({placement:"bottom"}).click(function(r){j.call(this,r)})}function o(){return $([''].join("")).hide().tooltip({placement:"bottom"})}function i(){d.find(".search-loading").toggle();d.find(".search-clear").toggle()}if(jQuery.type(q)==="string"){if(q==="toggle-loading"){i()}return d}if(jQuery.type(q)==="object"){q=jQuery.extend(true,{},h,q)}return d.addClass("search-input").prepend([m(),l(),o()])}jQuery.fn.extend({searchInput:function c(d){return this.each(function(){return a(this,d)})}})}());(function(){function c(o,n){this.currModeIndex=0;return this._init(o,n)}c.prototype.DATA_KEY="mode-button";c.prototype.defaults={switchModesOnClick:true};c.prototype._init=function j(o,n){n=n||{};this.$element=$(o);this.options=jQuery.extend(true,{},this.defaults,n);if(!n.modes){throw new Error('ModeButton requires a "modes" array')}var q=this;this.$element.click(function p(r){q.callModeFn();if(q.options.switchModesOnClick){q._incModeIndex()}$(this).html(q.options.modes[q.currModeIndex].html)});return this.reset()};c.prototype._incModeIndex=function l(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};c.prototype._getModeIndex=function f(n){for(var o=0;o','","
    "].join("\n"));if(b&&b.length>1){a.find("button").addClass("dropdown-toggle").attr("data-toggle","dropdown").append(' ');a.append(['"].join("\n"))}function d(g){var h=$(this),f=h.parents(".dropdown-select"),e=h.text();f.find(".dropdown-select-selected").text(e);f.trigger("change.dropdown-select",e)}a.find("a").click(d);return a}(function(){function e(k,j){return this.init(k,j)}e.prototype.DATA_KEY="filter-control";e.prototype.init=function g(k,j){j=j||{filters:[]};this.$element=$(k).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,j);this.currFilter=this.options.filters[0];return this.render()};e.prototype.render=function d(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};e.prototype._renderKeySelect=function a(){var j=this;var k=this.options.filters.map(function(l){return l.key});this.$keySelect=dropDownSelect(k,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(m,l){j.currFilter=_.findWhere(j.options.filters,{key:l});j.render()._triggerChange()});return this.$keySelect};e.prototype._renderOpSelect=function i(){var j=this,k=this.currFilter.ops;this.$opSelect=dropDownSelect(k,k[0]).addClass("filter-control-op").on("change.dropdown-select",function(m,l){j._triggerChange()});return this.$opSelect};e.prototype._renderValueInput=function c(){var j=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(l,k){j._triggerChange()})}else{this.$valueSelect=$("").addClass("form-control").on("change",function(k,l){j._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};e.prototype.val=function b(){var k=this.$element.find(".filter-control-key .dropdown-select-selected").text(),m=this.$element.find(".filter-control-op .dropdown-select-selected").text(),j=this.$element.find(".filter-control-value"),l=(j.hasClass("dropdown-select"))?(j.find(".dropdown-select-selected").text()):(j.val());return{key:k,op:m,value:l}};e.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function f(k){var j=jQuery.makeArray(arguments).slice(1);return this.map(function(){var n=$(this),m=n.data(e.prototype.DATA_KEY);if(jQuery.type(k)==="object"){m=new e(n,k);n.data(e.prototype.DATA_KEY,m)}if(m&&jQuery.type(k)==="string"){var l=m[k];if(jQuery.type(l)==="function"){return l.apply(m,j)}}return this})}})}());(function(){function i(o,n){this.numPages=null;this.currPage=0;return this.init(o,n)}i.prototype.DATA_KEY="pagination";i.prototype.defaults={startingPage:0,perPage:20,totalDataSize:null,currDataSize:null};i.prototype.init=function g(n,o){o=o||{};this.$element=n;this.options=jQuery.extend(true,{},this.defaults,o);this.currPage=this.options.startingPage;if(this.options.totalDataSize!==null){this.numPages=Math.ceil(this.options.totalDataSize/this.options.perPage);if(this.currPage>=this.numPages){this.currPage=this.numPages-1}}this.$element.data(i.prototype.DATA_KEY,this);this._render();return this};function m(n){return $(['
  • ',n,"
  • "].join(""))}i.prototype._render=function e(){if(this.options.totalDataSize===0){return this}if(this.numPages===1){return this}if(this.numPages>0){this._renderPages();this._scrollToActivePage()}else{this._renderPrevNext()}return this};i.prototype._renderPrevNext=function b(){var o=this,p=m("Prev"),n=m("Next"),q=$("
      ").addClass("pagination pagination-prev-next");if(this.currPage===0){p.addClass("disabled")}else{p.click(function(){o.prevPage()})}if((this.numPages&&this.currPage===(this.numPages-1))||(this.options.currDataSize&&this.options.currDataSize").addClass("pagination-scroll-container"),s=$("
        ").addClass("pagination pagination-page-list"),r=function(t){n.goToPage($(this).data("page"))};for(var o=0;o=this.numPages){n=this.numPages-1}if(n===this.currPage){return this}this.currPage=n;this.$element.trigger("pagination.page-change",this.currPage);this._render();return this};i.prototype.prevPage=function c(){return this.goToPage(this.currPage-1)};i.prototype.nextPage=function h(){return this.goToPage(this.currPage+1)};i.prototype.page=function f(){return this.currPage};i.create=function k(n,o){return new i(n,o)};jQuery.fn.extend({pagination:function d(o){var n=jQuery.makeArray(arguments).slice(1);if(jQuery.type(o)==="object"){return this.map(function(){i.create($(this),o);return this})}var q=$(this[0]),r=q.data(i.prototype.DATA_KEY);if(r){if(jQuery.type(o)==="string"){var p=r[o];if(jQuery.type(p)==="function"){return p.apply(r,n)}}else{return r}}return undefined}})}());(function(){var g={renameColumns:false,columnNames:[],commentChar:"#",hideCommentRows:false,includePrompts:true,topLeftContent:"Columns:"},s="peek-control.change",t="peek-control.rename",l="peek-control",v="control",f="control-prompt",c="selected",n="disabled",u="button",q="renamable-header",p="column-index",a="column-name";function i(w){if(w.disabled&&jQuery.type(w.disabled)!=="array"){throw new Error('"disabled" must be defined as an array of indeces: '+JSON.stringify(w))}if(w.multiselect&&w.selected&&jQuery.type(w.selected)!=="array"){throw new Error('Mulitselect rows need an array for "selected": '+JSON.stringify(w))}if(!w.label||!w.id){throw new Error("Peek controls need a label and id for each control row: "+JSON.stringify(w))}if(w.disabled&&w.disabled.indexOf(w.selected)!==-1){throw new Error("Selected column is in the list of disabled columns: "+JSON.stringify(w))}return w}function o(x,w){return $("
        ").addClass(u).text(x.label)}function k(x,w){var y=$("
    ").html(o(x,w)).attr("data-"+p,w);if(x.disabled&&x.disabled.indexOf(w)!==-1){y.addClass(n)}return y}function e(y,z,w){var x=y.children("."+u);if(y.hasClass(c)){x.html((z.selectedText!==undefined)?(z.selectedText):(z.label))}else{x.html((z.unselectedText!==undefined)?(z.unselectedText):(z.label))}}function h(z,x){var y=k(z,x);if(z.selected===x){y.addClass(c)}e(y,z,x);if(!y.hasClass(n)){y.click(function w(D){var E=$(this);if(!E.hasClass(c)){var A=E.parent().children("."+c).removeClass(c);A.each(function(){e($(this),z,x)});E.addClass(c);e(E,z,x);var C={},B=E.parent().attr("id"),F=E.data(p);C[B]=F;E.parents(".peek").trigger(s,C)}})}return y}function m(z,x){var y=k(z,x);if(z.selected&&z.selected.indexOf(x)!==-1){y.addClass(c)}e(y,z,x);if(!y.hasClass(n)){y.click(function w(D){var E=$(this);E.toggleClass(c);e(E,z,x);var C=E.parent().find("."+c).map(function(G,H){return $(H).data(p)});var B={},A=E.parent().attr("id"),F=jQuery.makeArray(C);B[A]=F;E.parents(".peek").trigger(s,B)})}return y}function j(y,z){var w=[];for(var x=0;x").attr("id",z.id).addClass(v);if(x){var w=$("").addClass(f).text(z.label+":");A.append(w)}A.append(j(y,z));return A}function b(E){E=jQuery.extend(true,{},g,E);var D=$(this).addClass(l),A=D.find("table"),z=A.find("th").size(),C=A.find("tr").size(),w=A.find("td[colspan]").map(function(H,F){var G=$(this);if(G.text()&&G.text().match(new RegExp("^"+E.commentChar))){return $(this).css("color","grey").parent().get(0)}return null});if(E.hideCommentRows){w.hide();C-=w.size()}if(E.includePrompts){var y=$("").addClass("top-left").text(E.topLeftContent).attr("rowspan",C);A.find("tr").first().prepend(y)}var B=A.find("th:not(.top-left)").each(function(G,I){var H=$(this),J=H.text().replace(/^\d+\.*/,""),F=E.columnNames[G]||J;H.attr("data-"+a,F).text((G+1)+((F)?("."+F):("")))});if(E.renameColumns){B.addClass(q).click(function x(){var G=$(this),F=G.index()+(E.includePrompts?0:1),I=G.data(a),H=prompt("New column name:",I);if(H!==null&&H!==I){G.text(F+(H?("."+H):"")).data(a,H).attr("data-",a,H);var J=jQuery.makeArray(G.parent().children("th:not(.top-left)").map(function(){return $(this).data(a)}));G.parents(".peek").trigger(t,J)}})}E.controls.forEach(function(G,F){i(G);var H=r(z,G,E.includePrompts);A.find("tbody").append(H)});return this}jQuery.fn.extend({peekControl:function d(w){return this.map(function(){return b.call(this,w)})}})}()); \ No newline at end of file diff --git a/static/scripts/packed/mvc/ui/ui-tabs.js b/static/scripts/packed/mvc/ui/ui-tabs.js index 8b1f8b56520..053b78855ca 100644 --- a/static/scripts/packed/mvc/ui/ui-tabs.js +++ b/static/scripts/packed/mvc/ui/ui-tabs.js @@ -1 +1 @@ -define(["utils/utils"],function(a){var b=Backbone.View.extend({optionsDefault:{title_new:"",operations:null,onnew:null,min:null,max:null,onchange:null},initialize:function(e){this.visible=false;this.$nav=null;this.$content=null;this.first_tab=null;this.options=a.merge(e,this.optionsDefault);var c=$(this._template(this.options));this.$nav=c.find(".tab-navigation");this.$content=c.find(".tab-content");this.setElement(c);this.list={};var d=this;if(this.options.operations){$.each(this.options.operations,function(g,h){h.$el.prop("id",g);d.$nav.find(".operations").append(h.$el)})}if(this.options.onnew){var f=$(this._template_tab_new(this.options));this.$nav.append(f);f.tooltip({title:"Add a new tab",placement:"bottom",container:d.$el});f.on("click",function(g){f.tooltip("hide");d.options.onnew()})}},size:function(){return _.size(this.list)},current:function(){return this.$el.find(".tab-pane.active").attr("id")},add:function(f){var e=this;var h=f.id;var g=$(this._template_tab(f));var d=$(this._template_tab_content(f));this.list[h]=f.ondel?true:false;if(this.options.onnew){this.$nav.find("#new-tab").before(g)}else{this.$nav.append(g)}d.append(f.$el);this.$content.append(d);if(this.size()==1){g.addClass("active");d.addClass("active");this.first_tab=h}if(this.options.max&&this.size()>=this.options.max){this.$el.find("#new-tab").hide()}if(f.ondel){var c=g.find("#delete");c.tooltip({title:"Delete this tab",placement:"bottom",container:e.$el});c.on("click",function(){c.tooltip("destroy");e.$el.find(".tooltip").remove();f.ondel();return false})}g.on("click",function(i){i.preventDefault();if(f.onclick){f.onclick()}else{e.show(h)}})},del:function(c){this.$el.find("#tab-"+c).remove();this.$el.find("#"+c).remove();if(this.first_tab==c){this.first_tab=null}if(this.first_tab!=null){this.show(this.first_tab)}if(this.list[c]){delete this.list[c]}if(this.size()
    '},_template_tab_new:function(c){return'
  • '+c.title_new+"
  • "},_template_tab:function(d){var c='
  • '+d.title+"";if(d.ondel){c+=''}c+="
  • ";return c},_template_tab_content:function(c){return'
    '}});return{View:b}}); \ No newline at end of file +define(["utils/utils"],function(a){var b=Backbone.View.extend({optionsDefault:{title_new:"",operations:null,onnew:null,min:null,max:null,onchange:null},initialize:function(e){this.visible=false;this.$nav=null;this.$content=null;this.first_tab=null;this.current_id=null;this.options=a.merge(e,this.optionsDefault);var c=$(this._template(this.options));this.$nav=c.find(".tab-navigation");this.$content=c.find(".tab-content");this.setElement(c);this.list={};var d=this;if(this.options.operations){$.each(this.options.operations,function(g,h){h.$el.prop("id",g);d.$nav.find(".operations").append(h.$el)})}if(this.options.onnew){var f=$(this._template_tab_new(this.options));this.$nav.append(f);f.tooltip({title:"Add a new tab",placement:"bottom",container:d.$el});f.on("click",function(g){f.tooltip("hide");d.options.onnew()})}},size:function(){return _.size(this.list)},current:function(){return this.$el.find(".tab-pane.active").attr("id")},add:function(f){var e=this;var h=f.id;var g=$(this._template_tab(f));var d=$(this._template_tab_content(f));this.list[h]=f.ondel?true:false;if(this.options.onnew){this.$nav.find("#new-tab").before(g)}else{this.$nav.append(g)}d.append(f.$el);this.$content.append(d);if(this.size()==1){g.addClass("active");d.addClass("active");this.first_tab=h}if(this.options.max&&this.size()>=this.options.max){this.$el.find("#new-tab").hide()}if(f.ondel){var c=g.find("#delete");c.tooltip({title:"Delete this tab",placement:"bottom",container:e.$el});c.on("click",function(){c.tooltip("destroy");e.$el.find(".tooltip").remove();f.ondel();return false})}g.on("click",function(i){i.preventDefault();if(f.onclick){f.onclick()}else{e.show(h)}});if(!this.current_id){this.current_id=h}},del:function(c){this.$el.find("#tab-"+c).remove();this.$el.find("#"+c).remove();if(this.first_tab==c){this.first_tab=null}if(this.first_tab!=null){this.show(this.first_tab)}if(this.list[c]){delete this.list[c]}if(this.size()
    '},_template_tab_new:function(c){return'
  • '+c.title_new+"
  • "},_template_tab:function(d){var c='
  • '+d.title+"";if(d.ondel){c+=''}c+="
  • ";return c},_template_tab_content:function(c){return'
    '}});return{View:b}}); \ No newline at end of file diff --git a/static/scripts/utils/add-logging.js b/static/scripts/utils/add-logging.js index 9ac4954402c..bc1d43b5de9 100644 --- a/static/scripts/utils/add-logging.js +++ b/static/scripts/utils/add-logging.js @@ -14,6 +14,9 @@ function addLogging( obj, namespace ){ return this.logger.emit( logFn, this._logNamespace, arguments ); } if( this.logger[ logFn ] ){ +//TODO:! there has to be a way to get the lineno/file into this +// http://stackoverflow.com/questions/13815640/a-proper-wrapper-for-console-log-with-correct-line-number +// http://www.paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ return this.logger[ logFn ].apply( this.logger, arguments ); } return undefined; diff --git a/static/style/blue/base.css b/static/style/blue/base.css index ae5786b13e5..46afa3b18d4 100644 --- a/static/style/blue/base.css +++ b/static/style/blue/base.css @@ -1656,15 +1656,20 @@ div.permissionContainer{padding-left:20px} .list-item .details label{margin:0px;padding:0px;font-weight:normal} .list-item .details .prompt{font-weight:normal;color:#555} .list-item .details .prompt:after{content:':';margin-right:4px} +.list-panel .vertical-spacing{margin-bottom:8px} .list-panel .controls>*:not(:empty){margin-bottom:8px} .list-panel .controls .name{word-wrap:break-word;font-weight:bold} -.list-panel .controls .subtitle{color:grey}.list-panel .controls .subtitle:not(:empty){margin-top:-8px} +.list-panel .controls .subtitle{color:#777;font-size:90%} .list-panel .controls .actions{display:inline-block;float:right}.list-panel .controls .actions .icon-btn:not(:last-child){margin:0px;border-radius:0px;border-right:none} .list-panel .controls .actions .icon-btn:first-child{margin-right:0px;border-top-left-radius:3px;border-bottom-left-radius:3px} .list-panel .controls .actions .icon-btn:last-child{margin-left:0px;border-radius:0px 3px 3px 0px} .list-panel .controls .actions .icon-btn:only-child{margin:0px;border-radius:3px} .list-panel .controls .actions .icon-btn{margin-left:2px} -.list-panel .controls .messages [class$=message]{margin:0px}.list-panel .controls .messages [class$=message]:not(:last-child){margin-bottom:8px} +.list-panel .controls .messages:before,.list-panel .controls .messages:after{content:" ";display:table;} +.list-panel .controls .messages:after{clear:both} +.list-panel .controls .messages:before,.list-panel .controls .messages:after{content:" ";display:table;} +.list-panel .controls .messages:after{clear:both} +.list-panel .controls .messages [class$=message],.list-panel .controls .messages [class$=messagesmall]{margin:0px}.list-panel .controls .messages [class$=message]:not(:last-child),.list-panel .controls .messages [class$=messagesmall]:not(:last-child){margin-bottom:8px} .list-panel .controls .list-actions{display:none}.list-panel .controls .list-actions:before,.list-panel .controls .list-actions:after{content:" ";display:table;} .list-panel .controls .list-actions:after{clear:both} .list-panel .controls .list-actions:before,.list-panel .controls .list-actions:after{content:" ";display:table;} @@ -1719,58 +1724,44 @@ div.permissionContainer{padding-left:20px} .dataset .details .actions .right .icon-btn{margin-left:2px} .dataset .details .actions:not(:last-child){margin-bottom:8px} .dataset .details .tags-display{display:none;margin-bottom:8px}.dataset .details .tags-display .select2-container{min-width:0px}.dataset .details .tags-display .select2-container .select2-choices{border-radius:3px} -.dataset .details .annotation-display{display:none;margin-bottom:8px}.dataset .details .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.30000000000000004);padding:4px;white-space:pre-line;overflow:auto} +.dataset .details .annotation-display{display:none;margin-bottom:8px}.dataset .details .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.30000000000000004);padding:4px;white-space:pre-wrap;overflow:auto} .dataset .details .annotation-display .annotation:empty:after{position:relative;top:-4px;font-size:10px;font-style:italic;color:grey;content:'Click here to edit annotation'} .dataset .details .annotation-display textarea{margin:0px 0px 2px 0px;display:block;border-radius:3px;width:100%} .dataset .details .display-applications .display-application:last-child{margin-bottom:8px} .dataset .details .dataset-peek{margin-bottom:8px;width:100%;margin:0px;border-radius:3px;background:white;color:black;font-size:10px;overflow:auto}.dataset .details .dataset-peek th{color:white;background:#5f6990} .dataset .details .dataset-peek table,.dataset .details .dataset-peek th,.dataset .details .dataset-peek tr,.dataset .details .dataset-peek td{font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:10px} .history-content.dataset .title-bar .title .hid:after,.history-content.dataset-collection .title-bar .title .hid:after{content:':'} -.history-panel .history-controls{margin:10px;padding:0px}.history-panel .history-controls [class$="messagesmall"]{margin:0px} -.history-panel .history-controls .message-container{cursor:pointer} -.history-panel .history-controls .message-container [class$=message]{margin:0px;margin-bottom:8px} -.history-panel .history-controls .history-search-controls{display:none;padding:0px 0px 8px 0px} -.history-panel .history-controls .history-title{margin-bottom:8px} -.history-panel .history-controls .history-name{word-wrap:break-word;font-weight:bold} -.history-panel .history-controls .history-title input{width:100%;margin:-2px 0 -2px -4px;font-weight:bold} -.history-panel .history-controls .editable-text{border:solid transparent 1px} -.history-panel .history-controls .editable-text:hover{cursor:pointer;border:1px dotted #999999} -.history-panel .history-controls .history-subtitle{margin-bottom:8px} -.history-panel .history-controls .history-size{float:left} -.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:':'} -.history-panel .history-controls .tags-display .select2-container{min-width:0px}.history-panel .history-controls .tags-display .select2-container .select2-choices{border-radius:3px} -.history-panel .history-controls .tags-display input{border-radius:3px} -.history-panel .history-controls .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.5);padding:4px;white-space:pre-line;overflow:auto} -.history-panel .history-controls .annotation-display .annotation:empty:after{position:relative;top:-4px;font-size:10px;font-style:italic;color:grey;content:'Click here to edit annotation'} -.history-panel .history-controls .annotation-display textarea{margin:0px 0px 2px 0px;display:block;border-radius:3px;width:100%} -.history-panel .history-controls .history-dataset-actions{display:none}.history-panel .history-controls .history-dataset-actions:before,.history-panel .history-controls .history-dataset-actions:after{content:" ";display:table;} -.history-panel .history-controls .history-dataset-actions:after{clear:both} -.history-panel .history-controls .history-dataset-actions:before,.history-panel .history-controls .history-dataset-actions:after{content:" ";display:table;} -.history-panel .history-controls .history-dataset-actions:after{clear:both} -.history-panel .history-controls .history-dataset-actions .btn{padding-top:2px;padding-bottom:2px;font-size:90%} -.history-panel .history-controls .history-dataset-actions .history-dataset-action-popup-btn{float:right} +.history-panel .controls{margin:10px;padding:0px}.history-panel .controls .messages>*{cursor:pointer} +.history-panel .controls .title{margin-bottom:4px}.history-panel .controls .title input{width:100%;margin:-3px 0 -3px -3px;font-weight:bold} +.history-panel .controls .subtitle:not(:empty){margin-top:-4px;margin-bottom:4px} +.history-panel .controls .editable-text{border:solid transparent 1px} +.history-panel .controls .editable-text:hover{cursor:pointer;border:1px dotted #999999} +.history-panel .controls .history-size{display:inline-block;color:black} +.history-panel .controls .actions{margin-bottom:0px} +.history-panel .controls .quota-message{display:none;margin:8px 0px 5px 0px} +.history-panel .controls .tags-display,.history-panel .controls .annotation-display{display:none;margin-bottom:8px}.history-panel .controls .tags-display label,.history-panel .controls .annotation-display label{display:block;margin:0px;padding:0px;font-weight:normal;color:#555} +.history-panel .controls .tags-display label:after,.history-panel .controls .annotation-display label:after{content:':'} +.history-panel .controls .tags-display .select2-container{min-width:0px}.history-panel .controls .tags-display .select2-container .select2-choices{border-radius:3px} +.history-panel .controls .tags-display input{border-radius:3px} +.history-panel .controls .annotation-display .annotation{border-radius:3px;border:1px solid rgba(153,153,153,0.5);padding:4px;white-space:pre-wrap;overflow:auto} +.history-panel .controls .annotation-display .annotation:empty:after{position:relative;top:-4px;font-size:10px;font-style:italic;color:grey;content:'Click here to edit annotation'} +.history-panel .controls .annotation-display textarea{margin:0px 0px 2px 0px;display:block;border-radius:3px;width:100%} +.history-panel .controls .list-actions .list-action-popup-btn{float:right} .history-panel .list-item.history-content{border-width:1px 0px 0px 0px}.history-panel .list-item.history-content:last-child{border-width:1px 0px 1px 0px} -.history-panel .empty-history-message{display:none;margin:0px 10px 0px 10px} -.annotated-history-panel [class$=messagesmall]{margin:8px 0px 8px 0px} -.annotated-history-panel .history-controls{margin:0px 0px 16px 4px;padding:0px}.annotated-history-panel .history-controls .history-name{font-size:150%;margin-bottom:4px} -.annotated-history-panel .history-controls .history-subtitle{margin:0px;padding:0px 0px 0px 4px;padding-left:4px} -.annotated-history-panel .history-controls .history-annotation{border:1px solid lightgrey;border-width:1px 0px 0px 0px;padding:4px 0px 4px 0px;margin:4px;white-space:pre-wrap} -.annotated-history-panel .history-controls .history-annotation:empty{display:none} -.annotated-history-panel .history-controls .history-search-controls{padding:16px 0px 0px 0px} -.annotated-history-panel table.datasets-list{width:100%;table-layout:fixed}.annotated-history-panel table.datasets-list .headers{border-bottom:2px solid grey} -.annotated-history-panel table.datasets-list .headers th{padding:8px} -.annotated-history-panel table.datasets-list .dataset-row{cursor:pointer;border-bottom:1px solid grey} -.annotated-history-panel table.datasets-list .dataset-row>td{margin:0px;padding:0px;vertical-align:top} -.annotated-history-panel table.datasets-list .dataset-row>td:nth-child(1){width:50%} -.annotated-history-panel table.datasets-list .dataset-row>td:nth-child(2){padding:8px 16px 8px 16px;white-space:pre-wrap} -.annotated-history-panel table.datasets-list .dataset-row .dataset{border:0px} +.history-panel .empty-message{margin:0px 10px 0px 10px} +.annotated-history-panel .controls{margin:0px}.annotated-history-panel .controls .name{font-size:150%;margin-bottom:4px} +.annotated-history-panel .controls .subtitle{color:black}.annotated-history-panel .controls .subtitle:not(:empty){margin:0px 0px 0px 4px} +.annotated-history-panel .controls .messages:not(:empty){margin-top:16px} +.annotated-history-panel .controls .annotation-display{display:block;border:1px solid lightgrey;border-width:1px 0px 0px 0px;padding:4px 0px 4px 0px;margin:4px 4px 0px 4px;white-space:pre-wrap}.annotated-history-panel .controls .annotation-display:empty{display:none} +.annotated-history-panel .controls .search{padding:16px 0px 0px 0px;margin-bottom:0px} +.annotated-history-panel table.list-items{width:100%;table-layout:fixed}.annotated-history-panel table.list-items .headers{border-bottom:2px solid grey} +.annotated-history-panel table.list-items .headers th{padding:8px} +.annotated-history-panel table.list-items>tbody>tr{cursor:pointer;border-bottom:1px solid grey}.annotated-history-panel table.list-items>tbody>tr>td{margin:0px;padding:0px;vertical-align:top}.annotated-history-panel table.list-items>tbody>tr>td:nth-child(1){width:50%} +.annotated-history-panel table.list-items>tbody>tr>td:nth-child(2){padding:8px 16px 8px 16px;white-space:pre-wrap} +.annotated-history-panel table.list-items>tbody>tr .list-item{border:0px} +.annotated-history-panel .empty-message{margin-top:8px} +.history-panel .dataset-collection .subtitle{margin-top:2px;font-size:90%;color:#777} +.history-panel .dataset-collection-panel .controls{padding:0px} body.historyPage{background:#dfe5f9;color:#000;margin:5px;border:0;padding:0} div.historyLinks{margin:5px 5px} div.historyItem{margin:0 -5px;padding:8px 10px;border-top:solid #bfbfbf 1px;border-right:none;word-wrap:break-word;background:#eee}div.historyItem .state-icon{display:inline-block;vertical-align:middle;width:16px;height:16px;background-position:0 1px;background-repeat:no-repeat} @@ -1824,8 +1815,7 @@ pre.peek table,pre.peek th,pre.peek tr,pre.peek td{font-family:Monaco,Menlo,Cons .dataset-collection-panel .controls{padding:8px}.dataset-collection-panel .controls .navigation{margin-top:4px}.dataset-collection-panel .controls .navigation .back .fa-icon{margin-right:4px;font-size:120%;font-weight:bold;text-decoration:none} .dataset-collection-panel .controls .title{margin-top:12px}.dataset-collection-panel .controls .title .name{font-weight:bold} .dataset-collection-panel .controls .subtitle{color:#777} -.dataset-collection-panel .datasets-list{margin-top:8px}.dataset-collection-panel .datasets-list .dataset-collection-element{border-width:1px 0px 0px 0px}.dataset-collection-panel .datasets-list .dataset-collection-element:last-child{border-bottom-width:1px} -.history-panel .dataset-collection .subtitle{margin-top:2px;font-size:90%;color:#777} +.dataset-collection-panel .list-items{margin-top:8px}.dataset-collection-panel .list-items .dataset-collection-element{border-width:1px 0px 0px 0px}.dataset-collection-panel .list-items .dataset-collection-element:last-child{border-bottom-width:1px} .collection-creator{height:100%;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}.collection-creator ol,.collection-creator li{list-style:none;padding:0;margin:0} .collection-creator>*:not(.popover){padding:0px 8px 0px 8px} .collection-creator .btn{border-color:#BFBFBF} diff --git a/static/style/blue/embed_item.css b/static/style/blue/embed_item.css index ee520706e00..a7fc7814a21 100644 --- a/static/style/blue/embed_item.css +++ b/static/style/blue/embed_item.css @@ -11,7 +11,7 @@ .embedded-item .expanded-content{display:none;background-color:white;padding:5px} .embedded-item .item-content{max-height:25em;overflow:auto} .embedded-item.visualization .item-content{overflow:hidden} -.embedded-item .title{vertical-align:top;text-align:center;font-weight:bold;padding-bottom:5px} +.embedded-item>.title{vertical-align:top;text-align:center;font-weight:bold;padding-bottom:5px} .embedded-item.placeholder .content{padding:0.5em 0.5em;font-style:italic;text-align:center} table.annotated-item{width:100%;border-collapse:collapse} table.annotated-item td,th{padding:0} diff --git a/static/style/src/less/collections.less b/static/style/src/less/collections.less index e0f2e18de1a..9bb63b75462 100644 --- a/static/style/src/less/collections.less +++ b/static/style/src/less/collections.less @@ -58,7 +58,7 @@ color: #777; } } - .datasets-list { + .list-items { margin-top: 8px; .dataset-collection-element { border-width: 1px 0px 0px 0px; @@ -68,16 +68,3 @@ } } } - -//============================================================================= collection inside history panel -.history-panel .dataset-collection { - .subtitle { - margin-top: 2px; - font-size: 90%; - color: #777; - } -} - -//============================================================================= collection panel inside history panel -//.history-panel .dataset-collection-panel { -//} diff --git a/static/style/src/less/embed_item.less b/static/style/src/less/embed_item.less index 4c081551405..5c263171ce0 100644 --- a/static/style/src/less/embed_item.less +++ b/static/style/src/less/embed_item.less @@ -66,7 +66,7 @@ overflow: hidden; } -.embedded-item .title { +.embedded-item > .title { vertical-align: top; text-align: center; font-weight: bold; @@ -96,4 +96,4 @@ table.annotated-item .annotation { table.annotated-item td.annotation { vertical-align: text-top; padding-top: 1em; -} \ No newline at end of file +} diff --git a/static/style/src/less/history.less b/static/style/src/less/history.less index a41b021e955..811621c9ebc 100644 --- a/static/style/src/less/history.less +++ b/static/style/src/less/history.less @@ -305,7 +305,8 @@ .list-item .vertical-spacing; .annotation { .list-item .info-section; - white-space: pre-line; + //white-space: pre-line; + white-space: pre-wrap; overflow: auto; } // fake placeholder for editable text annotation @@ -371,38 +372,26 @@ // ---------------------------------------------------------------------------- history .history-panel { - .history-controls { + .controls { margin: 10px; padding: 0px; - [class$="messagesmall"] { - margin: 0px; - } - .message-container { + .messages > * { cursor: pointer; } - .message-container [class$=message] { - margin: 0px; - margin-bottom: 8px; - } - .history-search-controls { - display: none; - padding: 0px 0px 8px 0px; + .title { + margin-bottom: 4px; + input { + width: 100%; + // keep the text in position + margin: -3px 0px -3px -3px; + font-weight: bold; + } } - - .history-title { - margin-bottom: 8px; - } - .history-name { - word-wrap: break-word; - font-weight: bold; - } - .history-title input { - width: 100%; - // keep the text in position - margin: -2px 0px -2px -4px; - font-weight: bold; + .subtitle:not(:empty){ + margin-top: -4px; + margin-bottom: 4px; } .editable-text { border: solid transparent 1px; @@ -412,18 +401,12 @@ border: 1px dotted #999999; } - .history-subtitle { - margin-bottom: 8px; - } .history-size { - float: left; + display: inline-block; + color: black; } - .history-secondary-actions { - .icon-btn-group(); - float: right; - .icon-btn { - margin-left: @icon-btn-margin; - } + .actions { + margin-bottom: 0px; } .quota-message { @@ -462,7 +445,8 @@ border-radius: 3px; border: 1px solid fadeout( @layout-border-color, 50% ); padding: 4px; - white-space: pre-line; + //white-space: pre-line; + white-space: pre-wrap; overflow: auto; } // fake placeholder for editable text annotation @@ -483,15 +467,8 @@ } } - .history-dataset-actions { - display: none; - .clear; - .btn { - padding-top: 2px; - padding-bottom: 2px; - font-size: 90%; - } - .history-dataset-action-popup-btn { + .list-actions { + .list-action-popup-btn { float: right; } } @@ -506,52 +483,50 @@ } } - .empty-history-message { - display: none; + .empty-message { margin: 0px 10px 0px 10px; } } // ---------------------------------------------------------------------------- annotated-history-panel .annotated-history-panel { - //TODO: most of these styles are specific to history/display.mako and not to this type of panel in general - //TODO: some of these styles are also overrides of history-panel - remove those from hpanel and revisit this - - [class$=messagesmall] { - margin: 8px 0px 8px 0px; - } - - .history-controls { - margin: 0px 0px 16px 4px; - padding: 0px; - - .history-name { + .controls { + margin: 0px; + .name { font-size: 150%; margin-bottom: 4px; } - .history-subtitle { - margin: 0px; - padding: 0px 0px 0px 4px; - padding-left: 4px; + .subtitle { + color: black; + &:not(:empty) { + margin: 0px 0px 0px 4px; + } } - - .history-annotation { + .messages { + &:not(:empty) { + margin-top: 16px; + } + } + // annotations are displayed by default in this view + .annotation-display { + display: block; border: 1px solid lightgrey; border-width: 1px 0px 0px 0px; padding: 4px 0px 4px 0px; - margin: 4px; + margin: 4px 4px 0px 4px; white-space: pre-wrap; - } - .history-annotation:empty { - display: none; + &:empty { + display: none; + } } - .history-search-controls { + .search { padding: 16px 0px 0px 0px; + margin-bottom: 0px; } } - table.datasets-list { + table.list-items { width: 100%; table-layout: fixed; @@ -561,25 +536,48 @@ .headers th { padding: 8px; } - .dataset-row { + & > tbody > tr { + // tr is clickable for expansion cursor: pointer; border-bottom: 1px solid grey; + & > td { + margin: 0px; + padding: 0px; + vertical-align: top; + &:nth-child(1) { + width: 50%; + } + &:nth-child(2) { + /* top and bottom should be == title-bar padding */ + padding: 8px 16px 8px 16px; + // do not html format + white-space: pre-wrap; + } + } + // tr has border - remove from items + .list-item { + border: 0px; + } } - .dataset-row > td { - margin: 0px; + } + .empty-message { + margin-top: 8px; + } +} + + +// ---------------------------------------------------------------------------- collections nested in histories +.history-panel { + .dataset-collection { + .subtitle { + margin-top: 2px; + font-size: 90%; + color: #777; + } + } + .dataset-collection-panel { + .controls { padding: 0px; - vertical-align: top; - } - .dataset-row > td:nth-child(1) { - width: 50%; - } - .dataset-row > td:nth-child(2) { - /* top and bottom should be <= title-bar padding */ - padding: 8px 16px 8px 16px; - white-space: pre-wrap; - } - .dataset-row .dataset { - border: 0px; } } } diff --git a/static/style/src/less/list-item.less b/static/style/src/less/list-item.less index ea85dc7f5c7..ea15ae4bb99 100644 --- a/static/style/src/less/list-item.less +++ b/static/style/src/less/list-item.less @@ -101,24 +101,25 @@ // ---------------------------------------------------------------------------- generic panel list (of list-items) .list-panel { + //TODO: duplicated + @vertical-gap: 8px; + .vertical-spacing { + margin-bottom: @vertical-gap; + } + .controls { & > *:not(:empty) { + //background: rgba( 0, 255, 0, 0.2 ); margin-bottom: 8px; } - .title { - } .name { word-wrap: break-word; font-weight: bold; } - .subtitle { - color: grey; - &:not(:empty) { - margin-top: -8px; - } + color: #777; + font-size: 90%; } - .actions { .icon-btn-group(); float: right; @@ -126,19 +127,16 @@ margin-left: @icon-btn-margin; } } - .messages { - [class$=message] { + .clearfix(); + [class$=message], + [class$=messagesmall] { margin: 0px; &:not(:last-child) { margin-bottom: 8px; } } } - - .search { - } - .list-actions { display: none; .clear; @@ -149,8 +147,7 @@ } } } - - // display only a top border on all + // display only a top border on all but the last .list-items { .list-item { &:not(:last-child) { @@ -158,7 +155,6 @@ } } } - .empty-message { display: none; margin: 0px; diff --git a/templates/webapps/galaxy/history/display.mako b/templates/webapps/galaxy/history/display.mako index ff8e77d29f8..a3771ddce90 100644 --- a/templates/webapps/galaxy/history/display.mako +++ b/templates/webapps/galaxy/history/display.mako @@ -45,11 +45,8 @@
    diff --git a/templates/webapps/galaxy/history/embed.mako b/templates/webapps/galaxy/history/embed.mako index 9b6cfface7d..9b3b362819d 100644 --- a/templates/webapps/galaxy/history/embed.mako +++ b/templates/webapps/galaxy/history/embed.mako @@ -62,14 +62,12 @@ } $(function(){ - var debugging = JSON.parse( sessionStorage.getItem( 'debugging' ) ) || false, - historyModel = require( 'mvc/history/history-model' ), + var historyModel = require( 'mvc/history/history-model' ), panel = new panelMod.AnnotatedHistoryPanel({ el : $embeddedHistory.find( ".history-panel" ), model : new historyModel.History( ${h.dumps( history_dict )}, - ${h.dumps( hda_dicts )}, - { logger: ( debugging )?( console ):( null ) } + ${h.dumps( hda_dicts )} ) }).render(); diff --git a/templates/webapps/galaxy/history/view.mako b/templates/webapps/galaxy/history/view.mako index 9e92adbaa71..21523083764 100644 --- a/templates/webapps/galaxy/history/view.mako +++ b/templates/webapps/galaxy/history/view.mako @@ -168,8 +168,8 @@ ${parent.javascripts()} hdaJSON = ${h.dumps( hdas )}; panelToUse = ( userIsOwner )? //TODO: change class names - ({ location: 'mvc/history/history-panel-edit', className: 'HistoryPanel' }): - ({ location: 'mvc/history/history-panel', className: 'ReadOnlyHistoryPanel' }); + ({ location: 'mvc/history/history-panel-edit', className: 'HistoryPanelEdit' }): + ({ location: 'mvc/history/history-panel', className: 'HistoryPanel' }); require.config({ baseUrl : "${h.url_for( '/static/scripts' )}" @@ -187,17 +187,14 @@ ${parent.javascripts()} show_hidden : ${show_hidden_json}, purgeAllowed : Galaxy.config.allow_user_dataset_purge, el : $( "#history-" + historyJSON.id ), - model : history, - onready : function(){ - var panel = this; - $( '#toggle-deleted' ).on( 'click', function(){ - panel.toggleShowDeleted(); - }); - $( '#toggle-hidden' ).on( 'click', function(){ - panel.toggleShowHidden(); - }); - this.render(); - } + model : history + }).render(); + + $( '#toggle-deleted' ).on( 'click', function(){ + historyPanel.toggleShowDeleted(); + }); + $( '#toggle-hidden' ).on( 'click', function(){ + historyPanel.toggleShowHidden(); }); }); }); diff --git a/templates/webapps/galaxy/root/history.mako b/templates/webapps/galaxy/root/history.mako index 069048c8784..fe76697c9e5 100644 --- a/templates/webapps/galaxy/root/history.mako +++ b/templates/webapps/galaxy/root/history.mako @@ -20,21 +20,25 @@ $(function(){