Upgrade content selector, add test cases

This commit is contained in:
guerler
2016-03-27 16:18:26 -04:00
parent 5b16589f29
commit f70df22060
23 changed files with 361 additions and 391 deletions
@@ -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) {
+6 -7
View File
@@ -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;
}
+6 -2
View File
@@ -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 {