1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/9728fcd5167c/ Changeset: 9728fcd5167c User: carlfeberhard Date: 2014-10-15 20:21:51+00:00 Summary: History structure: found a way to sync SVG and DOM when expanding/collapsing jobs and datasets in the vertical layout Affected #: 9 files diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 client/galaxy/scripts/mvc/history/history-structure-view.js --- a/client/galaxy/scripts/mvc/history/history-structure-view.js +++ b/client/galaxy/scripts/mvc/history/history-structure-view.js @@ -52,14 +52,22 @@ //this.debug( job.outputCollection ); // create the bbone view for the job (to be positioned later accrd. to the layout) and cache - var li = new JOB_LI.JobListItemView({ model: job, expanded: true }); + var li = new JOB_LI.JobListItemView({ model: job }); //var li = new JOB_LI.JobListItemView({ model: job }); - li.$el.appendTo( view.$el ); + li.render( 0 ).$el.appendTo( view.$el ); view._jobLiMap[ job.id ] = li; + view._setUpJobListeners( li ); + }); return view.jobs; }, + _setUpJobListeners : function( jobLi ){ + // update the layout during expansion and collapsing of job and output li's + jobLi.on( 'expanding expanded collapsing collapsed', this.render, this ); + jobLi.foldout.on( 'view:expanding view:expanded view:collapsing view:collapsed', this.render, this ); + }, + layoutDefaults : { paddingTop : 8, paddingLeft : 20, @@ -131,32 +139,32 @@ }, render : function( options ){ - this.log( this + '.renderComponent:', options ); + this.debug( this + '.render:', options ); var view = this; - view.component.eachVertex( function( v ){ - //TODO:? liMap needed - can't we attach to vertex? - var li = view._jobLiMap[ v.name ]; - if( !li.$el.is( ':visible' ) ){ - li.render( 0 ); - } - }); + function _render(){ + view._updateLayout(); + // set up the display containers + view.$el + .width( view.layout.el.width ) + .height( view.layout.el.height ); + view.renderSVG(); - view._updateLayout(); - // set up the display containers - view.$el - .width( view.layout.el.width ) - .height( view.layout.el.height ); - this.renderSVG(); - - // position the job views accrd. to the layout - view.component.eachVertex( function( v ){ - //TODO:? liMap needed - can't we attach to vertex? - var li = view._jobLiMap[ v.name ], - position = view.layout.nodeMap[ li.model.id ]; - //this.debug( position ); - li.$el.css({ top: position.y, left: position.x }); - }); + // position the job views accrd. to the layout + view.component.eachVertex( function( v ){ +//TODO:? liMap needed - can't we attach to vertex? + var li = view._jobLiMap[ v.name ], + position = view.layout.nodeMap[ li.model.id ]; + //this.debug( position ); + li.$el.css({ top: position.y, left: position.x }); + }); + } +//TODO: hack - li's invisible in updateLayout without this delay + if( !this.$el.is( ':visible' ) ){ + _.delay( _render, 0 ); + } else { + _render(); + } return this; }, @@ -195,7 +203,7 @@ .on( 'mouseover', highlightConnect ) .on( 'mouseout', unhighlightConnect ); - connections.transition() + connections .attr( 'd', function( d ){ return view._connectionPath( d ); }); //TODO: ? can we use tick here to update the links? @@ -256,7 +264,7 @@ */ var VerticalHistoryStructureComponent = HistoryStructureComponent.extend({ - logger : console, + //logger : console, className : HistoryStructureComponent.prototype.className + ' vertical', @@ -376,8 +384,8 @@ _createComponent : function( component ){ this.log( this + '._createComponent:', component ); - return new HistoryStructureComponent({ - //return new VerticalHistoryStructureComponent({ + //return new HistoryStructureComponent({ + return new VerticalHistoryStructureComponent({ model : this.model, component : component }); diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 client/galaxy/scripts/mvc/job/job-li.js --- a/client/galaxy/scripts/mvc/job/job-li.js +++ b/client/galaxy/scripts/mvc/job/job-li.js @@ -30,7 +30,6 @@ /** where should pages from links be displayed? (default to new tab/window) */ this.linkTarget = attributes.linkTarget || '_blank'; - this._setUpListeners(); }, /** In this override, add the state as a class for use with state-based CSS */ diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 client/galaxy/scripts/mvc/list/list-item.js --- a/client/galaxy/scripts/mvc/list/list-item.js +++ b/client/galaxy/scripts/mvc/list/list-item.js @@ -17,7 +17,7 @@ /** 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; + this.fxSpeed = attributes.fxSpeed !== undefined? attributes.fxSpeed : this.fxSpeed; }, // ........................................................................ render main @@ -119,15 +119,8 @@ */ 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; - view.$details().slideDown( view.fxSpeed, function(){ - view.trigger( 'expanded', view ); - }); + return view._fetchModelDetails().always( function(){ + view._expand(); }); }, @@ -141,6 +134,24 @@ return jQuery.when(); }, + /** Inner fn called when expand (public) has fetched the details */ + _expand : function(){ + var view = this, + $newDetails = view._renderDetails(); + view.$details().replaceWith( $newDetails ); + // needs to be set after the above or the slide will not show + view.expanded = true; + view.$details().slideDown({ + duration : view.fxSpeed, + step: function(){ + view.trigger( 'expanding', view ); + }, + complete: function(){ + view.trigger( 'expanded', view ); + } + }); + }, + /** Hide the body/details of an HDA. * @fires collapsed when a body has been collapsed */ @@ -148,8 +159,14 @@ this.debug( this + '(ExpandableView).collapse' ); var view = this; view.expanded = false; - this.$details().slideUp( view.fxSpeed, function(){ - view.trigger( 'collapsed', view ); + this.$details().slideUp({ + duration : view.fxSpeed, + step: function(){ + view.trigger( 'collapsing', view ); + }, + complete: function(){ + view.trigger( 'collapsed', view ); + } }); } @@ -377,11 +394,13 @@ * disrespect attributes.expanded if drilldown */ initialize : function( attributes ){ - ListItemView.prototype.initialize.call( this, attributes ); //TODO: hackish if( this.foldoutStyle === 'drilldown' ){ this.expanded = false; } this.foldoutStyle = attributes.foldoutStyle || this.foldoutStyle; this.foldoutPanelClass = attributes.foldoutPanelClass || this.foldoutPanelClass; + + ListItemView.prototype.initialize.call( this, attributes ); + this.foldout = this._createFoldoutPanel(); }, //TODO:?? override to exclude foldout scope? @@ -395,7 +414,7 @@ //TODO: hackish if( this.foldoutStyle === 'drilldown' ){ return $(); } var $newDetails = ListItemView.prototype._renderDetails.call( this ); - return this._attachFoldout( this._createFoldoutPanel(), $newDetails ); + return this._attachFoldout( this.foldout, $newDetails ); }, /** In this override, handle collection expansion. */ @@ -419,7 +438,8 @@ _getFoldoutPanelOptions : function(){ return { // propagate foldout style down - foldoutStyle : this.foldoutStyle + foldoutStyle : this.foldoutStyle, + fxSpeed : this.fxSpeed }; }, @@ -438,25 +458,13 @@ return view._fetchModelDetails() .always(function(){ if( view.foldoutStyle === 'foldout' ){ - view._expandByFoldout(); + view._expand(); } else if( view.foldoutStyle === 'drilldown' ){ view._expandByDrilldown(); } }); }, - /** For foldout, call render details then slide down */ - _expandByFoldout : function(){ - var view = this; - var $newDetails = view._renderDetails(); - view.$details().replaceWith( $newDetails ); - // needs to be set after the above or the slide will not show - view.expanded = true; - view.$details().slideDown( view.fxSpeed, function(){ - view.trigger( 'expanded', view ); - }); - }, - /** For drilldown, set up close handler and fire expanded:drilldown * containing views can listen to this and handle other things * (like hiding themselves) by listening for expanded/collapsed:drilldown @@ -464,7 +472,6 @@ _expandByDrilldown : function(){ var view = this; // attachment and rendering done by listener - view.foldout = this._createFoldoutPanel(); view.foldout.on( 'close', function(){ view.trigger( 'collapsed:drilldown', view, view.foldout ); }); diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/mvc/history/history-structure-view.js --- a/static/scripts/mvc/history/history-structure-view.js +++ b/static/scripts/mvc/history/history-structure-view.js @@ -52,14 +52,22 @@ //this.debug( job.outputCollection ); // create the bbone view for the job (to be positioned later accrd. to the layout) and cache - var li = new JOB_LI.JobListItemView({ model: job, expanded: true }); + var li = new JOB_LI.JobListItemView({ model: job }); //var li = new JOB_LI.JobListItemView({ model: job }); - li.$el.appendTo( view.$el ); + li.render( 0 ).$el.appendTo( view.$el ); view._jobLiMap[ job.id ] = li; + view._setUpJobListeners( li ); + }); return view.jobs; }, + _setUpJobListeners : function( jobLi ){ + // update the layout during expansion and collapsing of job and output li's + jobLi.on( 'expanding expanded collapsing collapsed', this.render, this ); + jobLi.foldout.on( 'view:expanding view:expanded view:collapsing view:collapsed', this.render, this ); + }, + layoutDefaults : { paddingTop : 8, paddingLeft : 20, @@ -131,32 +139,32 @@ }, render : function( options ){ - this.log( this + '.renderComponent:', options ); + this.debug( this + '.render:', options ); var view = this; - view.component.eachVertex( function( v ){ - //TODO:? liMap needed - can't we attach to vertex? - var li = view._jobLiMap[ v.name ]; - if( !li.$el.is( ':visible' ) ){ - li.render( 0 ); - } - }); + function _render(){ + view._updateLayout(); + // set up the display containers + view.$el + .width( view.layout.el.width ) + .height( view.layout.el.height ); + view.renderSVG(); - view._updateLayout(); - // set up the display containers - view.$el - .width( view.layout.el.width ) - .height( view.layout.el.height ); - this.renderSVG(); - - // position the job views accrd. to the layout - view.component.eachVertex( function( v ){ - //TODO:? liMap needed - can't we attach to vertex? - var li = view._jobLiMap[ v.name ], - position = view.layout.nodeMap[ li.model.id ]; - //this.debug( position ); - li.$el.css({ top: position.y, left: position.x }); - }); + // position the job views accrd. to the layout + view.component.eachVertex( function( v ){ +//TODO:? liMap needed - can't we attach to vertex? + var li = view._jobLiMap[ v.name ], + position = view.layout.nodeMap[ li.model.id ]; + //this.debug( position ); + li.$el.css({ top: position.y, left: position.x }); + }); + } +//TODO: hack - li's invisible in updateLayout without this delay + if( !this.$el.is( ':visible' ) ){ + _.delay( _render, 0 ); + } else { + _render(); + } return this; }, @@ -195,7 +203,7 @@ .on( 'mouseover', highlightConnect ) .on( 'mouseout', unhighlightConnect ); - connections.transition() + connections .attr( 'd', function( d ){ return view._connectionPath( d ); }); //TODO: ? can we use tick here to update the links? @@ -256,7 +264,7 @@ */ var VerticalHistoryStructureComponent = HistoryStructureComponent.extend({ - logger : console, + //logger : console, className : HistoryStructureComponent.prototype.className + ' vertical', @@ -376,8 +384,8 @@ _createComponent : function( component ){ this.log( this + '._createComponent:', component ); - return new HistoryStructureComponent({ - //return new VerticalHistoryStructureComponent({ + //return new HistoryStructureComponent({ + return new VerticalHistoryStructureComponent({ model : this.model, component : component }); diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/mvc/job/job-li.js --- a/static/scripts/mvc/job/job-li.js +++ b/static/scripts/mvc/job/job-li.js @@ -30,7 +30,6 @@ /** where should pages from links be displayed? (default to new tab/window) */ this.linkTarget = attributes.linkTarget || '_blank'; - this._setUpListeners(); }, /** In this override, add the state as a class for use with state-based CSS */ diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/mvc/list/list-item.js --- a/static/scripts/mvc/list/list-item.js +++ b/static/scripts/mvc/list/list-item.js @@ -17,7 +17,7 @@ /** 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; + this.fxSpeed = attributes.fxSpeed !== undefined? attributes.fxSpeed : this.fxSpeed; }, // ........................................................................ render main @@ -119,15 +119,8 @@ */ 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; - view.$details().slideDown( view.fxSpeed, function(){ - view.trigger( 'expanded', view ); - }); + return view._fetchModelDetails().always( function(){ + view._expand(); }); }, @@ -141,6 +134,24 @@ return jQuery.when(); }, + /** Inner fn called when expand (public) has fetched the details */ + _expand : function(){ + var view = this, + $newDetails = view._renderDetails(); + view.$details().replaceWith( $newDetails ); + // needs to be set after the above or the slide will not show + view.expanded = true; + view.$details().slideDown({ + duration : view.fxSpeed, + step: function(){ + view.trigger( 'expanding', view ); + }, + complete: function(){ + view.trigger( 'expanded', view ); + } + }); + }, + /** Hide the body/details of an HDA. * @fires collapsed when a body has been collapsed */ @@ -148,8 +159,14 @@ this.debug( this + '(ExpandableView).collapse' ); var view = this; view.expanded = false; - this.$details().slideUp( view.fxSpeed, function(){ - view.trigger( 'collapsed', view ); + this.$details().slideUp({ + duration : view.fxSpeed, + step: function(){ + view.trigger( 'collapsing', view ); + }, + complete: function(){ + view.trigger( 'collapsed', view ); + } }); } @@ -377,11 +394,13 @@ * disrespect attributes.expanded if drilldown */ initialize : function( attributes ){ - ListItemView.prototype.initialize.call( this, attributes ); //TODO: hackish if( this.foldoutStyle === 'drilldown' ){ this.expanded = false; } this.foldoutStyle = attributes.foldoutStyle || this.foldoutStyle; this.foldoutPanelClass = attributes.foldoutPanelClass || this.foldoutPanelClass; + + ListItemView.prototype.initialize.call( this, attributes ); + this.foldout = this._createFoldoutPanel(); }, //TODO:?? override to exclude foldout scope? @@ -395,7 +414,7 @@ //TODO: hackish if( this.foldoutStyle === 'drilldown' ){ return $(); } var $newDetails = ListItemView.prototype._renderDetails.call( this ); - return this._attachFoldout( this._createFoldoutPanel(), $newDetails ); + return this._attachFoldout( this.foldout, $newDetails ); }, /** In this override, handle collection expansion. */ @@ -419,7 +438,8 @@ _getFoldoutPanelOptions : function(){ return { // propagate foldout style down - foldoutStyle : this.foldoutStyle + foldoutStyle : this.foldoutStyle, + fxSpeed : this.fxSpeed }; }, @@ -438,25 +458,13 @@ return view._fetchModelDetails() .always(function(){ if( view.foldoutStyle === 'foldout' ){ - view._expandByFoldout(); + view._expand(); } else if( view.foldoutStyle === 'drilldown' ){ view._expandByDrilldown(); } }); }, - /** For foldout, call render details then slide down */ - _expandByFoldout : function(){ - var view = this; - var $newDetails = view._renderDetails(); - view.$details().replaceWith( $newDetails ); - // needs to be set after the above or the slide will not show - view.expanded = true; - view.$details().slideDown( view.fxSpeed, function(){ - view.trigger( 'expanded', view ); - }); - }, - /** For drilldown, set up close handler and fire expanded:drilldown * containing views can listen to this and handle other things * (like hiding themselves) by listening for expanded/collapsed:drilldown @@ -464,7 +472,6 @@ _expandByDrilldown : function(){ var view = this; // attachment and rendering done by listener - view.foldout = this._createFoldoutPanel(); view.foldout.on( 'close', function(){ view.trigger( 'collapsed:drilldown', view, view.foldout ); }); diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/packed/mvc/history/history-structure-view.js --- a/static/scripts/packed/mvc/history/history-structure-view.js +++ b/static/scripts/packed/mvc/history/history-structure-view.js @@ -1,1 +1,1 @@ -define(["mvc/job/job-model","mvc/job/job-li","mvc/history/history-content-model","mvc/history/job-dag","mvc/base-mvc","utils/localization","libs/d3"],function(e,f,i,c,h,a){var b=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure-component",initialize:function(j){this.log(this+"(HistoryStructureComponent).initialize:",j);this.component=j.component;this._jobLiMap={};this._createJobModels();this.layout=this._createLayout(j.layoutOptions)},_createJobModels:function(){var j=this;j.component.eachVertex(function(o){var n=o.data.job,m=new e.Job(n);var l=_.map(m.get("outputs"),function(p){var r=p.src==="hda"?"dataset":"dataset_collection",q=i.typeIdStr(r,p.id);return j.model.contents.get(q)});m.outputCollection.reset(l);m.outputCollection.historyId=j.model.id;var k=new f.JobListItemView({model:m,expanded:true});k.$el.appendTo(j.$el);j._jobLiMap[m.id]=k});return j.jobs},layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobHeight:308,jobWidthSpacing:320,linkAdjX:4,linkAdjY:0},_createLayout:function(l){l=_.defaults(_.clone(l||{}),this.layoutDefaults);var j=this,k=_.values(j.component.vertices),m=_.extend(l,{nodeMap:{},links:[],el:{width:0,height:0},svg:{width:0,height:0,top:0,left:0}});k.forEach(function(n,o){var p={name:n.name,x:0,y:0};m.nodeMap[n.name]=p});j.component.edges(function(o){var n={source:o.source,target:o.target};m.links.push(n)});return m},_updateLayout:function(){var k=this,l=k.layout;l.svg.height=l.paddingTop+(l.linkSpacing*_.size(l.nodeMap));l.el.height=l.svg.height+l.jobHeight;var j=l.paddingLeft,m=l.svg.height;_.each(l.nodeMap,function(o,n){o.x=j;o.y=m;j+=l.jobWidthSpacing});l.el.width=l.svg.width=Math.max(l.el.width,j);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY});return this.layout},render:function(k){this.log(this+".renderComponent:",k);var j=this;j.component.eachVertex(function(m){var l=j._jobLiMap[m.name];if(!l.$el.is(":visible")){l.render(0)}});j._updateLayout();j.$el.width(j.layout.el.width).height(j.layout.el.height);this.renderSVG();j.component.eachVertex(function(n){var m=j._jobLiMap[n.name],l=j.layout.nodeMap[m.model.id];m.$el.css({top:l.y,left:l.x})});return this},renderSVG:function(){var j=this,o=j.layout;var k=d3.select(this.el).select("svg");if(k.empty()){k=d3.select(this.el).append("svg")}k.attr("width",o.svg.width).attr("height",o.svg.height);function m(p){d3.select(this).classed("highlighted",true);j._jobLiMap[p.source].$el.addClass("highlighted");j._jobLiMap[p.target].$el.addClass("highlighted")}function l(p){d3.select(this).classed("highlighted",false);j._jobLiMap[p.source].$el.removeClass("highlighted");j._jobLiMap[p.target].$el.removeClass("highlighted")}var n=k.selectAll(".connection").data(o.links);n.enter().append("path").attr("class","connection").attr("id",function(p){return p.source+"-"+p.target}).on("mouseover",m).on("mouseout",l);n.transition().attr("d",function(p){return j._connectionPath(p)});return k.node()},_connectionPath:function(k){var l=0,j=((k.x2-k.x1)/this.layout.svg.width)*this.layout.svg.height;return["M",k.x1,",",k.y1," ","C",k.x1+l,",",k.y1-j," ",k.x2-l,",",k.y2-j," ",k.x2,",",k.y2].join("")},events:{"mouseover .job.list-item":function(j){this.highlightConnected(j.currentTarget,true)},"mouseout .job.list-item":function(j){this.highlightConnected(j.currentTarget,false)}},highlightConnected:function(p,k){k=k!==undefined?k:true;var j=this,l=j.component,n=k?jQuery.prototype.addClass:jQuery.prototype.removeClass,m=k?"connection highlighted":"connection";var o=n.call($(p),"highlighted"),q=o.attr("id").replace("job-","");l.edges({target:q}).forEach(function(r){n.call(j.$("#job-"+r.source),"highlighted");j.$("#"+r.source+"-"+q).attr("class",m)});l.vertices[q].eachEdge(function(r){n.call(j.$("#job-"+r.target),"highlighted");j.$("#"+q+"-"+r.target).attr("class",m)})},toString:function(){return"HistoryStructureComponent("+this.model.id+")"}});var g=b.extend({logger:console,className:b.prototype.className+" vertical",layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobWidth:308,jobHeight:308,initialSpacing:64,jobSpacing:16,linkAdjX:0,linkAdjY:4},_updateLayout:function(){var k=this,l=k.layout;l.svg.width=l.paddingLeft+(l.linkSpacing*_.size(l.nodeMap));l.el.width=l.svg.width+l.jobWidth;l.el.height=0;var j=l.svg.width,m=l.paddingTop;_.each(l.nodeMap,function(p,o){p.x=j;p.y=m;var n=k._jobLiMap[o];if(n.$el.is(":visible")){m+=n.$el.height()+l.jobSpacing}else{m+=l.initialSpacing+l.jobSpacing}});l.el.height=l.svg.height=Math.max(l.el.height,m);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY;k.debug("link:",n.x1,n.y1,n.x2,n.y2,n)});k.debug("el:",l.el);k.debug("svg:",l.svg);return l},_connectionPath:function(l){var k=0,j=((l.y2-l.y1)/this.layout.svg.height)*this.layout.svg.width;return["M",l.x1,",",l.y1," ","C",l.x1-j,",",l.y1+k," ",l.x2-j,",",l.y2-k," ",l.x2,",",l.y2].join("")},toString:function(){return"VerticalHistoryStructureComponent("+this.model.id+")"}});var d=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure",initialize:function(j){this.log(this+"(HistoryStructureView).initialize:",j,this.model);this.jobs=j.jobs;this._createDAG()},_createDAG:function(){this.dag=new c({historyContents:this.model.contents.toJSON(),jobs:this.jobs,excludeSetMetadata:true,excludeErroredJobs:true});this.log(this+".dag:",this.dag);this._createComponents()},_createComponents:function(){this.log(this+"._createComponents");var j=this;j.componentViews=j.dag.weakComponentGraphArray().map(function(k){return j._createComponent(k)})},_createComponent:function(j){this.log(this+"._createComponent:",j);return new b({model:this.model,component:j})},render:function(k){this.log(this+".render:",k);var j=this;j.$el.html(['<div class="controls"></div>','<div class="components"></div>'].join(""));j.componentViews.forEach(function(l){l.render().$el.appendTo(j.$components())});return j},$components:function(){return this.$(".components")},toString:function(){return"HistoryStructureView()"}});return d}); \ No newline at end of file +define(["mvc/job/job-model","mvc/job/job-li","mvc/history/history-content-model","mvc/history/job-dag","mvc/base-mvc","utils/localization","libs/d3"],function(e,f,i,c,h,a){var b=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure-component",initialize:function(j){this.log(this+"(HistoryStructureComponent).initialize:",j);this.component=j.component;this._jobLiMap={};this._createJobModels();this.layout=this._createLayout(j.layoutOptions)},_createJobModels:function(){var j=this;j.component.eachVertex(function(o){var n=o.data.job,m=new e.Job(n);var l=_.map(m.get("outputs"),function(p){var r=p.src==="hda"?"dataset":"dataset_collection",q=i.typeIdStr(r,p.id);return j.model.contents.get(q)});m.outputCollection.reset(l);m.outputCollection.historyId=j.model.id;var k=new f.JobListItemView({model:m});k.render(0).$el.appendTo(j.$el);j._jobLiMap[m.id]=k;j._setUpJobListeners(k)});return j.jobs},_setUpJobListeners:function(j){j.on("expanding expanded collapsing collapsed",this.render,this);j.foldout.on("view:expanding view:expanded view:collapsing view:collapsed",this.render,this)},layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobHeight:308,jobWidthSpacing:320,linkAdjX:4,linkAdjY:0},_createLayout:function(l){l=_.defaults(_.clone(l||{}),this.layoutDefaults);var j=this,k=_.values(j.component.vertices),m=_.extend(l,{nodeMap:{},links:[],el:{width:0,height:0},svg:{width:0,height:0,top:0,left:0}});k.forEach(function(n,o){var p={name:n.name,x:0,y:0};m.nodeMap[n.name]=p});j.component.edges(function(o){var n={source:o.source,target:o.target};m.links.push(n)});return m},_updateLayout:function(){var k=this,l=k.layout;l.svg.height=l.paddingTop+(l.linkSpacing*_.size(l.nodeMap));l.el.height=l.svg.height+l.jobHeight;var j=l.paddingLeft,m=l.svg.height;_.each(l.nodeMap,function(o,n){o.x=j;o.y=m;j+=l.jobWidthSpacing});l.el.width=l.svg.width=Math.max(l.el.width,j);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY});return this.layout},render:function(k){this.debug(this+".render:",k);var j=this;function l(){j._updateLayout();j.$el.width(j.layout.el.width).height(j.layout.el.height);j.renderSVG();j.component.eachVertex(function(o){var n=j._jobLiMap[o.name],m=j.layout.nodeMap[n.model.id];n.$el.css({top:m.y,left:m.x})})}if(!this.$el.is(":visible")){_.delay(l,0)}else{l()}return this},renderSVG:function(){var j=this,o=j.layout;var k=d3.select(this.el).select("svg");if(k.empty()){k=d3.select(this.el).append("svg")}k.attr("width",o.svg.width).attr("height",o.svg.height);function m(p){d3.select(this).classed("highlighted",true);j._jobLiMap[p.source].$el.addClass("highlighted");j._jobLiMap[p.target].$el.addClass("highlighted")}function l(p){d3.select(this).classed("highlighted",false);j._jobLiMap[p.source].$el.removeClass("highlighted");j._jobLiMap[p.target].$el.removeClass("highlighted")}var n=k.selectAll(".connection").data(o.links);n.enter().append("path").attr("class","connection").attr("id",function(p){return p.source+"-"+p.target}).on("mouseover",m).on("mouseout",l);n.attr("d",function(p){return j._connectionPath(p)});return k.node()},_connectionPath:function(k){var l=0,j=((k.x2-k.x1)/this.layout.svg.width)*this.layout.svg.height;return["M",k.x1,",",k.y1," ","C",k.x1+l,",",k.y1-j," ",k.x2-l,",",k.y2-j," ",k.x2,",",k.y2].join("")},events:{"mouseover .job.list-item":function(j){this.highlightConnected(j.currentTarget,true)},"mouseout .job.list-item":function(j){this.highlightConnected(j.currentTarget,false)}},highlightConnected:function(p,k){k=k!==undefined?k:true;var j=this,l=j.component,n=k?jQuery.prototype.addClass:jQuery.prototype.removeClass,m=k?"connection highlighted":"connection";var o=n.call($(p),"highlighted"),q=o.attr("id").replace("job-","");l.edges({target:q}).forEach(function(r){n.call(j.$("#job-"+r.source),"highlighted");j.$("#"+r.source+"-"+q).attr("class",m)});l.vertices[q].eachEdge(function(r){n.call(j.$("#job-"+r.target),"highlighted");j.$("#"+q+"-"+r.target).attr("class",m)})},toString:function(){return"HistoryStructureComponent("+this.model.id+")"}});var g=b.extend({className:b.prototype.className+" vertical",layoutDefaults:{paddingTop:8,paddingLeft:20,linkSpacing:16,jobWidth:308,jobHeight:308,initialSpacing:64,jobSpacing:16,linkAdjX:0,linkAdjY:4},_updateLayout:function(){var k=this,l=k.layout;l.svg.width=l.paddingLeft+(l.linkSpacing*_.size(l.nodeMap));l.el.width=l.svg.width+l.jobWidth;l.el.height=0;var j=l.svg.width,m=l.paddingTop;_.each(l.nodeMap,function(p,o){p.x=j;p.y=m;var n=k._jobLiMap[o];if(n.$el.is(":visible")){m+=n.$el.height()+l.jobSpacing}else{m+=l.initialSpacing+l.jobSpacing}});l.el.height=l.svg.height=Math.max(l.el.height,m);l.links.forEach(function(n){var o=l.nodeMap[n.source],p=l.nodeMap[n.target];n.x1=o.x+l.linkAdjX;n.y1=o.y+l.linkAdjY;n.x2=p.x+l.linkAdjX;n.y2=p.y+l.linkAdjY;k.debug("link:",n.x1,n.y1,n.x2,n.y2,n)});k.debug("el:",l.el);k.debug("svg:",l.svg);return l},_connectionPath:function(l){var k=0,j=((l.y2-l.y1)/this.layout.svg.height)*this.layout.svg.width;return["M",l.x1,",",l.y1," ","C",l.x1-j,",",l.y1+k," ",l.x2-j,",",l.y2-k," ",l.x2,",",l.y2].join("")},toString:function(){return"VerticalHistoryStructureComponent("+this.model.id+")"}});var d=Backbone.View.extend(h.LoggableMixin).extend({className:"history-structure",initialize:function(j){this.log(this+"(HistoryStructureView).initialize:",j,this.model);this.jobs=j.jobs;this._createDAG()},_createDAG:function(){this.dag=new c({historyContents:this.model.contents.toJSON(),jobs:this.jobs,excludeSetMetadata:true,excludeErroredJobs:true});this.log(this+".dag:",this.dag);this._createComponents()},_createComponents:function(){this.log(this+"._createComponents");var j=this;j.componentViews=j.dag.weakComponentGraphArray().map(function(k){return j._createComponent(k)})},_createComponent:function(j){this.log(this+"._createComponent:",j);return new g({model:this.model,component:j})},render:function(k){this.log(this+".render:",k);var j=this;j.$el.html(['<div class="controls"></div>','<div class="components"></div>'].join(""));j.componentViews.forEach(function(l){l.render().$el.appendTo(j.$components())});return j},$components:function(){return this.$(".components")},toString:function(){return"HistoryStructureView()"}});return d}); \ No newline at end of file diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/packed/mvc/job/job-li.js --- a/static/scripts/packed/mvc/job/job-li.js +++ b/static/scripts/packed/mvc/job/job-li.js @@ -1,1 +1,1 @@ -define(["mvc/list/list-item","mvc/dataset/dataset-list","mvc/base-mvc","utils/localization"],function(d,f,b,c){var e=d.FoldoutListItemView;var a=e.extend({className:e.prototype.className+" job",id:function(){return["job",this.model.get("id")].join("-")},foldoutPanelClass:f.DatasetList,initialize:function(g){if(g.logger){this.logger=this.model.logger=g.logger}this.log(this+".initialize:",g);e.prototype.initialize.call(this,g);this.linkTarget=g.linkTarget||"_blank";this._setUpListeners()},_swapNewRender:function(g){e.prototype._swapNewRender.call(this,g);if(this.model.has("state")){this.$el.addClass("state-"+this.model.get("state"))}return this.$el},_getFoldoutPanelOptions:function(){var g=e.prototype._getFoldoutPanelOptions.call(this);return _.extend(g,{collection:this.model.outputCollection,selecting:false})},toString:function(){return"JobListItemView("+this.model+")"}});a.prototype.templates=(function(){var g=b.wrapTemplate(['<div class="list-element">','<div class="id"><%= model.id %></div>','<div class="warnings"></div>','<div class="selector">','<span class="fa fa-2x fa-square-o"></span>',"</div>",'<div class="primary-actions"></div>','<div class="title-bar"></div>','<div class="details"></div>',"</div>"]);var h=b.wrapTemplate(['<div class="title-bar clear" tabindex="0">','<div class="title">','<span class="name"><%- job.tool_id %></span>',"</div>",'<div class="subtitle"></div>',"</div>"],"job");return _.extend({},e.prototype.templates,{titleBar:h,})}());return{JobListItemView:a}}); \ No newline at end of file +define(["mvc/list/list-item","mvc/dataset/dataset-list","mvc/base-mvc","utils/localization"],function(d,f,b,c){var e=d.FoldoutListItemView;var a=e.extend({className:e.prototype.className+" job",id:function(){return["job",this.model.get("id")].join("-")},foldoutPanelClass:f.DatasetList,initialize:function(g){if(g.logger){this.logger=this.model.logger=g.logger}this.log(this+".initialize:",g);e.prototype.initialize.call(this,g);this.linkTarget=g.linkTarget||"_blank"},_swapNewRender:function(g){e.prototype._swapNewRender.call(this,g);if(this.model.has("state")){this.$el.addClass("state-"+this.model.get("state"))}return this.$el},_getFoldoutPanelOptions:function(){var g=e.prototype._getFoldoutPanelOptions.call(this);return _.extend(g,{collection:this.model.outputCollection,selecting:false})},toString:function(){return"JobListItemView("+this.model+")"}});a.prototype.templates=(function(){var g=b.wrapTemplate(['<div class="list-element">','<div class="id"><%= model.id %></div>','<div class="warnings"></div>','<div class="selector">','<span class="fa fa-2x fa-square-o"></span>',"</div>",'<div class="primary-actions"></div>','<div class="title-bar"></div>','<div class="details"></div>',"</div>"]);var h=b.wrapTemplate(['<div class="title-bar clear" tabindex="0">','<div class="title">','<span class="name"><%- job.tool_id %></span>',"</div>",'<div class="subtitle"></div>',"</div>"],"job");return _.extend({},e.prototype.templates,{titleBar:h,})}());return{JobListItemView:a}}); \ No newline at end of file diff -r 78124761ad558e4670c338fdbdbf371acb6b91bd -r 9728fcd5167c2aceb8e0b72d3d8b9dc6fd5435b7 static/scripts/packed/mvc/list/list-item.js --- a/static/scripts/packed/mvc/list/list-item.js +++ b/static/scripts/packed/mvc/list/list-item.js @@ -1,1 +1,1 @@ -define(["mvc/base-mvc","utils/localization"],function(b,d){var e=Backbone.View.extend(b.LoggableMixin).extend({initialize:function(f){this.expanded=f.expanded||false;this.log("\t expanded:",this.expanded);this.fxSpeed=f.fxSpeed||this.fxSpeed},fxSpeed:"fast",render:function(g){var f=this._buildNewRender();this._setUpBehaviors(f);this._queueNewRender(f,g);return this},_buildNewRender:function(){var f=$(this.templates.el(this.model.toJSON(),this));if(this.expanded){this.$details(f).replaceWith(this._renderDetails().show())}return f},_queueNewRender:function(g,h){h=(h===undefined)?(this.fxSpeed):(h);var f=this;$(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){this.trigger("rendered",f);i()}])},_swapNewRender:function(f){return this.$el.empty().attr("class",_.isFunction(this.className)?this.className():this.className).append(f.children())},_setUpBehaviors:function(f){f=f||this.$el;f.find("[title]").tooltip({placement:"bottom"})},$details:function(f){f=f||this.$el;return f.find("> .details")},_renderDetails:function(){var f=$(this.templates.details(this.model.toJSON(),this));this._setUpBehaviors(f);return f},toggleExpanded:function(f){f=(f===undefined)?(!this.expanded):(f);if(f){this.expand()}else{this.collapse()}return this},expand:function(){var f=this;return f._fetchModelDetails().always(function(){var g=f._renderDetails();f.$details().replaceWith(g);f.expanded=true;f.$details().slideDown(f.fxSpeed,function(){f.trigger("expanded",f)})})},_fetchModelDetails:function(){if(!this.model.hasDetails()){return this.model.fetch()}return jQuery.when()},collapse:function(){this.debug(this+"(ExpandableView).collapse");var f=this;f.expanded=false;this.$details().slideUp(f.fxSpeed,function(){f.trigger("collapsed",f)})}});var a=e.extend(b.mixin(b.SelectableViewMixin,b.DraggableViewMixin,{tagName:"div",className:"list-item",initialize:function(f){e.prototype.initialize.call(this,f);b.SelectableViewMixin.initialize.call(this,f);b.DraggableViewMixin.initialize.call(this,f);this._setUpListeners()},_setUpListeners:function(){this.on("selectable",function(f){if(f){this.$(".primary-actions").hide()}else{this.$(".primary-actions").show()}},this);return this},_buildNewRender:function(){var f=e.prototype._buildNewRender.call(this);f.children(".warnings").replaceWith(this._renderWarnings());f.children(".title-bar").replaceWith(this._renderTitleBar());f.children(".primary-actions").append(this._renderPrimaryActions());f.find("> .title-bar .subtitle").replaceWith(this._renderSubtitle());return f},_swapNewRender:function(f){e.prototype._swapNewRender.call(this,f);if(this.selectable){this.showSelector(0)}if(this.draggable){this.draggableOn()}return this.$el},_renderWarnings:function(){var f=this,h=$('<div class="warnings"></div>'),g=f.model.toJSON();_.each(f.templates.warnings,function(i){h.append($(i(g,f)))});return h},_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(f){f.stopPropagation();if(f.altKey){this.toggleSelect(f);if(!this.selectable){this.showSelector()}}else{this.toggleExpanded()}},_keyDownTitleBar:function(h){var f=32,g=13;if(h&&(h.type==="keydown")&&(h.keyCode===f||h.keyCode===g)){this.toggleExpanded();h.stopPropagation();return false}return true},toString:function(){var f=(this.model)?(this.model+""):("(no model)");return"ListItemView("+f+")"}}));a.prototype.templates=(function(){var h=b.wrapTemplate(['<div class="list-element">','<div class="warnings"></div>','<div class="selector">','<span class="fa fa-2x fa-square-o"></span>',"</div>",'<div class="primary-actions"></div>','<div class="title-bar"></div>','<div class="details"></div>',"</div>"]);var f={};var i=b.wrapTemplate(['<div class="title-bar clear" tabindex="0">','<span class="state-icon"></span>','<div class="title">','<span class="name"><%- element.name %></span>',"</div>",'<div class="subtitle"></div>',"</div>"],"element");var j=b.wrapTemplate(['<div class="subtitle"></div>']);var g=b.wrapTemplate(['<div class="details"></div>']);return{el:h,warnings:f,titleBar:i,subtitle:j,details:g}}());var c=a.extend({foldoutStyle:"foldout",foldoutPanelClass:null,initialize:function(f){a.prototype.initialize.call(this,f);if(this.foldoutStyle==="drilldown"){this.expanded=false}this.foldoutStyle=f.foldoutStyle||this.foldoutStyle;this.foldoutPanelClass=f.foldoutPanelClass||this.foldoutPanelClass},_renderDetails:function(){if(this.foldoutStyle==="drilldown"){return $()}var f=a.prototype._renderDetails.call(this);return this._attachFoldout(this._createFoldoutPanel(),f)},_createFoldoutPanel:function(){var h=this.model;var i=this._getFoldoutPanelClass(h),g=this._getFoldoutPanelOptions(h),f=new i(_.extend(g,{model:h}));return f},_getFoldoutPanelClass:function(){return this.foldoutPanelClass},_getFoldoutPanelOptions:function(){return{foldoutStyle:this.foldoutStyle}},_attachFoldout:function(f,g){g=g||this.$("> .details");this.foldout=f.render(0);f.$("> .controls").hide();return g.append(f.$el)},expand:function(){var f=this;return f._fetchModelDetails().always(function(){if(f.foldoutStyle==="foldout"){f._expandByFoldout()}else{if(f.foldoutStyle==="drilldown"){f._expandByDrilldown()}}})},_expandByFoldout:function(){var f=this;var g=f._renderDetails();f.$details().replaceWith(g);f.expanded=true;f.$details().slideDown(f.fxSpeed,function(){f.trigger("expanded",f)})},_expandByDrilldown:function(){var f=this;f.foldout=this._createFoldoutPanel();f.foldout.on("close",function(){f.trigger("collapsed:drilldown",f,f.foldout)});f.trigger("expanded:drilldown",f,f.foldout)}});c.prototype.templates=(function(){var f=b.wrapTemplate(['<div class="details">',"</div>"],"collection");return _.extend({},a.prototype.templates,{details:f})}());return{ExpandableView:e,ListItemView:a,FoldoutListItemView:c}}); \ No newline at end of file +define(["mvc/base-mvc","utils/localization"],function(b,d){var e=Backbone.View.extend(b.LoggableMixin).extend({initialize:function(f){this.expanded=f.expanded||false;this.log("\t expanded:",this.expanded);this.fxSpeed=f.fxSpeed!==undefined?f.fxSpeed:this.fxSpeed},fxSpeed:"fast",render:function(g){var f=this._buildNewRender();this._setUpBehaviors(f);this._queueNewRender(f,g);return this},_buildNewRender:function(){var f=$(this.templates.el(this.model.toJSON(),this));if(this.expanded){this.$details(f).replaceWith(this._renderDetails().show())}return f},_queueNewRender:function(g,h){h=(h===undefined)?(this.fxSpeed):(h);var f=this;$(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){this.trigger("rendered",f);i()}])},_swapNewRender:function(f){return this.$el.empty().attr("class",_.isFunction(this.className)?this.className():this.className).append(f.children())},_setUpBehaviors:function(f){f=f||this.$el;f.find("[title]").tooltip({placement:"bottom"})},$details:function(f){f=f||this.$el;return f.find("> .details")},_renderDetails:function(){var f=$(this.templates.details(this.model.toJSON(),this));this._setUpBehaviors(f);return f},toggleExpanded:function(f){f=(f===undefined)?(!this.expanded):(f);if(f){this.expand()}else{this.collapse()}return this},expand:function(){var f=this;return f._fetchModelDetails().always(function(){f._expand()})},_fetchModelDetails:function(){if(!this.model.hasDetails()){return this.model.fetch()}return jQuery.when()},_expand:function(){var f=this,g=f._renderDetails();f.$details().replaceWith(g);f.expanded=true;f.$details().slideDown({duration:f.fxSpeed,step:function(){f.trigger("expanding",f)},complete:function(){f.trigger("expanded",f)}})},collapse:function(){this.debug(this+"(ExpandableView).collapse");var f=this;f.expanded=false;this.$details().slideUp({duration:f.fxSpeed,step:function(){f.trigger("collapsing",f)},complete:function(){f.trigger("collapsed",f)}})}});var a=e.extend(b.mixin(b.SelectableViewMixin,b.DraggableViewMixin,{tagName:"div",className:"list-item",initialize:function(f){e.prototype.initialize.call(this,f);b.SelectableViewMixin.initialize.call(this,f);b.DraggableViewMixin.initialize.call(this,f);this._setUpListeners()},_setUpListeners:function(){this.on("selectable",function(f){if(f){this.$(".primary-actions").hide()}else{this.$(".primary-actions").show()}},this);return this},_buildNewRender:function(){var f=e.prototype._buildNewRender.call(this);f.children(".warnings").replaceWith(this._renderWarnings());f.children(".title-bar").replaceWith(this._renderTitleBar());f.children(".primary-actions").append(this._renderPrimaryActions());f.find("> .title-bar .subtitle").replaceWith(this._renderSubtitle());return f},_swapNewRender:function(f){e.prototype._swapNewRender.call(this,f);if(this.selectable){this.showSelector(0)}if(this.draggable){this.draggableOn()}return this.$el},_renderWarnings:function(){var f=this,h=$('<div class="warnings"></div>'),g=f.model.toJSON();_.each(f.templates.warnings,function(i){h.append($(i(g,f)))});return h},_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(f){f.stopPropagation();if(f.altKey){this.toggleSelect(f);if(!this.selectable){this.showSelector()}}else{this.toggleExpanded()}},_keyDownTitleBar:function(h){var f=32,g=13;if(h&&(h.type==="keydown")&&(h.keyCode===f||h.keyCode===g)){this.toggleExpanded();h.stopPropagation();return false}return true},toString:function(){var f=(this.model)?(this.model+""):("(no model)");return"ListItemView("+f+")"}}));a.prototype.templates=(function(){var h=b.wrapTemplate(['<div class="list-element">','<div class="warnings"></div>','<div class="selector">','<span class="fa fa-2x fa-square-o"></span>',"</div>",'<div class="primary-actions"></div>','<div class="title-bar"></div>','<div class="details"></div>',"</div>"]);var f={};var i=b.wrapTemplate(['<div class="title-bar clear" tabindex="0">','<span class="state-icon"></span>','<div class="title">','<span class="name"><%- element.name %></span>',"</div>",'<div class="subtitle"></div>',"</div>"],"element");var j=b.wrapTemplate(['<div class="subtitle"></div>']);var g=b.wrapTemplate(['<div class="details"></div>']);return{el:h,warnings:f,titleBar:i,subtitle:j,details:g}}());var c=a.extend({foldoutStyle:"foldout",foldoutPanelClass:null,initialize:function(f){if(this.foldoutStyle==="drilldown"){this.expanded=false}this.foldoutStyle=f.foldoutStyle||this.foldoutStyle;this.foldoutPanelClass=f.foldoutPanelClass||this.foldoutPanelClass;a.prototype.initialize.call(this,f);this.foldout=this._createFoldoutPanel()},_renderDetails:function(){if(this.foldoutStyle==="drilldown"){return $()}var f=a.prototype._renderDetails.call(this);return this._attachFoldout(this.foldout,f)},_createFoldoutPanel:function(){var h=this.model;var i=this._getFoldoutPanelClass(h),g=this._getFoldoutPanelOptions(h),f=new i(_.extend(g,{model:h}));return f},_getFoldoutPanelClass:function(){return this.foldoutPanelClass},_getFoldoutPanelOptions:function(){return{foldoutStyle:this.foldoutStyle,fxSpeed:this.fxSpeed}},_attachFoldout:function(f,g){g=g||this.$("> .details");this.foldout=f.render(0);f.$("> .controls").hide();return g.append(f.$el)},expand:function(){var f=this;return f._fetchModelDetails().always(function(){if(f.foldoutStyle==="foldout"){f._expand()}else{if(f.foldoutStyle==="drilldown"){f._expandByDrilldown()}}})},_expandByDrilldown:function(){var f=this;f.foldout.on("close",function(){f.trigger("collapsed:drilldown",f,f.foldout)});f.trigger("expanded:drilldown",f,f.foldout)}});c.prototype.templates=(function(){var f=b.wrapTemplate(['<div class="details">',"</div>"],"collection");return _.extend({},a.prototype.templates,{details:f})}());return{ExpandableView:e,ListItemView:a,FoldoutListItemView:c}}); \ No newline at end of file Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.