mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
618 lines
24 KiB
JavaScript
618 lines
24 KiB
JavaScript
$( function() {
|
|
|
|
if ( window.lt_ie_7 ) {
|
|
show_modal(
|
|
"Browser not supported",
|
|
"Sorry, the workflow editor is not supported for IE6 and below."
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Init searching.
|
|
$("#tool-search-query").click( function (){
|
|
$(this).focus();
|
|
$(this).select();
|
|
})
|
|
.keyup( function () {
|
|
// Remove italics.
|
|
$(this).css("font-style", "normal");
|
|
// Don't update if same value as last time
|
|
if ( this.value.length < 3 ) {
|
|
reset_tool_search(false);
|
|
} else if ( this.value != this.lastValue ) {
|
|
// Add class to denote that searching is active.
|
|
$(this).addClass("search_active");
|
|
// input.addClass(config.loadingClass);
|
|
// Add '*' to facilitate partial matching.
|
|
var q = this.value + '*';
|
|
// Stop previous ajax-request
|
|
if (this.timer) {
|
|
clearTimeout(this.timer);
|
|
}
|
|
// Start a new ajax-request in X ms
|
|
$("#search-spinner").show();
|
|
this.timer = setTimeout(function () {
|
|
$.get(tool_search_url, { query: q }, function (data) {
|
|
// input.removeClass(config.loadingClass);
|
|
// Show live-search if results and search-term aren't empty
|
|
$("#search-no-results").hide();
|
|
// Hide all tool sections.
|
|
$(".toolSectionWrapper").hide();
|
|
// This hides all tools but not workflows link (which is in a .toolTitle div).
|
|
$(".toolSectionWrapper").find(".toolTitle").hide();
|
|
if ( data.length != 0 ) {
|
|
// Map tool ids to element ids and join them.
|
|
var s = $.map( data, function( n, i ) { return "link-" + n; } );
|
|
// First pass to show matching tools and their parents.
|
|
$(s).each( function(index,id) {
|
|
// Add class to denote match.
|
|
$("[id='"+id+"']").parent().addClass("search_match");
|
|
$("[id='"+id+"']").parent().show().parent().parent().show().parent().show();
|
|
});
|
|
// Hide labels that have no visible children.
|
|
$(".toolPanelLabel").each( function() {
|
|
var this_label = $(this);
|
|
var next = this_label.next();
|
|
var no_visible_tools = true;
|
|
// Look through tools following label and, if none are visible, hide label.
|
|
while (next.length !== 0 && next.hasClass("toolTitle")) {
|
|
if (next.is(":visible")) {
|
|
no_visible_tools = false;
|
|
break;
|
|
} else {
|
|
next = next.next();
|
|
}
|
|
}
|
|
if (no_visible_tools) {
|
|
this_label.hide();
|
|
}
|
|
});
|
|
} else {
|
|
$("#search-no-results").show();
|
|
}
|
|
$("#search-spinner").hide();
|
|
}, "json" );
|
|
}, 200 );
|
|
}
|
|
this.lastValue = this.value;
|
|
});
|
|
|
|
// Canvas overview management
|
|
canvas_manager = new CanvasManager( $("#canvas-viewport"), $("#overview") );
|
|
|
|
// Initialize workflow state
|
|
reset();
|
|
// Load the datatype info
|
|
$.ajax( {
|
|
url: get_datatypes_url,
|
|
dataType: "json",
|
|
cache: false,
|
|
success: function( data ) {
|
|
populate_datatype_info( data );
|
|
// Load workflow definition
|
|
$.ajax( {
|
|
url: load_workflow_url,
|
|
data: { id: workflow_id, "_": "true" },
|
|
dataType: 'json',
|
|
cache: false,
|
|
success: function( data ) {
|
|
reset();
|
|
workflow.from_simple( data );
|
|
workflow.has_changes = false;
|
|
workflow.fit_canvas_to_nodes();
|
|
scroll_to_nodes();
|
|
canvas_manager.draw_overview();
|
|
// Determine if any parameters were 'upgraded' and provide message
|
|
upgrade_message = "";
|
|
$.each( data.upgrade_messages, function( k, v ) {
|
|
upgrade_message += ( "<li>Step " + ( parseInt(k, 10) + 1 ) + ": " + workflow.nodes[k].name + "<ul>");
|
|
$.each( v, function( i, vv ) {
|
|
upgrade_message += "<li>" + vv +"</li>";
|
|
});
|
|
upgrade_message += "</ul></li>";
|
|
});
|
|
if ( upgrade_message ) {
|
|
show_modal( "Workflow loaded with changes",
|
|
"Problems were encountered loading this workflow (possibly a result of tool upgrades). Please review the following parameters and then save.<ul>" + upgrade_message + "</ul>",
|
|
{ "Continue" : hide_modal } );
|
|
} else {
|
|
hide_modal();
|
|
}
|
|
show_workflow_parameters();
|
|
},
|
|
beforeSubmit: function( data ) {
|
|
show_message( "Loading workflow", "progress" );
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// For autosave purposes
|
|
$(document).ajaxStart( function() {
|
|
active_ajax_call = true;
|
|
$(document).bind( "ajaxStop.global", function() {
|
|
active_ajax_call = false;
|
|
});
|
|
});
|
|
|
|
$(document).ajaxError( function ( e, x ) {
|
|
// console.log( e, x );
|
|
var message = x.responseText || x.statusText || "Could not connect to server";
|
|
show_modal( "Server error", message, { "Ignore error" : hide_modal } );
|
|
return false;
|
|
});
|
|
|
|
make_popupmenu( $("#workflow-options-button"), {
|
|
"Save" : save_current_workflow,
|
|
"Run": function() {
|
|
window.location = run_workflow_url;
|
|
},
|
|
//"Create New" : create_new_workflow_dialog,
|
|
"Edit Attributes" : edit_workflow_attributes,
|
|
//"Edit Workflow Outputs": edit_workflow_outputs,
|
|
"Auto Re-layout": layout_editor,
|
|
//"Load a Workflow" : load_workflow,
|
|
"Close": close_editor
|
|
});
|
|
|
|
function edit_workflow_outputs(){
|
|
workflow.clear_active_node();
|
|
$('.right-content').hide();
|
|
var new_content = "";
|
|
for (var node_key in workflow.nodes){
|
|
var node = workflow.nodes[node_key];
|
|
if(node.type == 'tool'){
|
|
new_content += "<div class='toolForm' style='margin-bottom:5px;'><div class='toolFormTitle'>Step " + node.id + " - " + node.name + "</div>";
|
|
for (var ot_key in node.output_terminals){
|
|
var output = node.output_terminals[ot_key];
|
|
// if (node.workflow_outputs[node.id + "|" + output.name]){
|
|
if ($.inArray(output.name, node.workflow_outputs) != -1){
|
|
new_content += "<p>"+output.name +"<input type='checkbox' name='"+ node.id + "|" + output.name +"' checked /></p>";
|
|
}
|
|
else{
|
|
new_content += "<p>"+output.name +"<input type='checkbox' name='"+ node.id + "|" + output.name +"' /></p>";
|
|
}
|
|
}
|
|
new_content += "</div>";
|
|
}
|
|
}
|
|
$("#output-fill-area").html(new_content);
|
|
$("#output-fill-area input").bind('click', function(){
|
|
var node_id = this.name.split('|')[0];
|
|
var output_name = this.name.split('|')[1];
|
|
if (this.checked){
|
|
if($.inArray(output_name, workflow.nodes[node_id].workflow_outputs) == -1){
|
|
workflow.nodes[node_id].workflow_outputs.push(output_name);
|
|
}//else it's already in the array. Shouldn't happen, but forget it.
|
|
}else{
|
|
while ($.inArray(output_name, workflow.nodes[node_id].workflow_outputs) != -1){
|
|
var ia = $.inArray(output_name, workflow.nodes[node_id].workflow_outputs);
|
|
workflow.nodes[node_id].workflow_outputs = workflow.nodes[node_id].workflow_outputs.slice(0,ia).concat( workflow.nodes[node_id].workflow_outputs.slice(ia+1) );
|
|
}
|
|
}
|
|
workflow.has_changes = true;
|
|
});
|
|
$('#workflow-output-area').show();
|
|
}
|
|
|
|
function layout_editor() {
|
|
workflow.layout();
|
|
workflow.fit_canvas_to_nodes();
|
|
scroll_to_nodes();
|
|
canvas_manager.draw_overview();
|
|
}
|
|
|
|
function edit_workflow_attributes() {
|
|
workflow.clear_active_node();
|
|
$('.right-content').hide();
|
|
$('#edit-attributes').show();
|
|
|
|
}
|
|
|
|
// On load, set the size to the pref stored in local storage if it exists
|
|
overview_size = $.jStorage.get("overview-size");
|
|
if (overview_size !== undefined) {
|
|
$("#overview-border").css( {
|
|
width: overview_size,
|
|
height: overview_size
|
|
});
|
|
}
|
|
|
|
// Show viewport on load unless pref says it's off
|
|
if ($.jStorage.get("overview-off")) {
|
|
hide_overview();
|
|
} else {
|
|
show_overview();
|
|
}
|
|
|
|
// Stores the size of the overview into local storage when it's resized
|
|
$("#overview-border").bind( "dragend", function( e, d ) {
|
|
var op = $(this).offsetParent();
|
|
var opo = op.offset();
|
|
var new_size = Math.max( op.width() - ( d.offsetX - opo.left ),
|
|
op.height() - ( d.offsetY - opo.top ) );
|
|
$.jStorage.set("overview-size", new_size + "px");
|
|
});
|
|
|
|
function show_overview() {
|
|
$.jStorage.set("overview-off", false);
|
|
$("#overview-border").css("right", "0px");
|
|
$("#close-viewport").css("background-position", "0px 0px");
|
|
}
|
|
|
|
function hide_overview() {
|
|
$.jStorage.set("overview-off", true);
|
|
$("#overview-border").css("right", "20000px");
|
|
$("#close-viewport").css("background-position", "12px 0px");
|
|
}
|
|
|
|
// Lets the overview be toggled visible and invisible, adjusting the arrows accordingly
|
|
$("#close-viewport").click( function() {
|
|
if ( $("#overview-border").css("right") === "0px" ) {
|
|
hide_overview();
|
|
} else {
|
|
show_overview();
|
|
}
|
|
});
|
|
|
|
// Unload handler
|
|
window.onbeforeunload = function() {
|
|
if ( workflow && workflow.has_changes ) {
|
|
return "There are unsaved changes to your workflow which will be lost.";
|
|
}
|
|
};
|
|
|
|
// Tool menu
|
|
$( "div.toolSectionBody" ).hide();
|
|
$( "div.toolSectionTitle > span" ).wrap( "<a href='#'></a>" );
|
|
var last_expanded = null;
|
|
$( "div.toolSectionTitle" ).each( function() {
|
|
var body = $(this).next( "div.toolSectionBody" );
|
|
$(this).click( function() {
|
|
if ( body.is( ":hidden" ) ) {
|
|
if ( last_expanded ) last_expanded.slideUp( "fast" );
|
|
last_expanded = body;
|
|
body.slideDown( "fast" );
|
|
}
|
|
else {
|
|
body.slideUp( "fast" );
|
|
last_expanded = null;
|
|
}
|
|
});
|
|
});
|
|
|
|
// Rename async.
|
|
async_save_text("workflow-name", "workflow-name", rename_async_url, "new_name");
|
|
|
|
// Tag async. Simply have the workflow edit element generate a click on the tag element to activate tagging.
|
|
$('#workflow-tag').click( function() {
|
|
$('.tag-area').click();
|
|
return false;
|
|
});
|
|
// Annotate async.
|
|
async_save_text("workflow-annotation", "workflow-annotation", annotate_async_url, "new_annotation", 25, true, 4);
|
|
});
|
|
|
|
// Global state for the whole workflow
|
|
function reset() {
|
|
if ( workflow ) {
|
|
workflow.remove_all();
|
|
}
|
|
workflow = new Workflow( $("#canvas-container") );
|
|
}
|
|
|
|
function scroll_to_nodes() {
|
|
var cv = $("#canvas-viewport");
|
|
var cc = $("#canvas-container");
|
|
var top, left;
|
|
if ( cc.width() < cv.width() ) {
|
|
left = ( cv.width() - cc.width() ) / 2;
|
|
} else {
|
|
left = 0;
|
|
}
|
|
if ( cc.height() < cv.height() ) {
|
|
top = ( cv.height() - cc.height() ) / 2;
|
|
} else {
|
|
top = 0;
|
|
}
|
|
cc.css( { left: left, top: top } );
|
|
}
|
|
|
|
// Add a new step to the workflow by tool id
|
|
function add_node_for_tool( id, title ) {
|
|
var node = prebuild_node( 'tool', title, id );
|
|
workflow.add_node( node );
|
|
workflow.fit_canvas_to_nodes();
|
|
canvas_manager.draw_overview();
|
|
workflow.activate_node( node );
|
|
$.ajax( {
|
|
url: get_new_module_info_url,
|
|
data: { type: "tool", tool_id: id, "_": "true" },
|
|
global: false,
|
|
dataType: "json",
|
|
success: function( data ) {
|
|
node.init_field_data( data );
|
|
},
|
|
error: function( x, e ) {
|
|
var m = "error loading field data";
|
|
if ( x.status === 0 ) {
|
|
m += ", server unavailable";
|
|
}
|
|
node.error( m );
|
|
}
|
|
});
|
|
}
|
|
|
|
function add_node_for_module( type, title ) {
|
|
node = prebuild_node( type, title );
|
|
workflow.add_node( node );
|
|
workflow.fit_canvas_to_nodes();
|
|
canvas_manager.draw_overview();
|
|
workflow.activate_node( node );
|
|
$.ajax( {
|
|
url: get_new_module_info_url,
|
|
data: { type: type, "_": "true" },
|
|
dataType: "json",
|
|
success: function( data ) {
|
|
node.init_field_data( data );
|
|
},
|
|
error: function( x, e ) {
|
|
var m = "error loading field data"
|
|
if ( x.status == 0 ) {
|
|
m += ", server unavailable"
|
|
}
|
|
node.error( m );
|
|
}
|
|
});
|
|
}
|
|
|
|
// This function preloads how to display known pja's.
|
|
function display_pja(pja, node) {
|
|
// DBTODO SANITIZE INPUTS.
|
|
$("#pja_container").append( get_pja_form(pja) );
|
|
$("#pja_container>.toolForm:last>.toolFormTitle>.buttons").click(function (){
|
|
action_to_rem = $(this).closest(".toolForm", ".action_tag").children(".action_tag:first").text();
|
|
$(this).closest(".toolForm").remove();
|
|
delete workflow.active_node.post_job_actions[action_to_rem];
|
|
workflow.active_form_has_changes = true;
|
|
});
|
|
}
|
|
|
|
function display_pja_list(){
|
|
return pja_list;
|
|
}
|
|
|
|
function display_file_list(node){
|
|
addlist = "<select id='node_data_list' name='node_data_list'>";
|
|
for (var out_terminal in node.output_terminals){
|
|
addlist += "<option value='" + out_terminal + "'>"+ out_terminal +"</option>";
|
|
}
|
|
addlist += "</select>";
|
|
return addlist;
|
|
}
|
|
|
|
function new_pja(action_type, target, node){
|
|
if (node.post_job_actions === undefined){
|
|
//New tool node, set up dict.
|
|
node.post_job_actions = {};
|
|
}
|
|
if (node.post_job_actions[action_type+target] === undefined){
|
|
var new_pja = {};
|
|
new_pja.action_type = action_type;
|
|
new_pja.output_name = target;
|
|
node.post_job_actions[action_type+target] = null;
|
|
node.post_job_actions[action_type+target] = new_pja;
|
|
display_pja(new_pja, node);
|
|
workflow.active_form_has_changes = true;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function show_workflow_parameters(){
|
|
var parameter_re = /\$\{.+?\}/g;
|
|
var workflow_parameters = [];
|
|
var wf_parm_container = $("#workflow-parameters-container");
|
|
var wf_parm_box = $("#workflow-parameters-box");
|
|
var new_parameter_content = "";
|
|
var matches = [];
|
|
$.each(workflow.nodes, function (k, node){
|
|
var form_matches = node.form_html.match(parameter_re);
|
|
if (form_matches){
|
|
matches = matches.concat(form_matches);
|
|
}
|
|
if (node.post_job_actions){
|
|
$.each(node.post_job_actions, function(k, pja){
|
|
if (pja.action_arguments){
|
|
$.each(pja.action_arguments, function(k, action_argument){
|
|
var arg_matches = action_argument.match(parameter_re);
|
|
if (arg_matches){
|
|
matches = matches.concat(arg_matches);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
if (matches){
|
|
$.each(matches, function(k, element){
|
|
if ($.inArray(element, workflow_parameters) === -1){
|
|
workflow_parameters.push(element);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
if (workflow_parameters && workflow_parameters.length !== 0){
|
|
$.each(workflow_parameters, function(k, element){
|
|
new_parameter_content += "<div>" + element.substring(2, element.length -1) + "</div>";
|
|
});
|
|
wf_parm_container.html(new_parameter_content);
|
|
wf_parm_box.show();
|
|
}else{
|
|
wf_parm_container.html(new_parameter_content);
|
|
wf_parm_box.hide();
|
|
}
|
|
}
|
|
|
|
function show_form_for_tool( text, node ) {
|
|
$('.right-content').hide();
|
|
$("#right-content").show().html( text );
|
|
// Add metadata form to tool.
|
|
if (node) {
|
|
$("#right-content").find(".toolForm:first").after( "<p><div class='metadataForm'> \
|
|
<div class='metadataFormTitle'>Edit Step Attributes</div> \
|
|
<div class='form-row'> \
|
|
<label>Annotation / Notes:</label> \
|
|
<div style='margin-right: 10px;'> \
|
|
<textarea name='annotation' rows='3' style='width: 100%'>" + node.annotation + "</textarea> \
|
|
<div class='toolParamHelp'>Add an annotation or notes to this step; annotations are available when a workflow is viewed.</div> \
|
|
</div> \
|
|
</div> \
|
|
</div>" );
|
|
}
|
|
// Add step actions.
|
|
if (node && node.type=='tool'){
|
|
pjastr = "<p><div class='metadataForm'><div class='metadataFormTitle'>Edit Step Actions</div><div class='form-row'> \
|
|
" + display_pja_list() + " <br/> "+ display_file_list(node) + " <div class='action-button' style='border:1px solid black;display:inline;' id='add_pja'>Create</div>\
|
|
</div><div class='form-row'>\
|
|
<div style='margin-right: 10px;'><span id='pja_container'></span>";
|
|
pjastr += "<div class='toolParamHelp'>Add actions to this step; actions are applied when this workflow step completes.</div></div></div></div>";
|
|
$("#right-content").find(".toolForm").after( pjastr );
|
|
for (var key in node.post_job_actions){
|
|
if (key != "undefined"){ //To make sure we haven't just deleted it.
|
|
display_pja(node.post_job_actions[key], node);
|
|
}
|
|
}
|
|
$("#add_pja").click(function (){
|
|
new_pja($("#new_pja_list").val(),$("#node_data_list").val(), node);
|
|
});
|
|
}
|
|
$("#right-content").find( "form" ).ajaxForm( {
|
|
type: 'POST',
|
|
dataType: 'json',
|
|
success: function( data ) {
|
|
workflow.active_form_has_changes = false;
|
|
node.update_field_data( data );
|
|
show_workflow_parameters();
|
|
},
|
|
beforeSubmit: function( data ) {
|
|
data.push( { name: 'tool_state', value: node.tool_state } );
|
|
data.push( { name: '_', value: "true" } );
|
|
}
|
|
}).each( function() {
|
|
var form = this;
|
|
$(this).find( "select[refresh_on_change='true']").change( function() {
|
|
$(form).submit();
|
|
});
|
|
$(this).find( ".popupmenu" ).each( function() {
|
|
var id = $(this).parents( "div.form-row" ).attr( 'id' );
|
|
var b = $('<a class="popup-arrow" id="popup-arrow-for-' + id + '">▼</a>');
|
|
var options = {};
|
|
$(this).find( "button" ).each( function() {
|
|
var name = $(this).attr( 'name' );
|
|
var value = $(this).attr( 'value' );
|
|
options[ $(this).text() ] = function() {
|
|
$(form).append( "<input type='hidden' name='"+name+"' value='"+value+"' />" ).submit();
|
|
};
|
|
});
|
|
b.insertAfter( this );
|
|
$(this).remove();
|
|
make_popupmenu( b, options );
|
|
});
|
|
// Implements auto-saving based on whether the inputs change. We consider
|
|
// "changed" to be when a field is accessed and not necessarily modified
|
|
// because of an issue where "onchange" is not triggered when activating
|
|
// another node, or saving the workflow.
|
|
$(this).find("input,textarea,select").each( function() {
|
|
$(this).bind("focus click", function() {
|
|
workflow.active_form_has_changes = true;
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
var close_editor = function() {
|
|
workflow.check_changes_in_active_form();
|
|
if ( workflow && workflow.has_changes ) {
|
|
do_close = function() {
|
|
window.onbeforeunload = undefined;
|
|
window.document.location = workflow_index_url;
|
|
};
|
|
show_modal( "Close workflow editor",
|
|
"There are unsaved changes to your workflow which will be lost.",
|
|
{
|
|
"Cancel" : hide_modal,
|
|
"Save Changes" : function() {
|
|
save_current_workflow( null, do_close );
|
|
}
|
|
}, {
|
|
"Don't Save": do_close
|
|
} );
|
|
} else {
|
|
window.document.location = workflow_index_url;
|
|
}
|
|
};
|
|
|
|
var save_current_workflow = function ( eventObj, success_callback ) {
|
|
show_message( "Saving workflow", "progress" );
|
|
workflow.check_changes_in_active_form();
|
|
if (!workflow.has_changes) {
|
|
hide_modal();
|
|
if ( success_callback ) {
|
|
success_callback();
|
|
}
|
|
return;
|
|
}
|
|
workflow.rectify_workflow_outputs();
|
|
var savefn = function(callback) {
|
|
$.ajax( {
|
|
url: save_workflow_url,
|
|
type: "POST",
|
|
data: {
|
|
id: workflow_id,
|
|
workflow_data: function() { return JSON.stringify( workflow.to_simple() ); },
|
|
"_": "true"
|
|
},
|
|
dataType: 'json',
|
|
success: function( data ) {
|
|
var body = $("<div></div>").text( data.message );
|
|
if ( data.errors ) {
|
|
body.addClass( "warningmark" );
|
|
var errlist = $( "<ul/>" );
|
|
$.each( data.errors, function( i, v ) {
|
|
$("<li></li>").text( v ).appendTo( errlist );
|
|
});
|
|
body.append( errlist );
|
|
} else {
|
|
body.addClass( "donemark" );
|
|
}
|
|
workflow.name = data.name;
|
|
workflow.has_changes = false;
|
|
workflow.stored = true;
|
|
show_workflow_parameters();
|
|
if ( data.errors ) {
|
|
show_modal( "Saving workflow", body, { "Ok" : hide_modal } );
|
|
} else {
|
|
if (callback) {
|
|
callback();
|
|
}
|
|
hide_modal();
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
// We bind to ajaxStop because of auto-saving, since the form submission ajax
|
|
// call needs to be completed so that the new data is saved
|
|
if (active_ajax_call) {
|
|
$(document).bind('ajaxStop.save_workflow', function() {
|
|
$(document).unbind('ajaxStop.save_workflow');
|
|
savefn();
|
|
$(document).unbind('ajaxStop.save_workflow'); // IE7 needs it here
|
|
active_ajax_call = false;
|
|
});
|
|
} else {
|
|
savefn(success_callback);
|
|
}
|
|
};
|