Very preliminary start on the workflow editor. Currently:

- Can add steps (tool invocations) to the workflow
 - Can modify options for tools that do not use dynamic options or nesting
 - Can connect outputs to inputs, the editor does check type compatibility
   (however this breaks down completely for the "input" type)

There is no way to save or run workflows yet (and not data model for them).

You'll need to set "enable_beta_features = True" in your config for this to
be accessible.
This commit is contained in:
James Taylor
2007-10-12 21:09:05 +00:00
parent 6c42599ddc
commit f14ba168aa
7 changed files with 981 additions and 1 deletions
+1
View File
@@ -22,6 +22,7 @@ class Configuration( object ):
def __init__( self, **kwargs ):
self.config_dict = kwargs
self.root = kwargs.get( 'root_dir', '.' )
self.enable_beta_features = kwargs.get( "enable_beta_features", False )
self.database = resolve_path( kwargs.get( "database_file", "database/universe.d" ), self.root )
self.database_connection = kwargs.get( "database_connection", False )
self.file_path = resolve_path( kwargs.get( "file_path", "database/files" ), self.root )
+2
View File
@@ -15,6 +15,8 @@ class BaseController( object ):
"""
Base class for Galaxy web application controllers.
"""
beta = False
def __init__( self, app ):
"""Initialize an interface for application 'app'"""
+2 -1
View File
@@ -41,7 +41,8 @@ def add_controllers( webapp, app ):
for key in dir( module ):
T = getattr( module, key )
if isclass( T ) and T is not BaseController and issubclass( T, BaseController ):
webapp.add_controller( name, T( app ) )
if app.config.enable_beta_features or not ( T.beta ):
webapp.add_controller( name, T( app ) )
def app_factory( global_conf, **kwargs ):
"""
@@ -0,0 +1,65 @@
from galaxy.web.base.controller import *
from galaxy.tools.parameters import DataToolParameter
from galaxy.datatypes.data import Data
class WorkflowEditor( BaseController ):
beta = True
@web.expose
def index( self, trans ):
return trans.fill_template( "workflow_editor/index.mako" )
@web.expose
def canvas( self, trans ):
return trans.fill_template( "workflow_editor/canvas.mako" )
@web.expose
def tool_form( self, trans, tool_id ):
tool = trans.app.toolbox.tools_by_id[tool_id]
return trans.fill_template( "workflow_editor/tool_form.mako", tool=tool, as_html=as_html )
@web.json
def get_tool_info( self, trans, tool_id ):
tool = trans.app.toolbox.tools_by_id[tool_id]
rval = {}
rval['name'] = tool.name
data_inputs = []
for name, input in tool.inputs.iteritems():
d = dict( data=False, name=input.name, label=input.label, html=as_html( input, None ) )
if isinstance( input, DataToolParameter ):
d.update( dict( data=True, extensions=input.extensions ) )
data_inputs.append( d )
rval['data_inputs'] = data_inputs
data_outputs = []
for name, ( format, metadata_source, parent ) in tool.outputs.iteritems():
data_outputs.append( dict( name=name, extension=format ) )
rval['data_outputs'] = data_outputs
return rval
@web.json
def get_datatypes( self, trans ):
ext_to_class_name = dict()
classes = []
for k, v in trans.app.datatypes_registry.datatypes_by_extension.iteritems():
c = v.__class__
ext_to_class_name[k] = c.__module__ + "." + c.__name__
classes.append( c )
class_to_classes = dict()
def visit_bases( types, cls ):
for base in cls.__bases__:
if issubclass( base, Data ):
types.add( base.__module__ + "." + base.__name__ )
visit_bases( types, base )
for c in classes:
n = c.__module__ + "." + c.__name__
types = set( [ n ] )
visit_bases( types, c )
class_to_classes[ n ] = dict( ( t, True ) for t in types )
return dict( ext_to_class_name=ext_to_class_name, class_to_classes=class_to_classes )
def as_html( param, trans ):
if type( param ) is DataToolParameter:
return "Data input '" + param.name + "' (" + ( " or ".join( param.extensions ) ) + ")"
else:
return param.get_html( trans )
+361
View File
@@ -0,0 +1,361 @@
/*
* jQuery blockUI plugin
* Version 1.33 (09/14/2007)
* @requires jQuery v1.1.1
*
* $Id$
*
* Examples at: http://malsup.com/jquery/block/
* Copyright (c) 2007 M. Alsup
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*/
(function($) {
/**
* blockUI provides a mechanism for blocking user interaction with a page (or parts of a page).
* This can be an effective way to simulate synchronous behavior during ajax operations without
* locking the browser. It will prevent user operations for the current page while it is
* active ane will return the page to normal when it is deactivate. blockUI accepts the following
* two optional arguments:
*
* message (String|Element|jQuery): The message to be displayed while the UI is blocked. The message
* argument can be a plain text string like "Processing...", an HTML string like
* "<h1><img src="busy.gif" /> Please wait...</h1>", a DOM element, or a jQuery object.
* The default message is "<h1>Please wait...</h1>"
*
* css (Object): Object which contains css property/values to override the default styles of
* the message. Use this argument if you wish to override the default
* styles. The css Object should be in a format suitable for the jQuery.css
* function. For example:
* $.blockUI({
* backgroundColor: '#ff8',
* border: '5px solid #f00,
* fontWeight: 'bold'
* });
*
* The default blocking message used when blocking the entire page is "<h1>Please wait...</h1>"
* but this can be overridden by assigning a value to $.blockUI.defaults.pageMessage in your
* own code. For example:
*
* $.blockUI.defaults.pageMessage = "<h1>Bitte Wartezeit</h1>";
*
* The default message styling can also be overridden. For example:
*
* $.extend($.blockUI.defaults.pageMessageCSS, { color: '#00a', backgroundColor: '#0f0' });
*
* The default styles work well for simple messages like "Please wait", but for longer messages
* style overrides may be necessary.
*
* @example $.blockUI();
* @desc prevent user interaction with the page (and show the default message of 'Please wait...')
*
* @example $.blockUI( { backgroundColor: '#f00', color: '#fff'} );
* @desc prevent user interaction and override the default styles of the message to use a white on red color scheme
*
* @example $.blockUI('Processing...');
* @desc prevent user interaction and display the message "Processing..." instead of the default message
*
* @name blockUI
* @param String|jQuery|Element message Message to display while the UI is blocked
* @param Object css Style object to control look of the message
* @cat Plugins/blockUI
*/
$.blockUI = function(msg, css, opts) {
$.blockUI.impl.install(window, msg, css, opts);
};
// expose version number so other plugins can interogate
$.blockUI.version = 1.33;
/**
* unblockUI removes the UI block that was put in place by blockUI
*
* @example $.unblockUI();
* @desc unblocks the page
*
* @name unblockUI
* @cat Plugins/blockUI
*/
$.unblockUI = function(opts) {
$.blockUI.impl.remove(window, opts);
};
/**
* Blocks user interaction with the selected elements. (Hat tip: Much of
* this logic comes from Brandon Aaron's bgiframe plugin. Thanks, Brandon!)
* By default, no message is displayed when blocking elements.
*
* @example $('div.special').block();
* @desc prevent user interaction with all div elements with the 'special' class.
*
* @example $('div.special').block('Please wait');
* @desc prevent user interaction with all div elements with the 'special' class
* and show a message over the blocked content.
*
* @name block
* @type jQuery
* @param String|jQuery|Element message Message to display while the element is blocked
* @param Object css Style object to control look of the message
* @cat Plugins/blockUI
*/
$.fn.block = function(msg, css, opts) {
return this.each(function() {
if (!this.$pos_checked) {
if ($.css(this,"position") == 'static')
this.style.position = 'relative';
if ($.browser.msie) this.style.zoom = 1; // force 'hasLayout' in IE
this.$pos_checked = 1;
}
$.blockUI.impl.install(this, msg, css, opts);
});
};
/**
* Unblocks content that was blocked by "block()"
*
* @example $('div.special').unblock();
* @desc unblocks all div elements with the 'special' class.
*
* @name unblock
* @type jQuery
* @cat Plugins/blockUI
*/
$.fn.unblock = function(opts) {
return this.each(function() {
$.blockUI.impl.remove(this, opts);
});
};
/**
* displays the first matched element in a "display box" above a page overlay.
*
* @example $('#myImage').displayBox();
* @desc displays "myImage" element in a box
*
* @name displayBox
* @type jQuery
* @cat Plugins/blockUI
*/
$.fn.displayBox = function(css, fn, isFlash) {
var msg = this[0];
if (!msg) return;
var $msg = $(msg);
css = css || {};
var w = $msg.width() || $msg.attr('width') || css.width || $.blockUI.defaults.displayBoxCSS.width;
var h = $msg.height() || $msg.attr('height') || css.height || $.blockUI.defaults.displayBoxCSS.height ;
if (w[w.length-1] == '%') {
var ww = document.documentElement.clientWidth || document.body.clientWidth;
w = parseInt(w) || 100;
w = (w * ww) / 100;
}
if (h[h.length-1] == '%') {
var hh = document.documentElement.clientHeight || document.body.clientHeight;
h = parseInt(h) || 100;
h = (h * hh) / 100;
}
var ml = '-' + parseInt(w)/2 + 'px';
var mt = '-' + parseInt(h)/2 + 'px';
// supress opacity on overlay if displaying flash content on mac/ff platform
var ua = navigator.userAgent.toLowerCase();
var opts = {
displayMode: fn || 1,
noalpha: isFlash && /mac/.test(ua) && /firefox/.test(ua)
};
$.blockUI.impl.install(window, msg, { width: w, height: h, marginTop: mt, marginLeft: ml }, opts);
};
// override these in your code to change the default messages and styles
$.blockUI.defaults = {
// the message displayed when blocking the entire page
pageMessage: '<h1>Please wait...</h1>',
// the message displayed when blocking an element
elementMessage: '', // none
// styles for the overlay iframe
overlayCSS: { backgroundColor: '#fff', opacity: '0.5' },
// styles for the message when blocking the entire page
pageMessageCSS: { width:'250px', margin:'-50px 0 0 -125px', top:'50%', left:'50%', textAlign:'center', color:'#000', backgroundColor:'#fff', border:'3px solid #aaa' },
// styles for the message when blocking an element
elementMessageCSS: { width:'250px', padding:'10px', textAlign:'center', backgroundColor:'#fff'},
// styles for the displayBox
displayBoxCSS: { width: '400px', height: '400px', top:'50%', left:'50%' },
// allow body element to be stetched in ie6
ie6Stretch: 1,
// supress tab nav from leaving blocking content?
allowTabToLeave: 0,
// Title attribute for overlay when using displayBox
closeMessage: 'Click to close',
// use fadeOut effect when unblocking (can be overridden on unblock call)
fadeOut: 1,
// fadeOut transition time in millis
fadeTime: 400
};
// the gory details
$.blockUI.impl = {
box: null,
boxCallback: null,
pageBlock: null,
pageBlockEls: [],
op8: window.opera && window.opera.version() < 9,
ie6: $.browser.msie && /MSIE 6.0/.test(navigator.userAgent),
install: function(el, msg, css, opts) {
opts = opts || {};
this.boxCallback = typeof opts.displayMode == 'function' ? opts.displayMode : null;
this.box = opts.displayMode ? msg : null;
var full = (el == window);
// use logical settings for opacity support based on browser but allow overrides via opts arg
var noalpha = this.op8 || $.browser.mozilla && /Linux/.test(navigator.platform);
if (typeof opts.alphaOverride != 'undefined')
noalpha = opts.alphaOverride == 0 ? 1 : 0;
if (full && this.pageBlock) this.remove(window, {fadeOut:0});
// check to see if we were only passed the css object (a literal)
if (msg && typeof msg == 'object' && !msg.jquery && !msg.nodeType) {
css = msg;
msg = null;
}
msg = msg ? (msg.nodeType ? $(msg) : msg) : full ? $.blockUI.defaults.pageMessage : $.blockUI.defaults.elementMessage;
if (opts.displayMode)
var basecss = jQuery.extend({}, $.blockUI.defaults.displayBoxCSS);
else
var basecss = jQuery.extend({}, full ? $.blockUI.defaults.pageMessageCSS : $.blockUI.defaults.elementMessageCSS);
css = jQuery.extend(basecss, css || {});
var f = ($.browser.msie) ? $('<iframe class="blockUI" style="z-index:1000;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="javascript:false;"></iframe>')
: $('<div class="blockUI" style="display:none"></div>');
var w = $('<div class="blockUI" style="z-index:1001;cursor:wait;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
var m = full ? $('<div class="blockUI blockMsg" style="z-index:1002;cursor:wait;padding:0;position:fixed"></div>')
: $('<div class="blockUI" style="display:none;z-index:1002;cursor:wait;position:absolute"></div>');
w.css('position', full ? 'fixed' : 'absolute');
if (msg) m.css(css);
if (!noalpha) w.css($.blockUI.defaults.overlayCSS);
if (this.op8) w.css({ width:''+el.clientWidth,height:''+el.clientHeight }); // lame
if ($.browser.msie) f.css('opacity','0.0');
$([f[0],w[0],m[0]]).appendTo(full ? 'body' : el);
// ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
var expr = $.browser.msie && (!$.boxModel || $('object,embed', full ? null : el).length > 0);
if (this.ie6 || expr) {
// stretch content area if it's short
if (full && $.blockUI.defaults.ie6Stretch && $.boxModel)
$('html,body').css('height','100%');
// fix ie6 problem when blocked element has a border width
if ((this.ie6 || !$.boxModel) && !full) {
var t = this.sz(el,'borderTopWidth'), l = this.sz(el,'borderLeftWidth');
var fixT = t ? '(0 - '+t+')' : 0;
var fixL = l ? '(0 - '+l+')' : 0;
}
// simulate fixed position
$.each([f,w,m], function(i,o) {
var s = o[0].style;
s.position = 'absolute';
if (i < 2) {
full ? s.setExpression('height','document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + "px"')
: s.setExpression('height','this.parentNode.offsetHeight + "px"');
full ? s.setExpression('width','jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"')
: s.setExpression('width','this.parentNode.offsetWidth + "px"');
if (fixL) s.setExpression('left', fixL);
if (fixT) s.setExpression('top', fixT);
}
else {
if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');
s.marginTop = 0;
}
});
}
if (opts.displayMode) {
w.css('cursor','default').attr('title', $.blockUI.defaults.closeMessage);
m.css('cursor','default');
$([f[0],w[0],m[0]]).removeClass('blockUI').addClass('displayBox');
$().click($.blockUI.impl.boxHandler).bind('keypress', $.blockUI.impl.boxHandler);
}
else
this.bind(1, el);
m.append(msg).show();
if (msg.jquery) msg.show();
if (opts.displayMode) return;
if (full) {
this.pageBlock = m[0];
this.pageBlockEls = $(':input:enabled:visible',this.pageBlock);
setTimeout(this.focus, 20);
}
else this.center(m[0]);
},
remove: function(el, opts) {
var o = $.extend({}, $.blockUI.defaults, opts);
this.bind(0, el);
var full = el == window;
var els = full ? $('body').children().filter('.blockUI') : $('.blockUI', el);
if (full) this.pageBlock = this.pageBlockEls = null;
if (o.fadeOut) {
els.fadeOut(o.fadeTime, function() {
if (this.parentNode) this.parentNode.removeChild(this);
});
}
else els.remove();
},
boxRemove: function(el) {
$().unbind('click',$.blockUI.impl.boxHandler).unbind('keypress', $.blockUI.impl.boxHandler);
if (this.boxCallback)
this.boxCallback(this.box);
$('body .displayBox').hide().remove();
},
// event handler to suppress keyboard/mouse events when blocking
handler: function(e) {
if (e.keyCode && e.keyCode == 9) {
if ($.blockUI.impl.pageBlock && !$.blockUI.defaults.allowTabToLeave) {
var els = $.blockUI.impl.pageBlockEls;
var fwd = !e.shiftKey && e.target == els[els.length-1];
var back = e.shiftKey && e.target == els[0];
if (fwd || back) {
setTimeout(function(){$.blockUI.impl.focus(back)},10);
return false;
}
}
}
if ($(e.target).parents('div.blockMsg').length > 0)
return true;
return $(e.target).parents().children().filter('div.blockUI').length == 0;
},
boxHandler: function(e) {
if ((e.keyCode && e.keyCode == 27) || (e.type == 'click' && $(e.target).parents('div.blockMsg').length == 0))
$.blockUI.impl.boxRemove();
return true;
},
// bind/unbind the handler
bind: function(b, el) {
var full = el == window;
// don't bother unbinding if there is nothing to unbind
if (!b && (full && !this.pageBlock || !full && !el.$blocked)) return;
if (!full) el.$blocked = b;
var $e = $(el).find('a,:input');
$.each(['mousedown','mouseup','keydown','keypress','click'], function(i,o) {
$e[b?'bind':'unbind'](o, $.blockUI.impl.handler);
});
},
focus: function(back) {
if (!$.blockUI.impl.pageBlockEls) return;
var e = $.blockUI.impl.pageBlockEls[back===true ? $.blockUI.impl.pageBlockEls.length-1 : 0];
if (e) e.focus();
},
center: function(el) {
var p = el.parentNode, s = el.style;
var l = ((p.offsetWidth - el.offsetWidth)/2) - this.sz(p,'borderLeftWidth');
var t = ((p.offsetHeight - el.offsetHeight)/2) - this.sz(p,'borderTopWidth');
s.left = l > 0 ? (l+'px') : '0';
s.top = t > 0 ? (t+'px') : '0';
},
sz: function(el, p) { return parseInt($.css(el,p))||0; }
};
})(jQuery);
+396
View File
@@ -0,0 +1,396 @@
<html>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
<style>
* { z-index: 500;}
canvas { position: absolute; z-index: 10; }
canvas.dragging { position: absolute; z-index: 1000; }
.node { position: absolute; width: 100px; background: green; }
.input-terminal { width: 10px; height: 10px; background: red; position: absolute; bottom: 0; left: -17px; }
.output-terminal { width: 10px; height: 10px; background: blue; position: absolute; bottom: 0; right: -17px; }
.drag-terminal { position: absolute; z-index: 1500; width: 10px; height: 10px; background: yellow; }
.input-terminal-active { background: yellow; }
.input-terminal-hover { background: yellow; border: solid black 1px; }
.unselectable { -moz-user-select: none; -khtml-user-select: none; user-select: none; }
</style>
<script type='text/javascript' src="/static/scripts/jquery.js"> </script>
<script type='text/javascript' src="/static/scripts/jquery.dimensions.js"> </script>
<script type='text/javascript' src="/static/scripts/jquery.ui.js"> </script>
<script>
var console = window.console;
console.log( "starting" );
## var old_shim_w = 10;
## var old_shim_h = 10;
## $.ui.plugin.add("draggable", "start", "grow", function(e,ui) {
## old_shim_w = $("#shim").width();
## old_shim_h = $("#shim").height();
## });
## $.ui.plugin.add("draggable", "drag", "grow", function(e,ui) {
## if ( ui.draggable.rpos[1] > $("#shim").height() - 100 ) { $("#shim").css( "height", $("#shim").height() + 200 ) }
## if ( ui.draggable.rpos[0] > $("#shim").width() - 100 ) { $("#shim").css( "width", $("#shim").width() + 200 ) }
## });
## $.ui.plugin.add("draggable", "stop", "grow", function(e,ui) {
## if ( ui.draggable.rpos[1] > old_shim_h - 100 ) { $("#shim").css( "height", Math.ceil( ui.draggable.rpos[1] / 200 ) * 200 ) }
## if ( ui.draggable.rpos[0] > old_shim_w - 100 ) { $("#shim").css( "width", Math.ceil( ui.draggable.rpos[0] / 200 ) * 200 ) }
## old_shim_w = $("#shim").width();
## old_shim_h = $("#shim").height();
## });
</script>
<script type='text/javascript'>
function OutputTerminal( element, datatype ) {
this.element = element;
this.connectors = [];
this.datatype = datatype
}
$.extend( OutputTerminal.prototype, {
connect: function ( connector ) {
this.connectors.push( connector );
},
disconnect: function ( connector ) {
this.connectors.splice( this.connectors.indexOf( connector ), 1 );
},
redraw: function () {
$.each( this.connectors, function( _, c ) {
c.redraw();
})
}
} )
function InputTerminal( element, datatypes ) {
this.element = element;
this.connectors = [];
this.max_connections = 1;
this.datatypes = datatypes;
}
$.extend( InputTerminal.prototype, {
connect: function ( connector ) {
this.connectors.push( connector );
},
disconnect: function ( connector ) {
this.connectors.splice( this.connectors.indexOf( connector ), 1 );
},
can_accept: function ( other ) {
if ( this.connectors.length < this.max_connections ) {
for ( t in this.datatypes ) {
## FIXME: No idea what to do about this case
if ( other.datatype == "input" ) { return true; }
if ( issubtype( other.datatype, this.datatypes[t] ) ) {
return true;
}
}
}
return false;
},
redraw: function () {
$.each( this.connectors, function( _, c ) {
c.redraw();
})
}
} )
function Connector( handle1, handle2 ) {
this.canvas = null;
this.dragging = false;
}
$.extend( Connector.prototype, {
connect: function ( t1, t2 ) {
this.handle1 = t1;
this.handle1.connect( this );
this.handle2 = t2;
this.handle2.connect( this );
},
destroy : function () {
if ( this.handle1 ) {
this.handle1.disconnect( this );
}
if ( this.handle2 ) {
this.handle2.disconnect( this );
}
$(this.canvas).remove();
},
redraw : function () {
if ( ! this.canvas ) {
this.canvas = document.createElement( "canvas" );
$("body").append( this.canvas );
if ( this.dragging ) { this.canvas.style.zIndex = "300" }
}
// Find the position of each handle
var o = $(this.handle1.element).offset();
var start_x = o.left + 5;
var start_y = o.top + 5;
var o = $(this.handle2.element).offset();
var end_x = o.left + 5;
var end_y = o.top + 5;
// Calculate canvas area
var canvas_extra = 50;
var canvas_min_x = Math.min( start_x, end_x );
var canvas_max_x = Math.max( start_x, end_x );
var canvas_min_y = Math.min( start_y, end_y );
var canvas_max_y = Math.max( start_y, end_y );
var cp_shift = Math.max( ( canvas_max_y - canvas_min_y ) / 2, 100 );
var canvas_left = canvas_min_x - canvas_extra;
var canvas_top = canvas_min_y - canvas_extra;
var canvas_width = canvas_max_x - canvas_min_x + 2 * canvas_extra;
var canvas_height = canvas_max_y - canvas_min_y + 2 * canvas_extra;
// Place the canvas
this.canvas.style.left = canvas_left + "px";
this.canvas.style.top = canvas_top + "px";
this.canvas.setAttribute( "width", canvas_width );
this.canvas.setAttribute( "height", canvas_height );
// Adjust points to be relative to the canvas
start_x -= canvas_left;
start_y -= canvas_top;
end_x -= canvas_left;
end_y -= canvas_top;
// Draw the line
var c = this.canvas.getContext("2d");
c.lineCap = "round";
c.strokeStyle = "#999";
c.lineWidth = 7;
c.beginPath();
c.moveTo( start_x, start_y );
c.bezierCurveTo( start_x + cp_shift, start_y, end_x - cp_shift, end_y, end_x, end_y );
c.stroke();
// Inner line
c.strokeStyle = "#EEEEEE";
c.lineWidth = 5;
c.beginPath();
c.moveTo( start_x, start_y );
c.bezierCurveTo( start_x + cp_shift, start_y, end_x - cp_shift, end_y, end_x, end_y );
c.stroke();
}
} )
function enable_input_terminal( element, types ) {
$(element).each( function() {
var terminal = this.terminal = new InputTerminal( this, types );
$(this).droppable( {
tolerance: 'intersect',
accept: function( draggable ) {
return ( draggable.terminal ) && ( terminal.can_accept( draggable.terminal ) );
},
activeClass: 'input-terminal-active',
hoverClass: 'input-terminal-active',
drop: function( e, d ) {
var source = d.draggable.element.terminal;
var target = d.droppable.element.terminal;
var c = new Connector();
c.connect( source, target );
c.redraw();
}
});
});
}
function enable_output_terminal( element, type ) {
$(element).each( function() {
var terminal = this.terminal = new OutputTerminal( this, type );
$(this).draggable( {
scroll: true,
// containment: 'document',
// appendTo: "body",
// cursorAt: { top: 5, left: 5 },
helper: function () {
var h = $( '<div class="drag-terminal" style="position: absolute;"></div>' ).get(0);
h.terminal = new OutputTerminal( h );
// // Already a connector? Destroy... no wait, don't
// if ( this.terminal.connector ) {
// this.terminal.connector.destroy();
// }
var c = new Connector();
this.drag_temp_connector = c;
c.dragging = true;
c.connect( this.terminal, h.terminal );
return h;
},
drag: function ( e, options ) {
options.helper.terminal.redraw();
},
stop: function( e, options ) {
this.drag_temp_connector.destroy();
// options.helper.remove();
}
});
});
}
function enable_drag( element ) {
$(element).draggable( {
cursor: 'move',
scroll: true,
scrollSensitivity: 100,
## grow: true,
start: function() {
//$(this).css( 'z-index', '1000' );
},
drag: function() {
$(this).find( ".terminal" ).each( function() {
this.terminal.redraw();
})
},
stop: function() {
//$(this).css( 'z-index', '100' );
$(this).find( ".terminal" ).each( function() {
this.terminal.redraw();
});
}
})
}
function add_node_for_tool( id ) {
$.getJSON( "${h.url_for( action='get_tool_info' )}", { tool_id: id }, function ( data ) {
var f = $("<div class='toolForm' style='position: absolute;'></div>");
var title = $("<div class='toolFormTitle unselectable'>" + data.name + "</div>" )
f.append( title );
f.appendTo( "body" );
var short_width = f.width();
console.log( ">>", short_width );
var b = $("<div class='toolFormBody'></div>")
b.append( $( "<div class='form-row titleRow'>Inputs:</div>" ) )
$.each( data.data_inputs, function( i, input ) {
if ( input.data ) {
t = $("<div class='terminal input-terminal'></div>")
enable_input_terminal( t, input.extensions );
b.append( $("<div class='form-row dataRow'><label>" + input.label + "</label><div>" + input.html + "</div></div>" ).prepend( t ) );
}
else
{
b.append( $("<div class='form-row fieldRow'><label>" + input.label + "</label><div>" + input.html + "</div></div>") );
}
});
b.append( $( "<div class='form-row titleRow'>Outputs:</div>" ) )
$.each( data.data_outputs, function( i, output ) {
var t = $( "<div class='terminal output-terminal'></div>" );
enable_output_terminal( t, output.extension );
b.append( $("<div class='form-row dataRow'>" + output.name + "</div>" ).append( t ) );
});
f.append( b )
// Fix width to computed width
f.css( "width", f.width() );
// Now add floats
var width = null;
var buttons = $("<div style='float: right'></div>");
var big = true;
buttons.append( $("<a href='#'>[+]</a>").click( function() {
b.children().not(".dataRow").toggle();
b.children( ".dataRow" ).children( "label" ).toggle();
f.css( "width", big ? short_width : width );
big = ! big;
f.find( ".terminal" ).each( function() {
this.terminal.redraw();
});
} ) );
width = f.width();
buttons.prependTo( title );
width += ( buttons.width() + 5 );
short_width += ( buttons.width() + 5 );
f.css( "width", width );
// Make draggable
enable_drag( f );
});
}
var ext_to_type = null;
var type_to_type = null;
function issubtype( child, parent ) {
child = ext_to_type[child];
parent = ext_to_type[parent];
return ( parent in type_to_type[child] );
}
$( function() {
$.getJSON( "${h.url_for( action='get_datatypes' )}", function( data ) {
ext_to_type = data.ext_to_class_name;
type_to_type = data.class_to_classes;
parent.notify();
});
})
</script>
<style>
body {
background: white url(${h.url_for('/static/images/light_gray_grid.gif')}) repeat;
}
#tool-area {
position: absolute;
top: 0; left: 0; bottom: 0;
width: 300px;
overflow: scroll;
}
div.toolMenu {
margin: 5px;
}
div.toolSectionPad {
margin: 0;
padding: 0;
height: 5px;
font-size: 0px;
}
div.toolSectionDetailsInner {
margin-left: 5px;
margin-right: 5px;
}
div.toolSectionTitle {
padding-bottom: 0px;
font-weight: bold;
}
div.toolTitle {
padding-top: 5px;
padding-bottom: 5px;
margin-left: 16px;
margin-right: 10px;
display: list-item;
list-style: square outside;
}
div.titleRow {
font-weight: bold;
border-bottom: dotted gray 1px;
margin-bottom: 0.5em;
padding-bottom: 0.25em;
}
div.form-row {
position: relative;
margin-bottom: 0.5em;
}
div.toolForm {
margin: 10px;
}
#canvas-area {
position: absolute;
top: 0; left: 305px; bottom: 0; right: 0;
border: solid red 1px;
overflow: none;
}
.form-row {
}
.form-row-body {
}
.form-row-clear {
clear: both;
}
</style>
</head>
<body>
## <div id="shim" style="position: absolute; top: 0px; left: 0px; width: 10px; height: 10px; background: red; opacity: 0.1"></div>
</body>
</html>
+154
View File
@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" />
<script type='text/javascript' src="/static/scripts/jquery.js"> </script>
<script type='text/javascript' src="/static/scripts/jquery.dimensions.js"> </script>
<script type='text/javascript' src="/static/scripts/jquery.ui.js"> </script>
<script type='text/javascript' src="/static/scripts/jquery.blockUI.js"> </script>
<script type='text/javascript' src="/static/scripts/jt.panels.js"> </script>
<script>
$( function() {
$.blockUI( "<b><img style='vertical-align: middle;' src='${h.url_for( '/static/style/data_running.gif' )}'/> Loading Galaxy Workflow Editor...</b>",
{ border: "solid #AAAA66 1px", background: "#FFFFCC", padding: "20px", width: "auto" } );
make_left_panel( $("#tool-area"), $("#canvas-area"), $("#left-border" ) );
// Insert div that covers everything when dragging the borders
$("<div id='DD-helper' style='background: white; top: 0; left: 0; width: 100%; height: 100%; position: absolute; z-index: 9000;'></div>")
.css("opacity", "0.01").appendTo("body").hide();
// Show border visual cues
$( "#left-border-inner" ).show();
})
function notify() {
$.unblockUI();
}
</script>
<style>
body, html
{
overflow: hidden;
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: #eee;
}
#tool-area {
background: url(${h.url_for('/static/style/menu_bg.png')});
## border-top: solid #999 1px;
position: absolute;
top: 0px; left: 0; bottom: 0;
width: 300px;
overflow: scroll;
}
div.toolMenu {
margin: 5px;
}
div.toolSectionPad {
margin: 0;
padding: 0;
height: 5px;
font-size: 0px;
}
div.toolSectionDetailsInner {
margin-left: 5px;
margin-right: 5px;
}
div.toolSectionTitle {
padding-bottom: 0px;
font-weight: bold;
}
div.toolTitle {
padding-top: 5px;
padding-bottom: 5px;
margin-left: 16px;
margin-right: 10px;
display: list-item;
list-style: square outside;
}
div.toolFormRow {
position: relative;
}
#canvas-area {
## border-top: solid #999 1px;
position: absolute;
top: 0; left: 305px; bottom: 0; right: 0;
overflow: none;
}
#left-border
{
position: absolute;
top: 0; left: 300px; bottom: 0; right: 0;
background: #eeeeee;
border-left: solid #999 1px;
border-right: solid #999 1px;
padding-right: 1px;
padding-left: 1px;
width: 5px;
z-index: 10000;
}
</style>
<script>
function add_node_for_tool( id ) {
window.frames.canvas.add_node_for_tool( id );
}
</script>
</head>
<body scroll="no">
## <div id="masthead">
## <iframe name="galaxy_masthead" src="${h.url_for( controller='root', action='masthead' )}" width="38" height="100%" frameborder="0" scroll="no" style="margin: 0; border: 0 none; width: 100%; height: 38px; overflow: hidden;"> </iframe>
## </div>
<div id="tool-area">
<div class="toolMenu">
<div class="toolSectionList">
%for i, section in enumerate( app.toolbox.sections ):
%if i > 0:
<div class="toolSectionPad"></div>
%endif
<div class="toolSectionTitle" id="title_${section.id}">
<span>${section.name}</span>
</div>
<div id="${section.id}" class="toolSectionBody">
<div class="toolSectionBg">
%for tool in section.tools:
%if not tool.hidden:
<div class="toolTitle">
## #if $tool.input_required
## #set $link = $h.url_for( 'tool_runner', tool_id=$tool.id )
## #else
## #set $link = $h.url_for( $tool.action, ** $tool.get_static_param_values( $t ) )
## #end if
%if "[[" in tool.description and "]]" in tool.description:
${tool.description.replace( '[[', '<a href="javascript:add_node_for_tool( ${tool.id} )">' % tool.id ).replace( "]]", "</a>" )}
%elif tool.name:
<a id="link-${tool.id}" href="javascript:add_node_for_tool( '${tool.id}' )">${tool.name}</a> ${tool.description}
%else:
<a id="link-${tool.id}" href="javascript:add_node_for_tool( '${tool.id}' )">${tool.description}</a>
%endif
</div>
%endif
%endfor
</div>
</div>
%endfor
</div>
</div>
</div>
<div id="left-border"><div id="left-border-inner" style="display: none;"></div></div>
<div id="canvas-area">
<iframe name="canvas" width="100%" height="100%" frameborder="0" src="${h.url_for( action='canvas' )}">
</div>
</body>
</html>