mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
UI, Collections: begin refactoring the list of pairs creator into a list collection creator: a subclass for all three (current) collection creators
This commit is contained in:
@@ -1,52 +1,938 @@
|
||||
|
||||
define([
|
||||
"mvc/history/hdca-model",
|
||||
"mvc/dataset/states",
|
||||
"mvc/ui/error-modal",
|
||||
"mvc/base-mvc",
|
||||
"mvc/ui/ui-modal",
|
||||
"utils/natural-sort",
|
||||
"utils/localization"
|
||||
], function( STATES, errorModal, _l ){
|
||||
], function( HDCA, STATES, BASE_MVC, UI_MODAL, naturalSort, _l ){
|
||||
/*==============================================================================
|
||||
|
||||
==============================================================================*/
|
||||
function validElements( collection ){
|
||||
var elements = collection.toJSON().filter( function( content ){
|
||||
return content.history_content_type === 'dataset'
|
||||
&& content.state === STATES.OK;
|
||||
});
|
||||
return elements;
|
||||
}
|
||||
/** A view for both DatasetDCEs and NestedDCDCEs
|
||||
* (things that implement collection-model:DatasetCollectionElementMixin)
|
||||
*/
|
||||
var DatasetCollectionElementView = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({
|
||||
|
||||
var noValidElementsMessage = 'No valid datasets were selected';
|
||||
//TODO: use proper class (DatasetDCE or NestedDCDCE (or the union of both))
|
||||
tagName : 'li',
|
||||
className : 'collection-element',
|
||||
|
||||
function noValidElementsErrorModal(){
|
||||
errorModal(
|
||||
'Use the checkboxes at the left of the dataset names to select them. ' +
|
||||
'Selected datasets should be error-free and should have completed running.',
|
||||
noValidElementsMessage
|
||||
);
|
||||
}
|
||||
initialize : function( attributes ){
|
||||
this.element = attributes.element || {};
|
||||
this.selected = attributes.selected || false;
|
||||
},
|
||||
|
||||
function createListHDCA( collection ){
|
||||
var name = 'New Dataset List',
|
||||
elementIdentifiers = validElements( collection ).map( function( element ){
|
||||
// TODO: Handle duplicate names.
|
||||
return {
|
||||
id : element.id,
|
||||
name : element.name,
|
||||
//TODO: this allows for list:list even if the filter above does not - reconcile
|
||||
src : ( element.history_content_type === 'dataset'? 'hda' : 'hdca' )
|
||||
render : function(){
|
||||
this.$el
|
||||
.attr( 'data-element-id', this.element.id )
|
||||
.attr( 'draggable', true )
|
||||
.html( this.template({ element: this.element }) );
|
||||
if( this.selected ){
|
||||
this.$el.addClass( 'selected' );
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
//TODO: lots of unused space in the element - possibly load details and display them horiz.
|
||||
template : _.template([
|
||||
'<span class="name" title="', _l( 'Click to rename' ), '"><%= element.name %></span>',
|
||||
'<button class="discard btn btn-sm" title="', _l( 'Remove this dataset from the list' ), '">',
|
||||
_l( 'Discard' ),
|
||||
'</span>',
|
||||
].join('')),
|
||||
|
||||
select : function( toggle ){
|
||||
this.$el.toggleClass( 'selected', toggle );
|
||||
this.trigger( 'select', {
|
||||
source : this,
|
||||
selected : this.$el.hasClass( 'selected' )
|
||||
});
|
||||
},
|
||||
|
||||
discard : function(){
|
||||
var element = this,
|
||||
parentWidth = this.$el.parent().width();
|
||||
this.$el.animate({ 'margin-right' : parentWidth }, 'fast', function(){
|
||||
element.trigger( 'discard', {
|
||||
source : element
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
events : {
|
||||
'click' : '_click',
|
||||
'click .name' : '_clickName',
|
||||
'click .discard': '_clickDiscard',
|
||||
|
||||
'dragstart' : '_dragstart',
|
||||
'dragend' : '_dragend',
|
||||
'dragover' : '_sendToParent',
|
||||
'drop' : '_sendToParent'
|
||||
},
|
||||
|
||||
_click : function( ev ){
|
||||
ev.stopPropagation();
|
||||
this.select( ev );
|
||||
},
|
||||
|
||||
/** rename a pair when the pair name is clicked */
|
||||
_clickName : function( ev ){
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
var response = prompt( _l( 'Enter a new name for the element' ) + ':', this.element.name );
|
||||
if( response ){
|
||||
this.element.name = response;
|
||||
this.render();
|
||||
}
|
||||
//TODO: cancelling with ESC leads to closure of the creator...
|
||||
},
|
||||
|
||||
_clickDiscard : function( ev ){
|
||||
ev.stopPropagation();
|
||||
this.discard();
|
||||
},
|
||||
|
||||
/** dragging pairs for re-ordering */
|
||||
_dragstart : function( ev ){
|
||||
if( ev.originalEvent ){ ev = ev.originalEvent; }
|
||||
ev.dataTransfer.effectAllowed = 'move';
|
||||
ev.dataTransfer.setData( 'text/plain', JSON.stringify( this.element ) );
|
||||
|
||||
this.$el.addClass( 'dragging' );
|
||||
this.$el.parent().trigger( 'collection-element.dragstart', [ this ] );
|
||||
},
|
||||
|
||||
/** dragging pairs for re-ordering */
|
||||
_dragend : function( ev ){
|
||||
this.$el.removeClass( 'dragging' );
|
||||
this.$el.parent().trigger( 'collection-element.dragend', [ this ] );
|
||||
},
|
||||
|
||||
/** manually bubble up an event to the parent/container */
|
||||
_sendToParent : function( ev ){
|
||||
this.$el.parent().trigger( ev );
|
||||
},
|
||||
|
||||
/** string rep */
|
||||
toString : function(){
|
||||
return 'DatasetCollectionElementView()';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// ============================================================================
|
||||
/** An interface for building collections.
|
||||
*/
|
||||
var ListCollectionCreator = Backbone.View.extend( BASE_MVC.LoggableMixin ).extend({
|
||||
|
||||
/** the class this creator will create and save */
|
||||
collectionClass : HDCA.HistoryListDatasetCollection,
|
||||
className : 'list-collection-creator collection-creator flex-row-container',
|
||||
|
||||
/** set up initial options, instance vars, behaviors */
|
||||
initialize : function( attributes ){
|
||||
this.metric( 'ListCollectionCreator.initialize', attributes );
|
||||
|
||||
attributes = _.defaults( attributes, {
|
||||
elements : [],
|
||||
creationFn : function(){ throw new TypeError( 'no creation fn for creator' ); }
|
||||
});
|
||||
|
||||
/** unordered, original list - cache to allow reversal */
|
||||
this.initialElements = attributes.elements;
|
||||
/** a list of invalid elements and the reasons they aren't valid */
|
||||
//TODO: display invalid and allow the user to move forward
|
||||
this.invalidElements = [];
|
||||
/** data for list in progress */
|
||||
this.workingElements = [];
|
||||
/** views for workingElements */
|
||||
this.elementViews = [];
|
||||
|
||||
//TODO: remove - use new collectionClass().save()
|
||||
/** takes elements and creates the proper collection - returns a promise */
|
||||
this.creationFn = attributes.creationFn;
|
||||
/** fn to call when the collection is created (scoped to this) */
|
||||
this.oncreate = attributes.oncreate;
|
||||
/** fn to call when the cancel button is clicked (scoped to this) - if falsy, no btn is displayed */
|
||||
this.oncancel = attributes.oncancel;
|
||||
|
||||
/** fn to call when the cancel button is clicked (scoped to this) - if falsy, no btn is displayed */
|
||||
this.autoscrollDist = attributes.autoscrollDist || 24;
|
||||
/** Ids of elements that have been selected by the user - to preserve over renders */
|
||||
this.selectedIds = {};
|
||||
/** DOM elements currently being dragged */
|
||||
this.$dragging = null;
|
||||
/** Used for blocking UI events during ajax/operations (don't post twice) */
|
||||
this.blocking = false;
|
||||
|
||||
this._setUpBehaviors();
|
||||
this._elementsSetUp();
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ process raw list
|
||||
/** set up main data */
|
||||
_elementsSetUp : function(){
|
||||
//this.debug( '-- _dataSetUp' );
|
||||
|
||||
// copy initial list, sort, add ids if needed
|
||||
this.workingElements = this.initialElements.slice( 0 );
|
||||
this._ensureElementIds();
|
||||
this._validateElements();
|
||||
this._sortElements();
|
||||
},
|
||||
|
||||
/** add ids to dataset objs in initial list if none */
|
||||
_ensureElementIds : function(){
|
||||
this.workingElements.forEach( function( element ){
|
||||
if( !element.hasOwnProperty( 'id' ) ){
|
||||
element.id = _.uniqueId();
|
||||
}
|
||||
});
|
||||
if( !elementIdentifiers.length ){
|
||||
noValidElementsErrorModal();
|
||||
return jQuery.Deferred().reject( noValidElementsMessage );
|
||||
}
|
||||
return collection.createHDCA( elementIdentifiers, 'list', name );
|
||||
return this.workingElements;
|
||||
},
|
||||
|
||||
/** separate working list into valid and invalid elements for this collection */
|
||||
_validateElements : function(){
|
||||
var creator = this;
|
||||
creator.invalidElements = [];
|
||||
|
||||
this.workingElements = this.workingElements.filter( function( element ){
|
||||
var problem = creator._isElementInvalid( element );
|
||||
if( problem ){
|
||||
creator.invalidElements.push({
|
||||
element : element,
|
||||
text : problem
|
||||
});
|
||||
}
|
||||
return !problem;
|
||||
});
|
||||
return this.workingElements;
|
||||
},
|
||||
|
||||
/** separate working list into valid and invalid elements for this collection */
|
||||
_isElementInvalid : function( element ){
|
||||
if( element.state !== STATES.OK ){
|
||||
if( _.contains( STATES.NOT_READY_STATES, element.state ) ){
|
||||
return _l( "hasn't finished running yet" );
|
||||
}
|
||||
return _l( "has errored, is paused, or is not accessible" );
|
||||
}
|
||||
if( element.history_content_type !== 'dataset' ){
|
||||
return _l( "is not a dataset" );
|
||||
}
|
||||
if( element.deleted || element.purged ){
|
||||
return _l( "has been deleted or purged" );
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/** sort a list of datasets */
|
||||
_sortElements : function( list ){
|
||||
// currently only natural sort by name
|
||||
this.workingElements.sort( function( a, b ){ return naturalSort( a.name, b.name ); });
|
||||
return this.workingElements;
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ rendering
|
||||
/** render the entire interface */
|
||||
render : function( speed, callback ){
|
||||
//this.debug( '-- _render' );
|
||||
this.$el.empty().html( ListCollectionCreator.templates.main() );
|
||||
this._renderHeader( speed );
|
||||
this._renderMiddle( speed );
|
||||
this._renderFooter( speed );
|
||||
this._addPluginComponents();
|
||||
this.trigger( 'rendered', this );
|
||||
return this;
|
||||
},
|
||||
|
||||
/** render the header section */
|
||||
_renderHeader : function( speed, callback ){
|
||||
var $header = this.$( '.header' ).empty().html( ListCollectionCreator.templates.header() )
|
||||
.find( '.help-content' ).prepend( $( ListCollectionCreator.templates.helpContent() ) );
|
||||
//TODO: should only show once despite calling _renderHeader again
|
||||
if( this.invalidElements.length ){
|
||||
this._invalidElementsAlert();
|
||||
}
|
||||
return $header;
|
||||
},
|
||||
|
||||
/** render the middle including the elements */
|
||||
_renderMiddle : function( speed, callback ){
|
||||
var $middle = this.$( '.middle' ).empty().html( ListCollectionCreator.templates.middle() );
|
||||
this._renderList( speed );
|
||||
return $middle;
|
||||
},
|
||||
|
||||
/** render the footer, completion controls, and cancel controls */
|
||||
_renderFooter : function( speed, callback ){
|
||||
var $footer = this.$( '.footer' ).empty().html( ListCollectionCreator.templates.footer() );
|
||||
if( typeof this.oncancel === 'function' ){
|
||||
this.$( '.cancel-create.btn' ).show();
|
||||
}
|
||||
return $footer;
|
||||
},
|
||||
|
||||
/** add any jQuery/bootstrap/custom plugins to elements rendered */
|
||||
_addPluginComponents : function(){
|
||||
this.$( '.help-content i' ).hoverhighlight( '.collection-creator', 'rgba( 64, 255, 255, 1.0 )' );
|
||||
},
|
||||
|
||||
/** add (or clear if clear is truthy) a validation warning to what */
|
||||
_validationWarning : function( what, clear ){
|
||||
var VALIDATION_CLASS = 'validation-warning';
|
||||
if( what === 'name' ){
|
||||
what = this.$( '.collection-name' ).add( this.$( '.collection-name-prompt' ) );
|
||||
this.$( '.collection-name' ).focus().select();
|
||||
}
|
||||
if( clear ){
|
||||
what = what || this.$( '.' + VALIDATION_CLASS );
|
||||
what.removeClass( VALIDATION_CLASS );
|
||||
} else {
|
||||
what.addClass( VALIDATION_CLASS );
|
||||
}
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ rendering elements
|
||||
/** */
|
||||
$list : function(){
|
||||
return this.$( '.collection-elements' );
|
||||
},
|
||||
|
||||
/** */
|
||||
_invalidElementsAlert : function(){
|
||||
var message = _l( 'The following selections could not be included due to problems' ) + ':';
|
||||
message += '<ul>';
|
||||
message += this.invalidElements.map( function( problem ){
|
||||
return [
|
||||
'<li><b>', problem.element.name, '</b>: ', problem.text, '</li>'
|
||||
].join( '' );
|
||||
}).join( '' );
|
||||
message += '</ul>';
|
||||
this._showAlert( message, 'alert-warning' );
|
||||
},
|
||||
|
||||
/** */
|
||||
_renderClearSelected : function(){
|
||||
if( _.size( this.selectedIds ) ){
|
||||
this.$( '.collection-elements-controls > .clear-selected' ).show();
|
||||
} else {
|
||||
this.$( '.collection-elements-controls > .clear-selected' ).hide();
|
||||
}
|
||||
},
|
||||
|
||||
/** */
|
||||
_renderList : function( speed, callback ){
|
||||
//this.debug( '-- _renderList' );
|
||||
var creator = this,
|
||||
$tmp = jQuery( '<div/>' );
|
||||
creator.workingElements.forEach( function( element ){
|
||||
var elementView = creator._createElementView( element );
|
||||
$tmp.append( elementView.$el );
|
||||
});
|
||||
|
||||
creator._renderClearSelected();
|
||||
creator.$list().empty().append( $tmp.children() );
|
||||
_.invoke( creator.elementViews, 'render' );
|
||||
},
|
||||
|
||||
/** */
|
||||
_createElementView : function( element ){
|
||||
var elementView = new DatasetCollectionElementView({
|
||||
//TODO: use non-generic class or not all
|
||||
// model : COLLECTION.DatasetDCE( element )
|
||||
element : element,
|
||||
selected: _.has( this.selectedIds, element.id )
|
||||
});
|
||||
this.elementViews.push( elementView );
|
||||
this._listenToElementView( elementView );
|
||||
return elementView;
|
||||
},
|
||||
|
||||
_listenToElementView : function( view ){
|
||||
var creator = this;
|
||||
creator.listenTo( view, {
|
||||
select : function( data ){
|
||||
var element = data.source.element;
|
||||
if( data.selected ){
|
||||
creator.selectedIds[ element.id ] = true;
|
||||
} else {
|
||||
delete creator.selectedIds[ element.id ];
|
||||
}
|
||||
creator.trigger( 'elements:select', data );
|
||||
},
|
||||
discard : function( data ){
|
||||
creator.trigger( 'elements:discard', data );
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/** */
|
||||
addElementView : function( element ){
|
||||
// workingElements is sorted, add element in appropo index
|
||||
// add element, sort elements, find element index
|
||||
var view = this._createElementView( element );
|
||||
return view;
|
||||
},
|
||||
|
||||
/** */
|
||||
removeElementView : function( view ){
|
||||
view.$el.remove();
|
||||
|
||||
delete this.selectedIds[ view.element.id ];
|
||||
this._renderClearSelected();
|
||||
|
||||
this.elementViews = _.without( this.elementViews, view );
|
||||
this.stopListening( view );
|
||||
view.off();
|
||||
},
|
||||
|
||||
_renderNoElementsLeft : function(){
|
||||
var $message = $( '<li class="no-elements-left-message"></li>' ),
|
||||
$startOverLink = $( '<a class="reset" href="javascript:void(0)"></a>' );
|
||||
$message.html([
|
||||
_l( 'No elements left' ), '<br />', _l( 'Would you like to ' ),
|
||||
'<a class="reset" href="javascript:void(0)">', _l( 'start over' ), '</a>?'
|
||||
].join( '' ));
|
||||
this.$( '.collection-elements' ).append( $message );
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ API
|
||||
/** convert element into JSON compatible with the collections API */
|
||||
_elementToJSON : function( element ){
|
||||
// return element.toJSON();
|
||||
return element;
|
||||
},
|
||||
|
||||
/** create the collection via the API
|
||||
* @returns {jQuery.xhr Object} the jquery ajax request
|
||||
*/
|
||||
createList : function( name ){
|
||||
var creator = this,
|
||||
elements = this.workingElements.map( function( element ){
|
||||
return creator._elementToJSON( element );
|
||||
});
|
||||
|
||||
creator.blocking = true;
|
||||
return creator.creationFn( elements, name )
|
||||
.always( function(){
|
||||
creator.blocking = false;
|
||||
})
|
||||
.fail( function( xhr, status, message ){
|
||||
creator.trigger( 'error', {
|
||||
xhr : xhr,
|
||||
status : status,
|
||||
message : _l( 'An error occurred while creating this collection' )
|
||||
});
|
||||
})
|
||||
.done( function( response, message, xhr ){
|
||||
creator.trigger( 'collection:created', response, message, xhr );
|
||||
creator.metric( 'collection:created', response );
|
||||
if( typeof creator.oncreate === 'function' ){
|
||||
creator.oncreate.call( this, response, message, xhr );
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ events
|
||||
/** set up event handlers on self */
|
||||
_setUpBehaviors : function(){
|
||||
this.on( 'error', this._errorHandler );
|
||||
|
||||
this.once( 'rendered', function(){
|
||||
this.trigger( 'rendered:initial', this );
|
||||
});
|
||||
|
||||
this.on( 'elements:select', function( data ){
|
||||
this._renderClearSelected();
|
||||
});
|
||||
|
||||
this.on( 'elements:discard', function( data ){
|
||||
var element = data.source.element;
|
||||
this.removeElementView( data.source );
|
||||
|
||||
this.workingElements = _.without( this.workingElements, element );
|
||||
if( !this.workingElements.length ){
|
||||
this._renderNoElementsLeft();
|
||||
}
|
||||
});
|
||||
|
||||
//this.on( 'all', function(){
|
||||
// this.info( arguments );
|
||||
//});
|
||||
return this;
|
||||
},
|
||||
|
||||
/** handle errors with feedback and details to the user (if available) */
|
||||
_errorHandler : function( data ){
|
||||
this.error( data );
|
||||
|
||||
var creator = this;
|
||||
content = data.message || _l( 'An error occurred' );
|
||||
if( data.xhr ){
|
||||
var xhr = data.xhr,
|
||||
message = data.message;
|
||||
if( xhr.readyState === 0 && xhr.status === 0 ){
|
||||
content += ': ' + _l( 'Galaxy could not be reached and may be updating.' ) +
|
||||
_l( ' Try again in a few minutes.' );
|
||||
} else if( xhr.responseJSON ){
|
||||
content += ':<br /><pre>' + JSON.stringify( xhr.responseJSON ) + '</pre>';
|
||||
} else {
|
||||
content += ': ' + message;
|
||||
}
|
||||
}
|
||||
creator._showAlert( content, 'alert-danger' );
|
||||
},
|
||||
|
||||
events : {
|
||||
// header
|
||||
'click .more-help' : '_clickMoreHelp',
|
||||
'click .less-help' : '_clickLessHelp',
|
||||
'click .main-help' : '_toggleHelp',
|
||||
'click .header .alert button' : '_hideAlert',
|
||||
|
||||
'click .reset' : 'reset',
|
||||
'click .clear-selected' : 'clearSelectedElements',
|
||||
|
||||
// elements - selection
|
||||
//TODO: this is not enough area - no affordance
|
||||
'click .collection-elements' : 'clearSelectedElements',
|
||||
|
||||
// elements - drop target
|
||||
// 'dragenter .collection-elements': '_dragenterElements',
|
||||
// 'dragleave .collection-elements': '_dragleaveElements',
|
||||
'dragover .collection-elements' : '_dragoverElements',
|
||||
'drop .collection-elements' : '_dropElements',
|
||||
|
||||
// these bubble up from the elements as custom events
|
||||
'collection-element.dragstart .collection-elements' : '_elementDragstart',
|
||||
'collection-element.dragend .collection-elements' : '_elementDragend',
|
||||
|
||||
// footer
|
||||
'change .collection-name' : '_changeName',
|
||||
'keydown .collection-name' : '_nameCheckForEnter',
|
||||
'click .cancel-create' : function( ev ){
|
||||
if( typeof this.oncancel === 'function' ){
|
||||
this.oncancel.call( this );
|
||||
}
|
||||
},
|
||||
'click .create-collection' : '_clickCreate'//,
|
||||
},
|
||||
|
||||
// ........................................................................ header
|
||||
/** expand help */
|
||||
_clickMoreHelp : function( ev ){
|
||||
ev.stopPropagation();
|
||||
this.$( '.main-help' ).addClass( 'expanded' );
|
||||
this.$( '.more-help' ).hide();
|
||||
},
|
||||
/** collapse help */
|
||||
_clickLessHelp : function( ev ){
|
||||
ev.stopPropagation();
|
||||
this.$( '.main-help' ).removeClass( 'expanded' );
|
||||
this.$( '.more-help' ).show();
|
||||
},
|
||||
/** toggle help */
|
||||
_toggleHelp : function( ev ){
|
||||
ev.stopPropagation();
|
||||
this.$( '.main-help' ).toggleClass( 'expanded' );
|
||||
this.$( '.more-help' ).toggle();
|
||||
},
|
||||
|
||||
/** show an alert on the top of the interface containing message (alertClass is bootstrap's alert-*)*/
|
||||
_showAlert : function( message, alertClass ){
|
||||
alertClass = alertClass || 'alert-danger';
|
||||
this.$( '.main-help' ).hide();
|
||||
this.$( '.header .alert' ).attr( 'class', 'alert alert-dismissable' ).addClass( alertClass ).show()
|
||||
.find( '.alert-message' ).html( message );
|
||||
},
|
||||
/** hide the alerts at the top */
|
||||
_hideAlert : function( message ){
|
||||
this.$( '.main-help' ).show();
|
||||
this.$( '.header .alert' ).hide();
|
||||
},
|
||||
|
||||
// ........................................................................ elements
|
||||
reset : function(){
|
||||
this.selectedIds = {};
|
||||
this.$dragging = null;
|
||||
this.blocking = false;
|
||||
this._elementsSetUp();
|
||||
this.render();
|
||||
},
|
||||
|
||||
/** deselect all pairs */
|
||||
clearSelectedElements : function( ev ){
|
||||
this.$( '.collection-elements .collection-element' ).removeClass( 'selected' );
|
||||
this.$( '.collection-elements-controls > .clear-selected' ).hide();
|
||||
},
|
||||
|
||||
//_dragenterElements : function( ev ){
|
||||
// this.debug( '_dragenterElements:', ev );
|
||||
//},
|
||||
_dragleaveElements : function( ev ){
|
||||
//this.debug( '_dragleaveElements:', ev );
|
||||
//TODO: if selected are dragged out of the list area - remove the placeholder - cuz it won't work anyway
|
||||
},
|
||||
|
||||
/** track the mouse drag over the paired list adding a placeholder to show where the drop would occur */
|
||||
_dragoverElements : function( ev ){
|
||||
//this.debug( '_dragoverElements:', ev );
|
||||
ev.preventDefault();
|
||||
|
||||
var $list = this.$list();
|
||||
this._checkForAutoscroll( $list, ev.originalEvent.clientY );
|
||||
var $nearest = this._getNearestElement( ev.originalEvent.clientY );
|
||||
|
||||
//TODO: no need to re-create - move instead
|
||||
this.$( '.element-drop-placeholder' ).remove();
|
||||
var $placeholder = $( '<div class="element-drop-placeholder"></div>' );
|
||||
if( !$nearest.size() ){
|
||||
$list.append( $placeholder );
|
||||
} else {
|
||||
$nearest.before( $placeholder );
|
||||
}
|
||||
},
|
||||
|
||||
/** If the mouse is near enough to the list's top or bottom, scroll the list */
|
||||
_checkForAutoscroll : function( $element, y ){
|
||||
var AUTOSCROLL_SPEED = 2,
|
||||
offset = $element.offset(),
|
||||
scrollTop = $element.scrollTop(),
|
||||
upperDist = y - offset.top,
|
||||
lowerDist = ( offset.top + $element.outerHeight() ) - y;
|
||||
if( upperDist >= 0 && upperDist < this.autoscrollDist ){
|
||||
$element.scrollTop( scrollTop - AUTOSCROLL_SPEED );
|
||||
} else if( lowerDist >= 0 && lowerDist < this.autoscrollDist ){
|
||||
$element.scrollTop( scrollTop + AUTOSCROLL_SPEED );
|
||||
}
|
||||
},
|
||||
|
||||
/** get the nearest element based on the mouse's Y coordinate.
|
||||
* If the y is at the end of the list, return an empty jQuery object.
|
||||
*/
|
||||
_getNearestElement : function( y ){
|
||||
var WIGGLE = 4,
|
||||
lis = this.$( '.collection-elements li.collection-element' ).toArray();
|
||||
for( var i=0; i<lis.length; i++ ){
|
||||
var $li = $( lis[i] ),
|
||||
top = $li.offset().top,
|
||||
halfHeight = Math.floor( $li.outerHeight() / 2 ) + WIGGLE;
|
||||
if( top + halfHeight > y && top - halfHeight < y ){
|
||||
//this.debug( y, top + halfHeight, top - halfHeight )
|
||||
return $li;
|
||||
}
|
||||
}
|
||||
return $();
|
||||
},
|
||||
|
||||
/** drop (dragged/selected elements) onto the list, re-ordering the internal list */
|
||||
_dropElements : function( ev ){
|
||||
if( ev.originalEvent ){ ev = ev.originalEvent; }
|
||||
// both required for firefox
|
||||
ev.preventDefault();
|
||||
ev.dataTransfer.dropEffect = 'move';
|
||||
|
||||
// remove the placeholder if any and insert before the nearest element or after the last.
|
||||
var $nearest = this._getNearestElement( ev.clientY );
|
||||
if( $nearest.size() ){
|
||||
this.$dragging.insertBefore( $nearest );
|
||||
} else {
|
||||
// no nearest before - insert after last element (unpair button)
|
||||
this.$dragging.insertAfter( this.$( '.collection-elements .collection-element' ).last() );
|
||||
}
|
||||
// resync the creator's list of paired based on the new DOM order
|
||||
this._syncOrderToDom();
|
||||
return false;
|
||||
},
|
||||
|
||||
/** resync the creator's list of elements based on the DOM order */
|
||||
_syncOrderToDom : function(){
|
||||
var creator = this,
|
||||
newElements = [];
|
||||
//TODO: doesn't seem wise to use the dom to store these - can't we sync another way?
|
||||
this.$( '.collection-elements .collection-element' ).each( function(){
|
||||
var id = $( this ).attr( 'data-element-id' ),
|
||||
element = _.findWhere( creator.workingElements, { id: id });
|
||||
if( element ){
|
||||
newElements.push( element );
|
||||
} else {
|
||||
console.error( 'missing element: ', id );
|
||||
}
|
||||
});
|
||||
this.workingElements = newElements;
|
||||
this._renderList();
|
||||
},
|
||||
|
||||
/** drag communication with element sub-views: dragstart */
|
||||
_elementDragstart : function( ev, element ){
|
||||
// auto select the element causing the event and move all selected
|
||||
element.select( true );
|
||||
this.$dragging = this.$( '.collection-elements .collection-element.selected' );
|
||||
},
|
||||
|
||||
/** drag communication with element sub-views: dragend - remove the placeholder */
|
||||
_elementDragend : function( ev, element ){
|
||||
$( '.element-drop-placeholder' ).remove();
|
||||
this.$dragging = null;
|
||||
},
|
||||
|
||||
// ........................................................................ footer
|
||||
/** handle a collection name change */
|
||||
_changeName : function( ev ){
|
||||
this._validationWarning( 'name', !!this._getName() );
|
||||
},
|
||||
|
||||
/** check for enter key press when in the collection name and submit */
|
||||
_nameCheckForEnter : function( ev ){
|
||||
if( ev.keyCode === 13 && !this.blocking ){
|
||||
this._clickCreate();
|
||||
}
|
||||
},
|
||||
|
||||
/** get the current collection name */
|
||||
_getName : function(){
|
||||
return _.escape( this.$( '.collection-name' ).val() );
|
||||
},
|
||||
|
||||
/** attempt to create the current collection */
|
||||
_clickCreate : function( ev ){
|
||||
var name = this._getName();
|
||||
if( !name ){
|
||||
this._validationWarning( 'name' );
|
||||
} else if( !this.blocking ){
|
||||
this.createList( name );
|
||||
}
|
||||
},
|
||||
|
||||
// ------------------------------------------------------------------------ misc
|
||||
/** string rep */
|
||||
toString : function(){ return 'ListCollectionCreator'; }
|
||||
});
|
||||
|
||||
|
||||
//TODO: move to require text plugin and load these as text
|
||||
//TODO: underscore currently unnecc. bc no vars are used
|
||||
//TODO: better way of localizing text-nodes in long strings
|
||||
/** underscore template fns attached to class */
|
||||
ListCollectionCreator.templates = ListCollectionCreator.templates || {
|
||||
|
||||
/** the skeleton */
|
||||
main : _.template([
|
||||
'<div class="header flex-row no-flex"></div>',
|
||||
'<div class="middle flex-row flex-row-container"></div>',
|
||||
'<div class="footer flex-row no-flex">'
|
||||
].join('')),
|
||||
|
||||
/** the header (not including help text) */
|
||||
header : _.template([
|
||||
'<div class="main-help well clear">',
|
||||
'<a class="more-help" href="javascript:void(0);">', _l( 'More help' ), '</a>',
|
||||
'<div class="help-content">',
|
||||
'<a class="less-help" href="javascript:void(0);">', _l( 'Less' ), '</a>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
'<div class="alert alert-dismissable">',
|
||||
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>',
|
||||
'<span class="alert-message"></span>',
|
||||
'</div>',
|
||||
].join('')),
|
||||
|
||||
/** the middle: unpaired, divider, and paired */
|
||||
middle : _.template([
|
||||
'<div class="collection-elements-controls">',
|
||||
'<a class="reset" href="javascript:void(0);" title="', _l( 'Undo all reordering and discards' ), '">',
|
||||
_l( 'Start over' ),
|
||||
'</a>',
|
||||
'<a class="clear-selected" href="javascript:void(0);" title="', _l( 'De-select all selected datasets' ), '">',
|
||||
_l( 'Clear selected' ),
|
||||
'</a>',
|
||||
'</div>',
|
||||
// contains two flex rows (rows that fill available space) and a divider btwn
|
||||
'<div class="collection-elements scroll-container flex-row">',
|
||||
'</div>'
|
||||
].join('')),
|
||||
|
||||
/** creation and cancel controls */
|
||||
footer : _.template([
|
||||
'<div class="attributes clear">',
|
||||
'<div class="clear">',
|
||||
'<input class="collection-name form-control pull-right" ',
|
||||
'placeholder="', _l( 'Enter a name for your new list' ), '" />',
|
||||
'<div class="collection-name-prompt pull-right">', _l( 'Name' ), ':</div>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
|
||||
'<div class="actions clear vertically-spaced">',
|
||||
'<div class="other-options pull-left">',
|
||||
'<button class="cancel-create btn" tabindex="-1">', _l( 'Cancel' ), '</button>',
|
||||
'<div class="create-other btn-group dropup">',
|
||||
'<button class="btn btn-default dropdown-toggle" data-toggle="dropdown">',
|
||||
_l( 'Create a different kind of collection' ),
|
||||
' <span class="caret"></span>',
|
||||
'</button>',
|
||||
'<ul class="dropdown-menu" role="menu">',
|
||||
'<li><a href="#">', _l( 'Create a <i>single</i> pair' ), '</a></li>',
|
||||
'<li><a href="#">', _l( 'Create a list of <i>unpaired</i> datasets' ), '</a></li>',
|
||||
'</ul>',
|
||||
'</div>',
|
||||
'</div>',
|
||||
|
||||
'<div class="main-options pull-right">',
|
||||
'<button class="create-collection btn btn-primary">', _l( 'Create list' ), '</button>',
|
||||
'</div>',
|
||||
'</div>'
|
||||
].join('')),
|
||||
|
||||
/** help content */
|
||||
helpContent : _.template([
|
||||
'<p>', _l([
|
||||
'Collections of datasets are permanent, ordered lists of datasets that can be passed to tools and ',
|
||||
'workflows in order to have analyses done on each member of the entire group. This interface allows ',
|
||||
'you to create a collection and re-order the final collection.'
|
||||
].join( '' )), '</p>',
|
||||
'<ul>',
|
||||
'<li>', _l([
|
||||
'Rename elements in the list by clicking on ',
|
||||
'<i data-target=".collection-element .name">the existing name</i>.'
|
||||
].join( '' )), '</li>',
|
||||
'<li>', _l([
|
||||
'Discard elements from the final created list by clicking on the ',
|
||||
'<i data-target=".collection-element .discard">"Discard"</i> button.'
|
||||
].join( '' )), '</li>',
|
||||
'<li>', _l([
|
||||
'Reorder the list by clicking and dragging elements. Select multiple elements by clicking on ',
|
||||
'<i data-target=".collection-element">them</i> and you can then move those selected by dragging the ',
|
||||
'entire group. Deselect them by clicking them again or by clicking the ',
|
||||
'the <i data-target=".clear-selected">"Clear selected"</i> link.'
|
||||
].join( '' )), '</li>',
|
||||
'<li>', _l([
|
||||
'Click the <i data-target=".reset">"Start over"</i> link to begin again as if you had just opened ',
|
||||
'the interface.'
|
||||
].join( '' )), '</li>',
|
||||
'<li>', _l([
|
||||
'Click the <i data-target=".cancel-create">"Cancel"</i> button to exit the interface.'
|
||||
].join( '' )), '</li>',
|
||||
'</ul><br />',
|
||||
'<p>', _l([
|
||||
'Once your collection is complete, enter a <i data-target=".collection-name">name</i> and ',
|
||||
'click <i data-target=".create-collection">"Create list"</i>.'
|
||||
].join( '' )), '</p>'
|
||||
].join(''))
|
||||
};
|
||||
|
||||
|
||||
//=============================================================================
|
||||
(function(){
|
||||
/** plugin that will highlight an element when this element is hovered over */
|
||||
jQuery.fn.extend({
|
||||
hoverhighlight : function $hoverhighlight( scope, color ){
|
||||
scope = scope || 'body';
|
||||
if( !this.size() ){ return this; }
|
||||
|
||||
$( this ).each( function(){
|
||||
var $this = $( this ),
|
||||
targetSelector = $this.data( 'target' );
|
||||
|
||||
if( targetSelector ){
|
||||
$this.mouseover( function( ev ){
|
||||
$( targetSelector, scope ).css({
|
||||
background: color
|
||||
});
|
||||
})
|
||||
.mouseout( function( ev ){
|
||||
$( targetSelector ).css({
|
||||
background: ''
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
|
||||
//=============================================================================
|
||||
var collectionCreatorModal = function _collectionCreatorModal( elements, options, CreatorClass ){
|
||||
|
||||
var deferred = jQuery.Deferred(),
|
||||
modal = Galaxy.modal || ( new UI_MODAL.View() ),
|
||||
creator;
|
||||
|
||||
//TODO: handle no elements here
|
||||
|
||||
options = _.defaults( options || {}, {
|
||||
elements : elements,
|
||||
oncancel : function(){
|
||||
modal.hide();
|
||||
deferred.reject( 'cancelled' );
|
||||
},
|
||||
oncreate : function( creator, response ){
|
||||
modal.hide();
|
||||
deferred.resolve( response );
|
||||
}
|
||||
});
|
||||
|
||||
creator = new CreatorClass( options );
|
||||
modal.show({
|
||||
title : _l( 'Create a collection from a list of datasets' ),
|
||||
body : creator.$el,
|
||||
width : '80%',
|
||||
height : 'min-content',
|
||||
closing_events: true
|
||||
});
|
||||
creator.render();
|
||||
window._collectionCreator = creator;
|
||||
|
||||
//TODO: remove modal header
|
||||
return deferred;
|
||||
};
|
||||
|
||||
var listCollectionCreatorModal = function _listCollectionCreatorModal( elements, options ){
|
||||
return collectionCreatorModal( elements, options, ListCollectionCreator );
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
function createListCollection( collection ){
|
||||
var elements = collection.toJSON(),
|
||||
promise = listCollectionCreatorModal( elements, {
|
||||
creationFn : function( elements, name ){
|
||||
// var list = this.workingElements.map( function( element ){
|
||||
// return element.name;
|
||||
// });
|
||||
// alert( list.join( '\n' ) );
|
||||
// return jQuery.Deferred().reject();
|
||||
|
||||
elements = elements.map( function( element ){
|
||||
//TODO: Handle duplicate names.
|
||||
return {
|
||||
id : element.id,
|
||||
name : element.name,
|
||||
//TODO: this allows for list:list even if the filter above does not - reconcile
|
||||
src : ( element.history_content_type === 'dataset'? 'hda' : 'hdca' )
|
||||
};
|
||||
});
|
||||
return collection.createHDCA( elements, 'list', name );
|
||||
}
|
||||
});
|
||||
promise.done( function( response ){
|
||||
console.debug( 'createListCollection, done:', response );
|
||||
});
|
||||
promise.fail( function( response ){
|
||||
console.error( 'createListCollection, fail:', arguments );
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
return {
|
||||
validElements : validElements,
|
||||
noValidElementsMessage : noValidElementsMessage,
|
||||
noValidElementsErrorModal : noValidElementsErrorModal,
|
||||
createListHDCA : createListHDCA
|
||||
ListCollectionCreator : ListCollectionCreator,
|
||||
listCollectionCreatorModal : listCollectionCreatorModal,
|
||||
|
||||
createListCollection : createListCollection
|
||||
};
|
||||
});
|
||||
|
||||
@@ -185,7 +185,7 @@ function autoPairFnBuilder( options ){
|
||||
*/
|
||||
var PairedCollectionCreator = Backbone.View.extend( baseMVC.LoggableMixin ).extend({
|
||||
|
||||
className: 'collection-creator flex-row-container',
|
||||
className: 'list-of-pairs-collection-creator collection-creator flex-row-container',
|
||||
|
||||
/** set up initial options, instance vars, behaviors, and autopair (if set to do so) */
|
||||
initialize : function( attributes ){
|
||||
@@ -1728,12 +1728,8 @@ var pairedCollectionCreatorModal = function _pairedCollectionCreatorModal( datas
|
||||
|
||||
//=============================================================================
|
||||
function createListOfPairsCollection( collection ){
|
||||
var elements = LIST_COLLECTION_CREATOR.validElements( collection );
|
||||
if( !elements.length ){
|
||||
LIST_COLLECTION_CREATOR.noValidElementsErrorModal();
|
||||
return jQuery.Deferred().reject( LIST_COLLECTION_CREATOR.noValidElementsMessage );
|
||||
}
|
||||
|
||||
var elements = collection.toJSON();
|
||||
//TODO: validate elements
|
||||
return pairedCollectionCreatorModal( elements, {
|
||||
historyId : collection.historyId
|
||||
});
|
||||
|
||||
@@ -287,7 +287,7 @@ var HistoryPanelEdit = _super.extend(
|
||||
var panel = this;
|
||||
return [
|
||||
{ html: _l( 'Build Dataset List' ), func: function() {
|
||||
LIST_COLLECTION_CREATOR.createListHDCA( panel.getSelectedModels() )
|
||||
LIST_COLLECTION_CREATOR.createListCollection( panel.getSelectedModels() )
|
||||
.done( function(){ panel.model.refresh() });
|
||||
}
|
||||
},
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2220,13 +2220,13 @@ pre.peek table,pre.peek th,pre.peek tr,pre.peek td{font-family:Monaco,Menlo,Cons
|
||||
.collection-creator .flex-column.no-flex{-webkit-flex:0 auto;-ms-flex:0 auto;flex:0 0 auto}
|
||||
.collection-creator .choose-filters .help{margin-bottom:2px;font-size:90%;color:grey}
|
||||
.collection-creator .choose-filters button{width:100%;margin-top:2px}
|
||||
.collection-creator .header .alert{display:none}
|
||||
.collection-creator .header .alert{display:none}.collection-creator .header .alert li{list-style:circle;margin-left:32px}
|
||||
.collection-creator .column{width:30%}
|
||||
.collection-creator .column-title{height:22px;line-height:22px;overflow:hidden}.collection-creator .column-title:hover{text-decoration:underline;cursor:pointer}
|
||||
.collection-creator .column-title .title{font-weight:bold}
|
||||
.collection-creator .column-title .title-info{color:grey}.collection-creator .column-title .title-info:before{content:' - '}
|
||||
.collection-creator .paired-column{text-align:center;width:22%}
|
||||
.collection-creator .header{padding-bottom:8px}.collection-creator .header .main-help{margin-bottom:17px;overflow:hidden;padding:15px;background:#f8f8f8}.collection-creator .header .main-help:not(.expanded){max-height:49px}.collection-creator .header .main-help:not(.expanded) .help-content p:first-child{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
|
||||
.collection-creator .header .main-help{margin-bottom:17px;overflow:hidden;padding:15px;background:#f8f8f8}.collection-creator .header .main-help:not(.expanded){max-height:49px}.collection-creator .header .main-help:not(.expanded) .help-content p:first-child{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
|
||||
.collection-creator .header .main-help:not(.expanded) .help-content>*:not(:first-child){display:none}
|
||||
.collection-creator .header .main-help.expanded{max-height:none}
|
||||
.collection-creator .header .main-help .help-content i{cursor:help;border-bottom:1px dotted grey;font-style:normal}
|
||||
@@ -2263,6 +2263,21 @@ pre.peek table,pre.peek th,pre.peek tr,pre.peek td{font-family:Monaco,Menlo,Cons
|
||||
.collection-creator .footer .attributes .collection-name-prompt.validation-warning:before{content:'(required)';margin-right:4px;color:red}
|
||||
.collection-creator .footer .attributes .collection-name{width:50%}.collection-creator .footer .attributes .collection-name.validation-warning{border-color:red}
|
||||
.collection-creator .footer .actions .other-options>*{display:none;margin-left:4px}
|
||||
.collection-creator.list-collection-creator .footer{margin-top:8px}
|
||||
.collection-creator.list-collection-creator .main-help{cursor:pointer}
|
||||
.collection-creator.list-collection-creator .collection-elements-controls{margin-bottom:8px}.collection-creator.list-collection-creator .collection-elements-controls .clear-selected{float:right !important;float:right;display:none}
|
||||
.collection-creator.list-collection-creator .collection-elements{max-height:400px;border:1px solid lightgrey;border-width:1px 0px 1px 0px;overflow-y:auto;overflow-x:hidden}
|
||||
.collection-creator.list-collection-creator .collection-element{height:32px;margin:2px 4px 0px 4px;opacity:1.0;border:1px solid lightgrey;border-radius:3px;padding:0 8px 0 8px;line-height:28px;cursor:pointer;overflow:hidden}.collection-creator.list-collection-creator .collection-element:last-of-type{margin-bottom:2px}
|
||||
.collection-creator.list-collection-creator .collection-element:hover{border-color:black}
|
||||
.collection-creator.list-collection-creator .collection-element.selected{border-color:black;background:black;color:white}
|
||||
.collection-creator.list-collection-creator .collection-element.dragging{opacity:0.4}.collection-creator.list-collection-creator .collection-element.dragging button{display:none}
|
||||
.collection-creator.list-collection-creator .collection-element .name:hover{text-decoration:underline}
|
||||
.collection-creator.list-collection-creator .collection-element button{margin-top:3px}
|
||||
.collection-creator.list-collection-creator .collection-element .discard{float:right !important;float:right}
|
||||
.collection-creator.list-collection-creator .element-drop-placeholder{width:60px;height:3px;margin:2px 0px 0px 6px;background:black}.collection-creator.list-collection-creator .element-drop-placeholder:before{float:left;font-size:120%;margin:-9px 0 0 -6px;font-family:FontAwesome;content:'\f0da'}
|
||||
.collection-creator.list-collection-creator .element-drop-placeholder:last-child{margin-bottom:4px}
|
||||
.collection-creator.list-collection-creator .no-elements-left-message{margin:8px;color:grey;font-style:italic;text-align:center}
|
||||
.collection-creator.list-of-pairs-collection-creator .column-headers{margin-bottom:8px}
|
||||
.search-input .search-query{width:100%;padding-right:24px}
|
||||
.search-input .search-clear,.search-input .search-loading{position:relative;display:inline-block;float:right;margin-top:-24px;margin-right:4px;font-size:1.4em;line-height:23px;color:grey}
|
||||
.search-input .search-clear:hover{color:#303030}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// --- main layout elements
|
||||
// ======================================================================== all types
|
||||
.collection-creator {
|
||||
|
||||
height: 100%;
|
||||
@@ -106,6 +106,11 @@
|
||||
|
||||
.header .alert {
|
||||
display: none;
|
||||
|
||||
li {
|
||||
list-style: circle;
|
||||
margin-left: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------ columns
|
||||
@@ -139,8 +144,6 @@
|
||||
|
||||
// ------------------------------------------------------------------------ header
|
||||
.header {
|
||||
padding-bottom: 8px;
|
||||
|
||||
.main-help {
|
||||
margin-bottom: 17px;
|
||||
overflow: hidden;
|
||||
@@ -434,3 +437,109 @@
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================== list
|
||||
.collection-creator.list-collection-creator {
|
||||
|
||||
.footer {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.main-help {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.collection-elements-controls {
|
||||
margin-bottom: 8px;
|
||||
|
||||
.clear-selected {
|
||||
.pull-right;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.collection-elements {
|
||||
max-height: 400px;
|
||||
border: 1px solid lightgrey;
|
||||
border-width: 1px 0px 1px 0px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
// TODO: taken from .dataset above - swap these out
|
||||
.collection-element {
|
||||
height: 32px;
|
||||
margin: 2px 4px 0px 4px;
|
||||
opacity: 1.0;
|
||||
border: 1px solid lightgrey;
|
||||
border-radius: 3px;
|
||||
padding: 0 8px 0 8px;
|
||||
line-height: 28px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
&:hover {
|
||||
border-color: black;
|
||||
}
|
||||
&.selected {
|
||||
border-color: black;
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
&.dragging {
|
||||
opacity: 0.4;
|
||||
button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
button {
|
||||
margin-top: 3px;
|
||||
}
|
||||
.discard {
|
||||
.pull-right;
|
||||
}
|
||||
}
|
||||
//TODO: copied from paired-drop-placeholder above - unifiy
|
||||
.element-drop-placeholder {
|
||||
width : 60px;
|
||||
height : 3px;
|
||||
margin : 2px 0px 0px 6px;
|
||||
background : black;
|
||||
&:before {
|
||||
float: left;
|
||||
font-size: 120%;
|
||||
margin: -9px 0px 0px -6px;
|
||||
font-family: FontAwesome;
|
||||
content: '\f0da';
|
||||
}
|
||||
&:last-child {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
.no-elements-left-message {
|
||||
margin: 8px;
|
||||
color: grey;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================== pair
|
||||
.collection-creator.pair-collection-creator {
|
||||
}
|
||||
|
||||
// ======================================================================== list of pairs
|
||||
.collection-creator.list-of-pairs-collection-creator {
|
||||
.column-headers {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,32 +16,61 @@ $(function(){
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
## <%def name="javascript_app()">
|
||||
## <script type="text/javascript">
|
||||
## define( 'app', function(){
|
||||
## require([
|
||||
## //'mvc/history/history-panel'
|
||||
## //'mvc/history/history-panel-annotated'
|
||||
## 'mvc/history/history-panel-edit'
|
||||
## //'mvc/history/history-panel-edit-current'
|
||||
## ], function( historyPanel ){
|
||||
## $(function(){
|
||||
## // history module is already in the dpn chain from the panel. We can re-scope it here.
|
||||
## var historyModel = require( 'mvc/history/history-model' );
|
||||
## //window.panel = new historyPanel.HistoryPanel({
|
||||
## //window.panel = new historyPanel.AnnotatedHistoryPanel({
|
||||
## window.panel = new historyPanel.HistoryPanelEdit({
|
||||
## //window.panel = new historyPanel.CurrentHistoryPanel({
|
||||
## show_deleted : bootstrapped.show_deleted,
|
||||
## show_hidden : bootstrapped.show_hidden,
|
||||
## purgeAllowed : Galaxy.config.allow_user_dataset_purge,
|
||||
## model : new historyModel.History( bootstrapped.history, bootstrapped.hdas )
|
||||
## });
|
||||
## panel.render().$el.appendTo( 'body' );
|
||||
## //$( 'body' ).css( 'padding', '10px' );
|
||||
## });
|
||||
## });
|
||||
## });
|
||||
## </script>
|
||||
## ${ galaxy_client.load( app='app', history=history, hdas=hdas, show_deleted=show_deleted, show_hidden=show_hidden ) }
|
||||
## </%def>
|
||||
|
||||
<%def name="javascript_app()">
|
||||
<script type="text/javascript">
|
||||
define( 'app', function(){
|
||||
require([
|
||||
//'mvc/history/history-panel'
|
||||
//'mvc/history/history-panel-annotated'
|
||||
'mvc/history/history-panel-edit'
|
||||
//'mvc/history/history-panel-edit-current'
|
||||
], function( historyPanel ){
|
||||
$(function(){
|
||||
// history module is already in the dpn chain from the panel. We can re-scope it here.
|
||||
var historyModel = require( 'mvc/history/history-model' );
|
||||
//window.panel = new historyPanel.HistoryPanel({
|
||||
//window.panel = new historyPanel.AnnotatedHistoryPanel({
|
||||
window.panel = new historyPanel.HistoryPanelEdit({
|
||||
//window.panel = new historyPanel.CurrentHistoryPanel({
|
||||
show_deleted : bootstrapped.show_deleted,
|
||||
show_hidden : bootstrapped.show_hidden,
|
||||
purgeAllowed : Galaxy.config.allow_user_dataset_purge,
|
||||
model : new historyModel.History( bootstrapped.history, bootstrapped.hdas )
|
||||
define( 'app', function(){
|
||||
require([
|
||||
'mvc/history/history-contents',
|
||||
'mvc/collection/list-collection-creator'
|
||||
], function( HISTORY_CONTENTS, LCC ){
|
||||
$(function(){
|
||||
var hdas = bootstrapped.hdas.filter( function( hda ){
|
||||
return !( hda.deleted || hda.purged );
|
||||
});
|
||||
console.debug( hdas );
|
||||
var collection = new HISTORY_CONTENTS.HistoryContents( hdas, { historyId : bootstrapped.history.id })
|
||||
|
||||
// LCC.listCollectionCreatorModal( hdas )
|
||||
LCC.createListCollection( collection )
|
||||
.done( function( response ){
|
||||
console.info( 'creation succeeded:', arguments );
|
||||
})
|
||||
.fail( function( response ){
|
||||
console.error( 'creation failed:', arguments );
|
||||
});
|
||||
});
|
||||
panel.render().$el.appendTo( 'body' );
|
||||
//$( 'body' ).css( 'padding', '10px' );
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
${ galaxy_client.load( app='app', history=history, hdas=hdas, show_deleted=show_deleted, show_hidden=show_hidden ) }
|
||||
</%def>
|
||||
|
||||
Reference in New Issue
Block a user