mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-28 02:58:18 +08:00
Upgrade content selector, add test cases
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
define(['utils/utils',
|
||||
'mvc/ui/ui-misc',
|
||||
'mvc/form/form-select-content',
|
||||
'mvc/ui/ui-select-content',
|
||||
'mvc/ui/ui-select-library',
|
||||
'mvc/ui/ui-select-ftp',
|
||||
'mvc/ui/ui-color-picker'],
|
||||
@@ -54,7 +54,7 @@ define(['utils/utils',
|
||||
*/
|
||||
_fieldData: function( input_def ) {
|
||||
var self = this;
|
||||
return new SelectContent.View( this.app, {
|
||||
return new SelectContent.View({
|
||||
id : 'field-' + input_def.id,
|
||||
extensions : input_def.extensions,
|
||||
optional : input_def.optional,
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
// dependencies
|
||||
define(['utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-tabs'], function(Utils, Ui, Tabs) {
|
||||
// hda/hdca content selector ui element
|
||||
var View = Backbone.View.extend({
|
||||
// initialize
|
||||
initialize : function(app, options) {
|
||||
// link app and options
|
||||
this.app = app;
|
||||
this.options = options;
|
||||
|
||||
// track current history elements
|
||||
this.history = {};
|
||||
|
||||
|
||||
// link this
|
||||
var self = this;
|
||||
|
||||
// add element
|
||||
this.setElement('<div class="ui-select-content"/>');
|
||||
|
||||
// list of select fieldsFormSection
|
||||
this.list = {};
|
||||
|
||||
// radio button options
|
||||
var radio_buttons = [];
|
||||
|
||||
// identify selector type
|
||||
if (options.type == 'data_collection') {
|
||||
this.mode = 'collection';
|
||||
} else {
|
||||
if (options.multiple) {
|
||||
this.mode = 'multiple';
|
||||
} else {
|
||||
this.mode = 'single';
|
||||
}
|
||||
}
|
||||
|
||||
// set initial state
|
||||
this.current = this.mode;
|
||||
this.list = {};
|
||||
|
||||
// error messages
|
||||
var extensions = Utils.textify(options.extensions);
|
||||
var hda_error = 'No dataset available.';
|
||||
if (extensions) {
|
||||
hda_error = 'No ' + extensions + ' dataset available.';
|
||||
}
|
||||
var hdca_error = 'No dataset list available.';
|
||||
if (extensions) {
|
||||
hdca_error = 'No ' + extensions + ' dataset collection available.';
|
||||
}
|
||||
|
||||
// add single dataset selector
|
||||
if (this.mode == 'single') {
|
||||
radio_buttons.push({
|
||||
icon : 'fa-file-o',
|
||||
value : 'single',
|
||||
tooltip : 'Single dataset'
|
||||
});
|
||||
this.select_single = new Ui.Select.View({
|
||||
optional : options.optional,
|
||||
error_text : hda_error,
|
||||
onchange : function() {
|
||||
self.trigger('change');
|
||||
}
|
||||
});
|
||||
this.list['single'] = {
|
||||
field: this.select_single,
|
||||
type : 'hda'
|
||||
};
|
||||
}
|
||||
|
||||
// add multiple dataset selector
|
||||
if (this.mode == 'single' || this.mode == 'multiple') {
|
||||
radio_buttons.push({
|
||||
icon : 'fa-files-o',
|
||||
value : 'multiple',
|
||||
tooltip : 'Multiple datasets'
|
||||
});
|
||||
this.select_multiple = new Ui.Select.View({
|
||||
multiple : true,
|
||||
searchable : false,
|
||||
optional : options.optional,
|
||||
error_text : hda_error,
|
||||
onchange : function() {
|
||||
self.trigger('change');
|
||||
}
|
||||
});
|
||||
this.list['multiple'] = {
|
||||
field: this.select_multiple,
|
||||
type : 'hda'
|
||||
};
|
||||
}
|
||||
|
||||
// add collection selector
|
||||
if (this.mode == 'single' || this.mode == 'multiple' || this.mode == 'collection') {
|
||||
radio_buttons.push({
|
||||
icon : 'fa-folder-o',
|
||||
value : 'collection',
|
||||
tooltip : 'Dataset collection'
|
||||
});
|
||||
var multiple = this.mode == 'multiple';
|
||||
this.select_collection = new Ui.Select.View({
|
||||
error_text : hdca_error,
|
||||
multiple : multiple,
|
||||
searchable : false,
|
||||
optional : options.optional,
|
||||
onchange : function() {
|
||||
self.trigger('change');
|
||||
}
|
||||
});
|
||||
this.list['collection'] = {
|
||||
field: this.select_collection,
|
||||
type : 'hdca'
|
||||
};
|
||||
}
|
||||
|
||||
// create button
|
||||
this.button_type = new Ui.RadioButton.View({
|
||||
value : this.current,
|
||||
data : radio_buttons,
|
||||
onchange: function(value) {
|
||||
self.current = value;
|
||||
self.refresh();
|
||||
self.trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
// add batch mode information
|
||||
this.$batch = $(this.template_batch());
|
||||
|
||||
// number of radio buttons
|
||||
var n_buttons = _.size(this.list);
|
||||
|
||||
// add button to dom
|
||||
var button_width = 0;
|
||||
if (n_buttons > 1) {
|
||||
this.$el.append(this.button_type.$el);
|
||||
button_width = Math.max(0, _.size(this.list) * 35) + 'px';
|
||||
}
|
||||
|
||||
// append field elements
|
||||
for (var i in this.list) {
|
||||
this.$el.append(this.list[i].field.$el.css({
|
||||
'margin-left': button_width
|
||||
}));
|
||||
}
|
||||
|
||||
// append batch message
|
||||
this.$el.append(this.$batch.css({
|
||||
'margin-left': button_width
|
||||
}));
|
||||
|
||||
// update options
|
||||
this.update(options.data);
|
||||
|
||||
// set initial value
|
||||
if (this.options.value !== undefined) {
|
||||
this.value(this.options.value);
|
||||
}
|
||||
|
||||
// refresh view
|
||||
this.refresh();
|
||||
|
||||
// add change event. fires on trigger
|
||||
this.on('change', function() {
|
||||
if (options.onchange) {
|
||||
options.onchange(self.value());
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/** Indicate that select fields are being updated */
|
||||
wait: function() {
|
||||
for (var i in this.list) {
|
||||
this.list[i].field.wait();
|
||||
}
|
||||
},
|
||||
|
||||
/** Indicate that the options update has been completed */
|
||||
unwait: function() {
|
||||
for (var i in this.list) {
|
||||
this.list[i].field.unwait();
|
||||
}
|
||||
},
|
||||
|
||||
/** Update content selector */
|
||||
update: function(options) {
|
||||
// update a particular select field
|
||||
var self = this;
|
||||
function _update(field, options) {
|
||||
if (field) {
|
||||
// identify available options
|
||||
var select_options = [];
|
||||
for (var i in options) {
|
||||
var item = options[i];
|
||||
select_options.push({
|
||||
hid : item.hid,
|
||||
label: item.hid + ': ' + item.name,
|
||||
value: item.id
|
||||
});
|
||||
// backup to local history
|
||||
self.history[item.id + '_' + item.src] = item;
|
||||
}
|
||||
// update field
|
||||
field.add( select_options, function( a, b ) { return b.hid - a.hid } );
|
||||
}
|
||||
}
|
||||
|
||||
// update available options
|
||||
_update(this.select_single, options.hda);
|
||||
_update(this.select_multiple, options.hda);
|
||||
_update(this.select_collection, options.hdca);
|
||||
},
|
||||
|
||||
/** Return the currently selected dataset values */
|
||||
value : function (new_value) {
|
||||
// update current value
|
||||
if (new_value !== undefined) {
|
||||
if (new_value && new_value.values) {
|
||||
try {
|
||||
// create list with values
|
||||
var list = [];
|
||||
for (var i in new_value.values) {
|
||||
list.push(new_value.values[i].id);
|
||||
}
|
||||
|
||||
// identify suitable select field
|
||||
if (new_value && new_value.values.length > 0 && new_value.values[0].src == 'hdca') {
|
||||
this.current = 'collection';
|
||||
this.select_collection.value(list);
|
||||
} else {
|
||||
if (this.mode == 'multiple') {
|
||||
this.current = 'multiple';
|
||||
this.select_multiple.value(list);
|
||||
} else {
|
||||
this.current = 'single';
|
||||
this.select_single.value(list[0]);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
Galaxy.emit.debug('tools-select-content::value()', 'Skipped.');
|
||||
}
|
||||
} else {
|
||||
for (var i in this.list) {
|
||||
this.list[i].field.value(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// refresh view
|
||||
this.refresh();
|
||||
|
||||
// validate value
|
||||
var id_list = this._select().value();
|
||||
if (id_list === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// transform into an array
|
||||
if (!(id_list instanceof Array)) {
|
||||
id_list = [id_list];
|
||||
}
|
||||
|
||||
// check if value exists
|
||||
if (id_list.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// prepare result dict
|
||||
var result = {
|
||||
batch : this._batch(),
|
||||
values : []
|
||||
}
|
||||
|
||||
// append to dataset ids
|
||||
for (var i in id_list) {
|
||||
var details = this.history[id_list[i] + '_' + this.list[this.current].type];
|
||||
if (details) {
|
||||
result.values.push(details);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// sort by history ids
|
||||
result.values.sort(function(a, b){
|
||||
return a.hid - b.hid;
|
||||
});
|
||||
|
||||
// return
|
||||
return result;
|
||||
},
|
||||
|
||||
/** Refreshes data selection view */
|
||||
refresh: function() {
|
||||
this.button_type.value(this.current);
|
||||
for (var i in this.list) {
|
||||
var $el = this.list[i].field.$el;
|
||||
if (this.current == i) {
|
||||
$el.show();
|
||||
} else {
|
||||
$el.hide();
|
||||
}
|
||||
}
|
||||
if (this._batch()) {
|
||||
this.$batch.show();
|
||||
} else {
|
||||
this.$batch.hide();
|
||||
}
|
||||
},
|
||||
|
||||
/** Assists in selecting the current field */
|
||||
_select: function() {
|
||||
return this.list[this.current].field;
|
||||
},
|
||||
|
||||
/** Assists in identifying the batch mode */
|
||||
_batch: function() {
|
||||
if (this.current == 'collection') {
|
||||
var hdca = this.history[this._select().value() + '_hdca'];
|
||||
if (hdca && hdca.map_over_type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this.current != 'single') {
|
||||
if (this.mode == 'single') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/** Batch message template */
|
||||
template_batch: function() {
|
||||
return '<div class="ui-form-info">' +
|
||||
'<i class="fa fa-sitemap" style="font-size: 1.2em; padding: 2px 5px;"/>' +
|
||||
'This is a batch mode input field. A separate job will be triggered for each dataset.' +
|
||||
'</div>';
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
View: View
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,239 @@
|
||||
define([ 'utils/utils', 'mvc/ui/ui-misc', 'mvc/ui/ui-select-default' ], function( Utils, Ui, Select ) {
|
||||
|
||||
/** List of available content selectors options */
|
||||
var Configurations = {
|
||||
'data': [
|
||||
{ name: 'single', src: 'hda', icon: 'fa-file-o', tooltip: 'Single dataset', batchmode: false, multiple: false },
|
||||
{ name: 'multiple', src: 'hda', icon: 'fa-files-o', tooltip: 'Multiple datasets', batchmode: true, multiple: true },
|
||||
{ name: 'collection', src: 'hdca', icon: 'fa-folder-o', tooltip: 'Dataset collection', batchmode: true, multiple: false } ],
|
||||
'data_collection': [
|
||||
{ name: 'collection', src: 'hdca', icon: 'fa-folder-o', tooltip: 'Dataset collection', batchmode: false, multiple: false } ],
|
||||
'data_multiple': [
|
||||
{ name: 'multiple', src: 'hda', icon: 'fa-files-o', tooltip: 'Multiple datasets', batchmode: false, multiple: true },
|
||||
{ name: 'collection', src: 'hdca', icon: 'fa-folder-o', tooltip: 'Dataset collection', batchmode: false, multiple: false } ]
|
||||
};
|
||||
|
||||
/** View for hda and hdca content selector ui elements */
|
||||
var View = Backbone.View.extend({
|
||||
initialize : function( options ) {
|
||||
var self = this;
|
||||
this.model = options && options.model || new Backbone.Model( options );
|
||||
this.setElement( $( '<div/>' ).addClass( 'ui-select-content' ) );
|
||||
this.$batch = $( '<div/>' ).addClass( 'ui-form-info' )
|
||||
.append( $( '<i/>' ).addClass( 'fa fa-sitemap' ) )
|
||||
.append( $( '<span/>' ).html( 'This is a batch mode input field. A separate job will be triggered for each dataset.' ) );
|
||||
|
||||
// track current history elements
|
||||
this.history = {};
|
||||
|
||||
// add listeners
|
||||
this.listenTo( this.model, 'change', this.render, this );
|
||||
this.listenTo( this.model, 'change:type change:multiple', this._changeType, this );
|
||||
this.listenTo( this.model, 'change:value', this._changeValue, this );
|
||||
this.listenTo( this.model, 'change:data', this._changeData, this );
|
||||
this.listenTo( this.model, 'change:wait', this._changeWait, this );
|
||||
this.render();
|
||||
|
||||
// update selectable data and initial value
|
||||
this.model.get( 'value' ) !== undefined && this.model.trigger( 'change:value' );
|
||||
this.model.trigger( 'change:type' );
|
||||
this.model.trigger( 'change:value' );
|
||||
|
||||
// add change event. fires on trigger
|
||||
this.on( 'change', function() { options.onchange && options.onchange( self.value() ) } );
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var self = this;
|
||||
_.each( this.fields, function( field, i ) {
|
||||
if ( self.model.get( 'current' ) == i ) {
|
||||
field.$el.show();
|
||||
self.$batch[ self.config[ i ].batchmode && 'show' || 'hide' ]();
|
||||
self.button_type.value( i );
|
||||
} else {
|
||||
field.$el.hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/** Indicate that select fields are being updated */
|
||||
wait: function() {
|
||||
this.model.set( 'wait', true );
|
||||
},
|
||||
|
||||
/** Indicate that the options update has been completed */
|
||||
unwait: function() {
|
||||
this.model.set( 'wait', false );
|
||||
},
|
||||
|
||||
/** Update data representing selectable options */
|
||||
update: function( options ) {
|
||||
this.model.set( 'data', options );
|
||||
},
|
||||
|
||||
/** Return the currently selected dataset values */
|
||||
value: function ( new_value ) {
|
||||
new_value !== undefined && this.model.set( 'value', new_value );
|
||||
var current = this.model.get( 'current' );
|
||||
if ( this.config[ current ] ) {
|
||||
var id_list = this.fields[ current ].value();
|
||||
if (id_list !== null) {
|
||||
id_list = $.isArray( id_list ) ? id_list : [ id_list ];
|
||||
if ( id_list.length > 0 ) {
|
||||
var result = { batch: this._batch(), values: [] };
|
||||
for ( var i in id_list ) {
|
||||
var details = this.history[ id_list[ i ] + '_' + this.config[ current ].src ];
|
||||
if ( details ) {
|
||||
result.values.push( details );
|
||||
} else {
|
||||
Galaxy.emit.debug( 'tools-select-content::_changeValue()', 'Requested details not found for \'' + id_list[ i ] + '\'.' );
|
||||
return null;
|
||||
}
|
||||
}
|
||||
result.values.sort( function( a, b ) { return a.hid - b.hid } );
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Galaxy.emit.debug( 'tools-select-content::_changeValue()', 'Invalid value/source \'' + new_value + '\'.' );
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/** Change of type */
|
||||
_changeType: function() {
|
||||
var self = this;
|
||||
this.model.set( 'current', 0 );
|
||||
|
||||
// identify selector type
|
||||
var config_id = String( this.model.get( 'type' ) ) + ( this.model.get( 'multiple' ) ? '_multiple' : '' );
|
||||
if ( Configurations[ config_id ] ) {
|
||||
this.config = Configurations[ config_id ];
|
||||
} else {
|
||||
this.config = Configurations[ 'data' ];
|
||||
Galaxy.emit.debug( 'tools-select-content::_changeType()', 'Invalid configuration/type id \'' + config_id + '\'.' );
|
||||
}
|
||||
|
||||
// prepare error messages
|
||||
var extensions = Utils.textify( this.model.get( 'extensions' ) );
|
||||
var hda_error = extensions ? 'No ' + extensions + ' dataset available.' : 'No dataset available.';
|
||||
var hdca_error = extensions ? 'No ' + extensions + ' dataset collection available.' : 'No dataset collection available.';
|
||||
|
||||
// build views
|
||||
this.fields = [];
|
||||
this.button_data = [];
|
||||
_.each( this.config, function( c, i ) {
|
||||
self.button_data.push({
|
||||
value : i,
|
||||
icon : c.icon,
|
||||
tooltip : c.label
|
||||
});
|
||||
self.fields.push(
|
||||
new Select.View({
|
||||
optional : self.model.get( 'optional' ),
|
||||
multiple : c.multiple,
|
||||
searchable : !c.multiple,
|
||||
error_text : c.type === 'hda' ? hda_error : hdca_error,
|
||||
onchange : function() {
|
||||
self.trigger( 'change' );
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
this.button_type = new Ui.RadioButton.View({
|
||||
value : this.model.get( 'current' ),
|
||||
data : this.button_data,
|
||||
onchange: function( value ) {
|
||||
self.model.set( 'current', value );
|
||||
self.trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
// append views
|
||||
this.$el.empty();
|
||||
var button_width = 0;
|
||||
if ( this.fields.length > 1 ) {
|
||||
this.$el.append( this.button_type.$el );
|
||||
button_width = Math.max( 0, this.fields.length * 35 ) + 'px';
|
||||
}
|
||||
_.each( this.fields, function( field ) {
|
||||
self.$el.append( field.$el.css( { 'margin-left': button_width } ) );
|
||||
});
|
||||
this.$el.append( this.$batch.css( { 'margin-left': button_width } ) );
|
||||
this.model.trigger( 'change:data' );
|
||||
},
|
||||
|
||||
/** Change of wait flag */
|
||||
_changeWait: function() {
|
||||
var self = this;
|
||||
_.each( this.fields, function( field ) { field[ self.model.get( 'wait' ) ? 'wait' : 'unwait' ]() } );
|
||||
},
|
||||
|
||||
/** Change of available options */
|
||||
_changeData: function() {
|
||||
var options = this.model.get( 'data' );
|
||||
var self = this;
|
||||
var select_options = {};
|
||||
_.each( options, function( items, src ) {
|
||||
select_options[ src ] = [];
|
||||
_.each( items, function( item ) {
|
||||
select_options[ src ].push({
|
||||
hid : item.hid,
|
||||
label: item.hid + ': ' + item.name,
|
||||
value: item.id
|
||||
});
|
||||
self.history[ item.id + '_' + src ] = item;
|
||||
});
|
||||
});
|
||||
_.each( this.config, function( c, i ) {
|
||||
self.fields[ i ].add( select_options[ c.src ], function( a, b ) { return b.hid - a.hid } );
|
||||
});
|
||||
},
|
||||
|
||||
/** Change of incoming value */
|
||||
_changeValue: function () {
|
||||
var new_value = this.model.get( 'value' );
|
||||
if ( new_value && new_value.values && new_value.values.length > 0 ) {
|
||||
// create list with content ids
|
||||
var list = [];
|
||||
_.each( new_value.values, function( value ) {
|
||||
list.push( value.id );
|
||||
});
|
||||
// sniff first suitable field type from config list
|
||||
var src = new_value.values[ 0 ].src;
|
||||
var multiple = new_value.values.length > 1;
|
||||
var current = -1;
|
||||
for( var i = 0; i < this.config.length; i++ ) {
|
||||
var field = this.fields[ i ];
|
||||
var c = this.config[ i ];
|
||||
if ( current === -1 && c.src == src && [ multiple, true ].indexOf( c.multiple ) !== -1 ) {
|
||||
this.model.set( 'current', current = i );
|
||||
field.value( list );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_.each( this.fields, function( field ) {
|
||||
field.value( null );
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/** Assists in identifying the batch mode */
|
||||
_batch: function() {
|
||||
var current = this.model.get( 'current' );
|
||||
var config = this.config[ current ];
|
||||
if ( config.src == 'hdca' && !config.multiple ) {
|
||||
var hdca = this.history[ this.fields[ current ].value() + '_hdca' ];
|
||||
if ( hdca && hdca.map_over_type ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return config.batchmode;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
View: View
|
||||
}
|
||||
|
||||
});
|
||||
@@ -191,7 +191,7 @@ var View = Backbone.View.extend({
|
||||
_.each( this.model.get( 'options' ), function( v ) {
|
||||
!_.findWhere( options, v ) && options.push( v );
|
||||
});
|
||||
sorter && options.sort( sorter );
|
||||
sorter && options && options.sort( sorter );
|
||||
this.update( options );
|
||||
},
|
||||
update: function(options) {
|
||||
|
||||
@@ -64,13 +64,12 @@ function validate ( value ) {
|
||||
* Convert list to pretty string
|
||||
* @param{String} lst - List of strings to be converted in human readable list sentence
|
||||
*/
|
||||
function textify(lst) {
|
||||
var lst = lst.toString();
|
||||
if (lst) {
|
||||
lst = lst.replace(/,/g, ', ');
|
||||
var pos = lst.lastIndexOf(', ');
|
||||
if (pos != -1) {
|
||||
lst = lst.substr(0, pos) + ' or ' + lst.substr(pos+1);
|
||||
function textify( lst ) {
|
||||
if ( $.isArray( lst ) ) {
|
||||
var lst = lst.toString().replace( /,/g, ', ' );
|
||||
var pos = lst.lastIndexOf( ', ' );
|
||||
if ( pos != -1 ) {
|
||||
lst = lst.substr( 0, pos ) + ' or ' + lst.substr( pos + 1 );
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
|
||||
@@ -286,8 +286,12 @@
|
||||
}
|
||||
|
||||
.ui-form-info {
|
||||
&:extend(.toolParamHelp);
|
||||
clear: both !important;
|
||||
&:extend(.toolParamHelp);
|
||||
clear: both !important;
|
||||
i {
|
||||
font-size: 1.2em;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-form-footer-info {
|
||||
|
||||
Reference in New Issue
Block a user