details: http://www.bx.psu.edu/hg/galaxy/rev/068d572a2bf8 changeset: 3313:068d572a2bf8 user: jeremy goecks <jeremy.goecks@emory.edu> date: Mon Feb 01 16:30:18 2010 -0500 description: Added UI support for annotating histories, HDAs, workflows, and workflow steps. Numerous small changes as well: (a) added an 'edit workflow attributes' to workflow editor; (b) added 'Edit tags and Annotation' area to history panel; (c) added generic javascript method to ajax text updates. Also fixed public URL column bug. diffstat: lib/galaxy/web/base/controller.py | 57 ++++++++++- lib/galaxy/web/controllers/history.py | 13 ++- lib/galaxy/web/controllers/root.py | 4 + lib/galaxy/web/controllers/workflow.py | 67 +++++++++++- static/june_2007_style/base.css.tmpl | 42 +++++++ static/june_2007_style/blue/base.css | 7 + static/june_2007_style/blue/history.css | 7 - static/june_2007_style/history.css.tmpl | 40 ------- static/scripts/galaxy.base.js | 44 ++++++++ static/scripts/galaxy.workflow_editor.canvas.js | 5 +- static/scripts/packed/galaxy.base.js | 2 +- static/scripts/packed/galaxy.workflow_editor.canvas.js | 2 +- templates/dataset/edit_attributes.mako | 39 ++++-- templates/grid_base.mako | 2 +- templates/root/history.mako | 80 ++++++++------ templates/sharing_base.mako | 8 +- templates/tagging_common.mako | 10 +- templates/workflow/edit_attributes.mako | 66 ++++++++++++ templates/workflow/editor.mako | 96 +++++++++++++++-- 19 files changed, 461 insertions(+), 130 deletions(-) diffs (993 lines): diff -r d24f0190c2a8 -r 068d572a2bf8 lib/galaxy/web/base/controller.py --- a/lib/galaxy/web/base/controller.py Mon Feb 01 13:34:44 2010 -0500 +++ b/lib/galaxy/web/base/controller.py Mon Feb 01 16:30:18 2010 -0500 @@ -25,9 +25,13 @@ # Item's public URL based on username and slug. class PublicURLColumn( grids.TextColumn ): def get_link( self, trans, grid, item ): - if item.user.username: + if item.user.username and item.slug: return dict( action='display_by_username_and_slug', username=item.user.username, slug=item.slug ) - else: + elif not item.user.username: + # TODO: provide link to set username. + return None + elif not item.user.slug: + # TODO: provide link to set slg return None class BaseController( object ): @@ -73,6 +77,55 @@ item_class = None return item_class + def get_item_annotation_str( self, db_session, user, item ): + """ Returns a user's annotation string for an item. """ + annotation_obj = self.get_item_annotation_obj( db_session, user, item ) + if annotation_obj: + return annotation_obj.annotation + return None + + def get_item_annotation_obj( self, db_session, user, item ): + """ Returns a user's annotation object for an item. """ + # Get annotation association. TODO: we could replace this eval() with a long if/else stmt, but this is more general without sacrificing + try: + annotation_assoc_class = eval( "model.%sAnnotationAssociation" % item.__class__.__name__ ) + except: + # Item doesn't have an annotation association class and cannot be annotated. + return False + + # Get annotation association object. + annotation_assoc = db_session.query( annotation_assoc_class ).filter_by( user=user ) + if item.__class__ == model.History: + annotation_assoc = annotation_assoc.filter_by( history=item ) + elif item.__class__ == model.HistoryDatasetAssociation: + annotation_assoc = annotation_assoc.filter_by( hda=item ) + elif item.__class__ == model.StoredWorkflow: + annotation_assoc = annotation_assoc.filter_by( stored_workflow=item ) + elif item.__class__ == model.WorkflowStep: + annotation_assoc = annotation_assoc.filter_by( workflow_step=item ) + return annotation_assoc.first() + + def add_item_annotation( self, trans, item, annotation ): + """ Add or update an item's annotation; a user can only have a single annotation for an item. """ + + # Get/create annotation association object. + annotation_assoc = self.get_item_annotation_obj( trans.sa_session, trans.get_user(), item ) + if not annotation_assoc: + # Create association. + # TODO: we could replace this eval() with a long if/else stmt, but this is more general without sacrificing + try: + annotation_assoc_class = eval( "model.%sAnnotationAssociation" % item.__class__.__name__ ) + except: + # Item doesn't have an annotation association class and cannot be annotated. + return False + annotation_assoc = annotation_assoc_class() + item.annotations.append( annotation_assoc ) + annotation_assoc.user = trans.get_user() + + # Set annotation. + annotation_assoc.annotation = annotation + return True + Root = BaseController class SharingStatusColumn( grids.GridColumn ): diff -r d24f0190c2a8 -r 068d572a2bf8 lib/galaxy/web/controllers/history.py --- a/lib/galaxy/web/controllers/history.py Mon Feb 01 13:34:44 2010 -0500 +++ b/lib/galaxy/web/controllers/history.py Mon Feb 01 16:30:18 2010 -0500 @@ -353,7 +353,7 @@ return trans.show_ok_message( "History deleted, a new history is active", refresh_frames=['history'] ) @web.expose def rename_async( self, trans, id=None, new_name=None ): - history = trans.sa_session.query( model.History ).get( id ) + history = self.get_history( trans, id ) # Check that the history exists, and is either owned by the current # user (if logged in) or the current history assert history is not None @@ -365,6 +365,17 @@ history.name = new_name trans.sa_session.add( history ) trans.sa_session.flush() + + @web.expose + @web.require_login( "use Galaxy histories" ) + def annotate_async( self, trans, id, new_annotation=None, **kwargs ): + history = self.get_history( trans, id ) + if new_annotation: + self.add_item_annotation( trans, history, new_annotation ) + trans.sa_session.flush() + return + else: + return "failed" @web.expose @web.json diff -r d24f0190c2a8 -r 068d572a2bf8 lib/galaxy/web/controllers/root.py --- a/lib/galaxy/web/controllers/root.py Mon Feb 01 13:34:44 2010 -0500 +++ b/lib/galaxy/web/controllers/root.py Mon Feb 01 16:30:18 2010 -0500 @@ -78,6 +78,7 @@ query = query.filter( model.HistoryDatasetAssociation.deleted == False ) return trans.stream_template_mako( "root/history.mako", history = history, + annotation = self.get_item_annotation_str( trans.sa_session, trans.get_user(), history ), datasets = query.all(), hda_id = hda_id, show_deleted = show_deleted ) @@ -304,6 +305,8 @@ else: setattr( data.metadata, name, spec.unwrap( params.get (name, None) ) ) data.datatype.after_setting_metadata( data ) + self.add_item_annotation( trans, data, params.annotation ) + else: msg = ' (Metadata could not be changed because this dataset is currently being used as input or output. You must cancel or wait for these jobs to complete before changing metadata.)' trans.sa_session.flush() @@ -383,6 +386,7 @@ messagetype = 'done' return trans.fill_template( "/dataset/edit_attributes.mako", data=data, + data_annotation=self.get_item_annotation_str( trans.sa_session, trans.get_user(), data ), datatypes=ldatatypes, current_user_roles=current_user_roles, all_roles=all_roles, diff -r d24f0190c2a8 -r 068d572a2bf8 lib/galaxy/web/controllers/workflow.py --- a/lib/galaxy/web/controllers/workflow.py Mon Feb 01 13:34:44 2010 -0500 +++ b/lib/galaxy/web/controllers/workflow.py Mon Feb 01 16:30:18 2010 -0500 @@ -303,6 +303,28 @@ session.flush() # Redirect to load galaxy frames. return trans.response.send_redirect( url_for( controller='workflow' ) ) + + @web.expose + @web.require_login( "use Galaxy workflows" ) + def edit_attributes( self, trans, id, **kwargs ): + # Get workflow and do error checking. + stored = get_stored_workflow( trans, id ) + if not stored: + error( "You do not own this workflow or workflow ID is invalid." ) + + # Update workflow attributes if new values submitted. + if 'name' in kwargs: + # Rename workflow. + stored.name = kwargs[ 'name' ] + if 'annotation' in kwargs: + # Set workflow annotation. + self.add_item_annotation( trans, stored, kwargs[ 'annotation' ] ) + trans.sa_session.flush() + + return trans.fill_template( 'workflow/edit_attributes.mako', + stored=stored, + annotation=self.get_item_annotation_str( trans.sa_session, trans.get_user(), stored ) + ) @web.expose @web.require_login( "use Galaxy workflows" ) @@ -320,7 +342,29 @@ else: return form( url_for( action='rename', id=trans.security.encode_id(stored.id) ), "Rename workflow", submit_text="Rename" ) \ .add_text( "new_name", "Workflow Name", value=stored.name ) - + + @web.expose + @web.require_login( "use Galaxy workflows" ) + def rename_async( self, trans, id, new_name=None, **kwargs ): + stored = get_stored_workflow( trans, id ) + if new_name: + stored.name = new_name + trans.sa_session.flush() + return + else: + return "failed" + + @web.expose + @web.require_login( "use Galaxy workflows" ) + def annotate_async( self, trans, id, new_annotation=None, **kwargs ): + stored = get_stored_workflow( trans, id ) + if new_annotation: + self.add_item_annotation( trans, stored, new_annotation ) + trans.sa_session.flush() + return + else: + return "failed" + @web.expose @web.require_login( "use Galaxy workflows" ) def clone( self, trans, id ): @@ -400,11 +444,11 @@ """ if not id: error( "Invalid workflow id" ) - id = trans.security.decode_id( id ) - return trans.fill_template( "workflow/editor.mako", workflow_id=id ) + stored = get_stored_workflow( trans, id ) + return trans.fill_template( "workflow/editor.mako", stored=stored, annotation=self.get_item_annotation_str( trans.sa_session, trans.get_user(), stored ) ) @web.json - def editor_form_post( self, trans, type='tool', tool_id=None, **incoming ): + def editor_form_post( self, trans, type='tool', tool_id=None, annotation=None, **incoming ): """ Accepts a tool state and incoming values, and generates a new tool form and some additional information, packed into a json dictionary. @@ -423,7 +467,8 @@ 'data_inputs': module.get_data_inputs(), 'data_outputs': module.get_data_outputs(), 'tool_errors': module.get_errors(), - 'form_html': module.get_config_form() + 'form_html': module.get_config_form(), + 'annotation': annotation } @web.json @@ -445,7 +490,8 @@ 'tooltip': module.get_tooltip(), 'data_inputs': module.get_data_inputs(), 'data_outputs': module.get_data_outputs(), - 'form_html': module.get_config_form() + 'form_html': module.get_config_form(), + 'annotation': "" } @web.json @@ -477,6 +523,11 @@ # FIXME: Frontend should be able to handle workflow messages # as a dictionary not just the values data['upgrade_messages'][step.order_index] = upgrade_message.values() + # Get user annotation. + step_annotation = self.get_item_annotation_obj ( trans.sa_session, trans.get_user(), step ) + annotation_str = "" + if step_annotation: + annotation_str = step_annotation.annotation # Pack attributes into plain dictionary step_dict = { 'id': step.order_index, @@ -489,6 +540,7 @@ 'data_inputs': module.get_data_inputs(), 'data_outputs': module.get_data_outputs(), 'form_html': module.get_config_form(), + 'annotation' : annotation_str } # Connections input_connections = step.input_connections @@ -551,6 +603,9 @@ workflow.has_errors = True # Stick this in the step temporarily step.temp_input_connections = step_dict['input_connections'] + + # Save step annotation. + self.add_item_annotation( trans, step, step_dict[ 'annotation' ] ) # Second pass to deal with connections between steps for step in steps: # Input connections diff -r d24f0190c2a8 -r 068d572a2bf8 static/june_2007_style/base.css.tmpl --- a/static/june_2007_style/base.css.tmpl Mon Feb 01 13:34:44 2010 -0500 +++ b/static/june_2007_style/base.css.tmpl Mon Feb 01 16:30:18 2010 -0500 @@ -656,3 +656,45 @@ } } + +## Icon buttons. + +.icon-button { + width: 16px; + height: 16px; + display: block; + float: left; + margin-left: 2px; + ## Allow alt text for screen readers + text-indent: 20px; +} + +.icon-button.display { + -sprite-group: history-buttons; + -sprite-image: eye_icon.png; +} + +.icon-button.display:hover { + -sprite-group: history-buttons; + -sprite-image: eye_icon_dark.png; +} + +.icon-button.delete { + -sprite-group: history-buttons; + -sprite-image: delete_icon.png; +} + +.icon-button.delete:hover { + -sprite-group: history-buttons; + -sprite-image: delete_icon_dark.png; +} + +.icon-button.edit { + -sprite-group: history-buttons; + -sprite-image: pencil_icon.png; +} + +.icon-button.edit:hover { + -sprite-group: history-buttons; + -sprite-image: pencil_icon_dark.png; +} \ No newline at end of file diff -r d24f0190c2a8 -r 068d572a2bf8 static/june_2007_style/blue/base.css --- a/static/june_2007_style/blue/base.css Mon Feb 01 13:34:44 2010 -0500 +++ b/static/june_2007_style/blue/base.css Mon Feb 01 16:30:18 2010 -0500 @@ -110,3 +110,10 @@ .text-content fieldset{border-color:#ccc;border:1px solid #ccc;} .text-content th,.text-content td{border-bottom:1px solid #ddd;border-right:1px solid #ccc;} .text-content th,.text-content td{padding:.8em;} +.icon-button{width:16px;height:16px;display:block;float:left;margin-left:2px;text-indent:20px;} +.icon-button.edit{background:url(history-buttons.png) no-repeat 0px -52px;} +.icon-button.edit:hover{background:url(history-buttons.png) no-repeat 0px -78px;} +.icon-button.display{background:url(history-buttons.png) no-repeat 0px -0px;} +.icon-button.display:hover{background:url(history-buttons.png) no-repeat 0px -26px;} +.icon-button.delete{background:url(history-buttons.png) no-repeat 0px -104px;} +.icon-button.delete:hover{background:url(history-buttons.png) no-repeat 0px -130px;} \ No newline at end of file diff -r d24f0190c2a8 -r 068d572a2bf8 static/june_2007_style/blue/history.css --- a/static/june_2007_style/blue/history.css Mon Feb 01 13:34:44 2010 -0500 +++ b/static/june_2007_style/blue/history.css Mon Feb 01 16:30:18 2010 -0500 @@ -22,13 +22,6 @@ div.historyItem-noPermission{filter:alpha(opacity=60);-moz-opacity:.60;opacity:.60;} div.historyItemTitleBar.spinner .state-icon{background:url(data_running.gif) 0 1px no-repeat !important;} div.historyItemButtons{float:right;} -.icon-button{width:16px;height:16px;display:block;float:left;margin-left:2px;text-indent:20px;} -.icon-button.display{background:url(history-buttons.png) no-repeat 0px -0px;} -.icon-button.display:hover{background:url(history-buttons.png) no-repeat 0px -26px;} -.icon-button.edit{background:url(history-buttons.png) no-repeat 0px -52px;} -.icon-button.edit:hover{background:url(history-buttons.png) no-repeat 0px -78px;} -.icon-button.delete{background:url(history-buttons.png) no-repeat 0px -104px;} -.icon-button.delete:hover{background:url(history-buttons.png) no-repeat 0px -130px;} div.historyItemBody div{padding-top:2px;} pre.peek{background:white;color:black;width:100%;overflow:auto;} pre.peek th{color:white;background:#023858;} diff -r d24f0190c2a8 -r 068d572a2bf8 static/june_2007_style/history.css.tmpl --- a/static/june_2007_style/history.css.tmpl Mon Feb 01 13:34:44 2010 -0500 +++ b/static/june_2007_style/history.css.tmpl Mon Feb 01 16:30:18 2010 -0500 @@ -120,46 +120,6 @@ float: right; } -.icon-button { - width: 16px; - height: 16px; - display: block; - float: left; - margin-left: 2px; - ## Allow alt text for screen readers - text-indent: 20px; -} - -.icon-button.display { - -sprite-group: history-buttons; - -sprite-image: eye_icon.png; -} - -.icon-button.display:hover { - -sprite-group: history-buttons; - -sprite-image: eye_icon_dark.png; -} - -.icon-button.edit { - -sprite-group: history-buttons; - -sprite-image: pencil_icon.png; -} - -.icon-button.edit:hover { - -sprite-group: history-buttons; - -sprite-image: pencil_icon_dark.png; -} - -.icon-button.delete { - -sprite-group: history-buttons; - -sprite-image: delete_icon.png; -} - -.icon-button.delete:hover { - -sprite-group: history-buttons; - -sprite-image: delete_icon_dark.png; -} - div.historyItemBody div { padding-top: 2px; } diff -r d24f0190c2a8 -r 068d572a2bf8 static/scripts/galaxy.base.js --- a/static/scripts/galaxy.base.js Mon Feb 01 13:34:44 2010 -0500 +++ b/static/scripts/galaxy.base.js Mon Feb 01 16:30:18 2010 -0500 @@ -207,3 +207,47 @@ }); } } // end replace_dbkey_select() + +// +// Edit and save text asynchronously. +// +function async_save_text(click_to_edit_elt, text_elt_id, save_url, text_parm_name, use_textarea, num_rows) +{ + $("#" + click_to_edit_elt).click( function() { + var old_text = $("#" + text_elt_id).text() + if (use_textarea) + var t = $("<textarea rows='" + num_rows + "'>" + old_text + "</textarea>" ); + else + var t = $("<input type='text' value='" + old_text + "'></input>" ); + t.blur( function() { + $(this).remove(); + $("#" + text_elt_id).show(); + }); + t.keyup( function( e ) { + if ( e.keyCode == 27 ) { + // Escape key + $(this).trigger( "blur" ); + } else if ( e.keyCode == 13 ) { + // Enter key + new_text = this.value; + $(this).trigger( "blur" ); + var ajax_data = new Object(); + ajax_data[text_parm_name] = new_text; + $.ajax({ + url: save_url, + data: ajax_data, + error: function() { alert( "Text editing for elt " + text_elt_id + " failed" ) }, + success: function() { + $("#" + text_elt_id).text( new_text ); + } + }); + } + }); + $("#" + text_elt_id).hide(); + t.insertAfter( $("#" + text_elt_id) ); + t.focus(); + t.select(); + return false; + }); + +} diff -r d24f0190c2a8 -r 068d572a2bf8 static/scripts/galaxy.workflow_editor.canvas.js --- a/static/scripts/galaxy.workflow_editor.canvas.js Mon Feb 01 13:34:44 2010 -0500 +++ b/static/scripts/galaxy.workflow_editor.canvas.js Mon Feb 01 16:30:18 2010 -0500 @@ -272,6 +272,7 @@ this.tool_state = data.tool_state; this.tool_errors = data.tool_errors; this.tooltip = data.tooltip ? data.tooltip : "" + this.annotation = data.annotation; if ( this.tool_errors ) { f.addClass( "tool-node-error" ); @@ -306,6 +307,7 @@ node = this; this.tool_state = data.tool_state; this.form_html = data.form_html; + this.annotation = data.annotation; this.tool_errors = data.tool_errors; if ( this.tool_errors ) { el.addClass( "tool-node-error" ); @@ -405,7 +407,8 @@ tool_state : node.tool_state, tool_errors : node.tool_errors, input_connections : input_connections, - position : $(node.element).position() + position : $(node.element).position(), + annotation: node.annotation }; nodes[ node.id ] = node_data; }); diff -r d24f0190c2a8 -r 068d572a2bf8 static/scripts/packed/galaxy.base.js --- a/static/scripts/packed/galaxy.base.js Mon Feb 01 13:34:44 2010 -0500 +++ b/static/scripts/packed/galaxy.base.js Mon Feb 01 16:30:18 2010 -0500 @@ -1,1 +1,1 @@ -$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};jQuery(document).ready(function(){jQuery("a[confirm]").click(function(){return confirm(jQuery(this).attr("confirm"))});make_popup_menus()});function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}function ensure_popup_helper(){if($("#popup-helper").length==0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",t op:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function make_popupmenu(d,c){ensure_popup_helper();var a=$(d);var b=$("<ul id='"+d.attr("id")+"-menu'></ul>");$.each(c,function(g,f){if(f){$("<li/>").html(g).click(f).appendTo(b)}else{$("<li class='head'/>").html(g).appendTo(b)}});var e=$("<div class='popmenu-wrapper'>");e.append(b).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(d,e)}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){var h=$(b).offset();$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}var array_length=function(a){if(a.length){return a.length}var b=0;for(element in a){b++}return b};var replace_dbkey_select=function(){var c=$("select[name=dbkey]");var d=c.attr("value");if(c.length!=0){var e=$("<input id='dbkey-input' type='text'></input>");e.attr("size",40);e.attr("name",c.attr("name"));e.click(function(){var g=$(this).attr("value");$(this).attr("value","Loading...");$(this).showAllInCache();$(this).attr("value",g);$(this).select()});var b=new Array();var a=new Object();c.children("option").each(function(){var h=$(this).text();var g=$(this).attr("value");if(g=="?"){return}b.push(h);a[h]=g;if(g==d){e.attr("value",h)}});if(e.attr("value")==""){e.attr("value","Click to Search or Select Build")}var f={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:1000,minChars:0,hideForLessThanMinChars:false};e.autocomplete(b,f);c.replaceWith(e);$("form").submit(function(){var i=$("#dbkey-input");if(i.length!=0){var h=i.attr("value");var g=a[h];if(g!=null&&g!=undefined){i.attr("value",g)}else{if(d!=""){i.attr("value",d)}else{i. attr("value","?")}}}})}}; \ No newline at end of file +$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};jQuery(document).ready(function(){jQuery("a[confirm]").click(function(){return confirm(jQuery(this).attr("confirm"))});make_popup_menus()});function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}function ensure_popup_helper(){if($("#popup-helper").length==0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",t op:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function make_popupmenu(d,c){ensure_popup_helper();var a=$(d);var b=$("<ul id='"+d.attr("id")+"-menu'></ul>");$.each(c,function(g,f){if(f){$("<li/>").html(g).click(f).appendTo(b)}else{$("<li class='head'/>").html(g).appendTo(b)}});var e=$("<div class='popmenu-wrapper'>");e.append(b).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(d,e)}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){var h=$(b).offset();$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}var array_length=function(a){if(a.length){return a.length}var b=0;for(element in a){b++}return b};var replace_dbkey_select=function(){var c=$("select[name=dbkey]");var d=c.attr("value");if(c.length!=0){var e=$("<input id='dbkey-input' type='text'></input>");e.attr("size",40);e.attr("name",c.attr("name"));e.click(function(){var g=$(this).attr("value");$(this).attr("value","Loading...");$(this).showAllInCache();$(this).attr("value",g);$(this).select()});var b=new Array();var a=new Object();c.children("option").each(function(){var h=$(this).text();var g=$(this).attr("value");if(g=="?"){return}b.push(h);a[h]=g;if(g==d){e.attr("value",h)}});if(e.attr("value")==""){e.attr("value","Click to Search or Select Build")}var f={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:1000,minChars:0,hideForLessThanMinChars:false};e.autocomplete(b,f);c.replaceWith(e);$("form").submit(function(){var i=$("#dbkey-input");if(i.length!=0){var h=i.attr("value");var g=a[h];if(g!=null&&g!=undefined){i.attr("value",g)}else{if(d!=""){i.attr("value",d)}else{i. attr("value","?")}}}})}};function async_save_text(d,c,a,e,f,b){$("#"+d).click(function(){var h=$("#"+c).text();if(f){var g=$("<textarea rows='"+b+"'>"+h+"</textarea>")}else{var g=$("<input type='text' value='"+h+"'></input>")}g.blur(function(){$(this).remove();$("#"+c).show()});g.keyup(function(j){if(j.keyCode==27){$(this).trigger("blur")}else{if(j.keyCode==13){new_text=this.value;$(this).trigger("blur");var i=new Object();i[e]=new_text;$.ajax({url:a,data:i,error:function(){alert("Text editing for elt "+c+" failed")},success:function(){$("#"+c).text(new_text)}})}}});$("#"+c).hide();g.insertAfter($("#"+c));g.focus();g.select();return false})}; \ No newline at end of file diff -r d24f0190c2a8 -r 068d572a2bf8 static/scripts/packed/galaxy.workflow_editor.canvas.js --- a/static/scripts/packed/galaxy.workflow_editor.canvas.js Mon Feb 01 13:34:44 2010 -0500 +++ b/static/scripts/packed/galaxy.workflow_editor.canvas.js Mon Feb 01 16:30:18 2010 -0500 @@ -1,1 +1,1 @@ -function Terminal(a){this.element=a;this.connectors=[]}$.extend(Terminal.prototype,{connect:function(a){this.connectors.push(a);if(this.node){this.node.changed()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.changed()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});function OutputTerminal(a,b){Terminal.call(this,a);this.datatype=b}OutputTerminal.prototype=new Terminal();function InputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}InputTerminal.prototype=new Terminal();$.extend(InputTerminal.prototype,{can_accept:function(a){if(this.connectors.length<1){for(var b in this.datatypes){if(a.datatype=="input"){return true}if(issubtype(a.datatype,this.datatypes[b])){return true}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a) {this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;this.handle1.connect(this);this.handle2=a;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(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};var h=n(this.handle1.element)+5;var g=i(this.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*f; this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canvas.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});function Node(a){this.element=a;this.input_terminals={};this.output_terminals={};this.tool_errors={}}$.extend(Node.prototype,{enable_input_terminal:function(d,a,b){var c=this;$(d).each(function(){var f=this.terminal=new InputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dropstart",function(g){g.dragProxy.terminal.connectors[0].inner_color="#BBFFBB"}).bind("dropend",function(g){g.dragProxy.terminal.connectors[0].inner_color="#FFFFFF"}).bind("drop",function(g){(new Connector(g.dragTarget.terminal,g.dropTarget.terminal)).redraw()}).bind("hov er",function(){if(f.connectors.length>0){var g=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='buttons'></div>").append($("<img src='../images/delete_icon.png' />").click(function(){$.each(f.connectors,function(i,h){h.destroy()});g.remove()}))).bind("mouseleave",function(){$(this).remove()});g.css({top:$(this).offset().top-2,left:$(this).offset().left-g.width(),"padding-right":$(this).width()}).show()}});c.input_terminals[a]=f})},enable_output_terminal:function(d,a,b){var c=this;$(d).each(function(){var g=this;var f=this.terminal=new OutputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dragstart",function(j){var i=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);i.terminal=new OutputTerminal(i);var k=new Connector();k.dragging=true;k.connect(this.terminal,i.terminal);$.dropManage({filter:function(h){return this.terminal.can_accept(f)}}).addClass("input-terminal-active");ret urn i}).bind("drag",function(i){var h=function(){var k=$(i.dragProxy).offsetParent().offset(),j=i.offsetX-k.left,l=i.offsetY-k.top;$(i.dragProxy).css({left:j,top:l});i.dragProxy.terminal.redraw();canvas_manager.update_viewport_overlay()};h();$("#canvas-container").get(0).scroll_panel.test(i,h)}).bind("dragend",function(h){h.dragProxy.terminal.connectors[0].destroy();$(h.dragProxy).remove();$.dropManage().removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()});c.output_terminals[a]=f})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b .appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(g){var d=this.element;if(g.type){this.type=g.type}this.name=g.name;this.form_html=g.form_html;this.tool_state=g.tool_state;this.tool_errors=g.tool_errors;this.tooltip=g.tooltip?g.tooltip:"";if(this.tool_errors){d.addClass("tool-node-error")}else{d.removeClass("tool-node-error")}var c=this;var a=d.find(".toolFormBody");a.find("div").remove();var h=$("<div class='inputs'></div>").appendTo(a);$.each(g.data_inputs,function(j,b){var f=$("<div class='terminal input-terminal'></div>");c.enable_input_terminal(f,b.name,b.extensions);h.append($("<div class='form-row dataRow input-data-row' name='"+b.name+"'>"+b.label+"</div>").prepend(f))});if((g.data_inputs.length>0)&&(g.data_outputs.length>0)){a.append($("<div class='rule'></div>"))}$.each(g.data_outputs,function(k,b){var j=$("<div class='terminal output-terminal'></div>");c.enable_output_terminal(j,b.name,b.extension);var f=b.name;if(b.ext ension!="input"){f=f+" ("+b.extension+")"}a.append($("<div class='form-row dataRow'>"+f+"</div>").append(j))});workflow.node_changed(this)},update_field_data:function(f){var c=$(this.element),d=this;this.tool_state=f.tool_state;this.form_html=f.form_html;this.tool_errors=f.tool_errors;if(this.tool_errors){c.addClass("tool-node-error")}else{c.removeClass("tool-node-error")}var g=c.find("div.inputs");var b=$("<div class='inputs'></div>");var a=g.find("div.input-data-row");$.each(f.data_inputs,function(k,h){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,h.name,h.extensions);g.find("div[name="+h.name+"]").each(function(){$(this).find(".input-terminal").each(function(){var i=this.terminal.connectors[0];if(i){j[0].terminal.connectors[0]=i;i.handle2=j[0].terminal}});$(this).remove()});b.append($("<div class='form-row dataRow input-data-row' name='"+h.name+"'>"+h.label+"</div>").prepend(j))});g.replaceWith(b);g.find("div.input-data-row > .terminal") .each(function(){this.terminal.destroy()});this.changed();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},changed:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},to_simple:function(){var a={};$.each(this.nodes,function(b,d){var f={};$.each(d.input_terminals,function(g ,h){f[h.name]=null;$.each(h.connectors,function(j,k){f[h.name]={id:k.handle1.node.id,output_name:k.handle1.name}})});var c={id:d.id,type:d.type,tool_id:d.tool_id,tool_state:d.tool_state,tool_errors:d.tool_errors,input_connections:f,position:$(d.element).position()};a[d.id]=c});return{steps:a}},from_simple:function(a){wf=this;var b=0;wf.name=a.name;$.each(a.steps,function(f,d){var c=prebuild_node("tool",d.name,d.tool_id);c.init_field_data(d);if(d.position){c.element.css({top:d.position.top,left:d.position.left})}c.id=d.id;wf.nodes[c.id]=c;b=Math.max(b,parseInt(f))});wf.id_counter=b+1;$.each(a.steps,function(f,d){var c=wf.nodes[f];$.each(d.input_connections,function(h,g){if(g){var i=wf.nodes[g.id];var j=new Connector();j.connect(i.output_terminals[g.output_name],c.input_terminals[h]);j.redraw()}})})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$("#right-content").find("form").submit();this.active_form_has_changes=false}},clear _active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)}}if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent();var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Mat h.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node(i);g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='../images/loading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h ;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<img src='../images/delete_icon.png' />").click(function(b){g.destroy()}).hover(function(){$(this).attr("src","../images/delete_icon_dark.png")},function(){$(this).attr("src","../images/delete_icon.png")}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o){var f=$(this).offsetParent().offset(),b=o.offsetX-f.left,p=o.offsetY-f.top;$(this).css({left:b,top:p});$(this).find(".terminal").each(function(){this.terminal.red raw()})});return g}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.min(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.time out=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(g){var h=$(this).offset();var f=b.cc.position();c=f.top-h.top;d=f.left-h.left}).bind("drag",function(f){a(f.offsetX+d,f.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k){var j=b.cc.width(),g=b.cc.height(),f=b.oc.width(),h=b.oc.height(),i=$(this).offsetParent( ).offset(),m=k.offsetX-i.left,l=k.offsetY-i.top;a(-(m/f*j),-(l/h*g))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g){var i=$(this).offsetParent();var h=i.offset();var f=Math.max(i.width()-(g.offsetX-h.left),i.height()-(g.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(f){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b){k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m- k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;$.each(workflow.nodes,function(t,q){var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}}); \ No newline at end of file +function Terminal(a){this.element=a;this.connectors=[]}$.extend(Terminal.prototype,{connect:function(a){this.connectors.push(a);if(this.node){this.node.changed()}},disconnect:function(a){this.connectors.splice($.inArray(a,this.connectors),1);if(this.node){this.node.changed()}},redraw:function(){$.each(this.connectors,function(a,b){b.redraw()})},destroy:function(){$.each(this.connectors.slice(),function(a,b){b.destroy()})}});function OutputTerminal(a,b){Terminal.call(this,a);this.datatype=b}OutputTerminal.prototype=new Terminal();function InputTerminal(a,b){Terminal.call(this,a);this.datatypes=b}InputTerminal.prototype=new Terminal();$.extend(InputTerminal.prototype,{can_accept:function(a){if(this.connectors.length<1){for(var b in this.datatypes){if(a.datatype=="input"){return true}if(issubtype(a.datatype,this.datatypes[b])){return true}}}return false}});function Connector(b,a){this.canvas=null;this.dragging=false;this.inner_color="#FFFFFF";this.outer_color="#D8B365";if(b&&a) {this.connect(b,a)}}$.extend(Connector.prototype,{connect:function(b,a){this.handle1=b;this.handle1.connect(this);this.handle2=a;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(){var d=$("#canvas-container");if(!this.canvas){this.canvas=document.createElement("canvas");if(window.G_vmlCanvasManager){G_vmlCanvasManager.initElement(this.canvas)}d.append($(this.canvas));if(this.dragging){this.canvas.style.zIndex="300"}}var n=function(c){return $(c).offset().left-d.offset().left};var i=function(c){return $(c).offset().top-d.offset().top};var h=n(this.handle1.element)+5;var g=i(this.handle1.element)+5;var p=n(this.handle2.element)+5;var m=i(this.handle2.element)+5;var f=100;var k=Math.min(h,p);var a=Math.max(h,p);var j=Math.min(g,m);var t=Math.max(g,m);var b=Math.min(Math.max(Math.abs(t-j)/2,100),300);var o=k-f;var s=j-f;var q=a-k+2*f;var l=t-j+2*f; this.canvas.style.left=o+"px";this.canvas.style.top=s+"px";this.canvas.setAttribute("width",q);this.canvas.setAttribute("height",l);h-=o;g-=s;p-=o;m-=s;var r=this.canvas.getContext("2d");r.lineCap="round";r.strokeStyle=this.outer_color;r.lineWidth=7;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke();r.strokeStyle=this.inner_color;r.lineWidth=5;r.beginPath();r.moveTo(h,g);r.bezierCurveTo(h+b,g,p-b,m,p,m);r.stroke()}});function Node(a){this.element=a;this.input_terminals={};this.output_terminals={};this.tool_errors={}}$.extend(Node.prototype,{enable_input_terminal:function(d,a,b){var c=this;$(d).each(function(){var f=this.terminal=new InputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dropstart",function(g){g.dragProxy.terminal.connectors[0].inner_color="#BBFFBB"}).bind("dropend",function(g){g.dragProxy.terminal.connectors[0].inner_color="#FFFFFF"}).bind("drop",function(g){(new Connector(g.dragTarget.terminal,g.dropTarget.terminal)).redraw()}).bind("hov er",function(){if(f.connectors.length>0){var g=$("<div class='callout'></div>").css({display:"none"}).appendTo("body").append($("<div class='buttons'></div>").append($("<img src='../images/delete_icon.png' />").click(function(){$.each(f.connectors,function(i,h){h.destroy()});g.remove()}))).bind("mouseleave",function(){$(this).remove()});g.css({top:$(this).offset().top-2,left:$(this).offset().left-g.width(),"padding-right":$(this).width()}).show()}});c.input_terminals[a]=f})},enable_output_terminal:function(d,a,b){var c=this;$(d).each(function(){var g=this;var f=this.terminal=new OutputTerminal(this,b);f.node=c;f.name=a;$(this).bind("dragstart",function(j){var i=$('<div class="drag-terminal" style="position: absolute;"></div>').appendTo("#canvas-container").get(0);i.terminal=new OutputTerminal(i);var k=new Connector();k.dragging=true;k.connect(this.terminal,i.terminal);$.dropManage({filter:function(h){return this.terminal.can_accept(f)}}).addClass("input-terminal-active");ret urn i}).bind("drag",function(i){var h=function(){var k=$(i.dragProxy).offsetParent().offset(),j=i.offsetX-k.left,l=i.offsetY-k.top;$(i.dragProxy).css({left:j,top:l});i.dragProxy.terminal.redraw();canvas_manager.update_viewport_overlay()};h();$("#canvas-container").get(0).scroll_panel.test(i,h)}).bind("dragend",function(h){h.dragProxy.terminal.connectors[0].destroy();$(h.dragProxy).remove();$.dropManage().removeClass("input-terminal-active");$("#canvas-container").get(0).scroll_panel.stop()});c.output_terminals[a]=f})},redraw:function(){$.each(this.input_terminals,function(a,b){b.redraw()});$.each(this.output_terminals,function(a,b){b.redraw()})},destroy:function(){$.each(this.input_terminals,function(a,b){b.destroy()});$.each(this.output_terminals,function(a,b){b.destroy()});workflow.remove_node(this);$(this.element).remove()},make_active:function(){$(this.element).addClass("toolForm-active")},make_inactive:function(){var a=this.element.get(0);(function(b){b.removeChild(a);b .appendChild(a)})(a.parentNode);$(a).removeClass("toolForm-active")},init_field_data:function(g){var d=this.element;if(g.type){this.type=g.type}this.name=g.name;this.form_html=g.form_html;this.tool_state=g.tool_state;this.tool_errors=g.tool_errors;this.tooltip=g.tooltip?g.tooltip:"";this.annotation=g.annotation;if(this.tool_errors){d.addClass("tool-node-error")}else{d.removeClass("tool-node-error")}var c=this;var a=d.find(".toolFormBody");a.find("div").remove();var h=$("<div class='inputs'></div>").appendTo(a);$.each(g.data_inputs,function(j,b){var f=$("<div class='terminal input-terminal'></div>");c.enable_input_terminal(f,b.name,b.extensions);h.append($("<div class='form-row dataRow input-data-row' name='"+b.name+"'>"+b.label+"</div>").prepend(f))});if((g.data_inputs.length>0)&&(g.data_outputs.length>0)){a.append($("<div class='rule'></div>"))}$.each(g.data_outputs,function(k,b){var j=$("<div class='terminal output-terminal'></div>");c.enable_output_terminal(j,b.name,b.ext ension);var f=b.name;if(b.extension!="input"){f=f+" ("+b.extension+")"}a.append($("<div class='form-row dataRow'>"+f+"</div>").append(j))});workflow.node_changed(this)},update_field_data:function(f){var c=$(this.element),d=this;this.tool_state=f.tool_state;this.form_html=f.form_html;this.annotation=f.annotation;this.tool_errors=f.tool_errors;if(this.tool_errors){c.addClass("tool-node-error")}else{c.removeClass("tool-node-error")}var g=c.find("div.inputs");var b=$("<div class='inputs'></div>");var a=g.find("div.input-data-row");$.each(f.data_inputs,function(k,h){var j=$("<div class='terminal input-terminal'></div>");d.enable_input_terminal(j,h.name,h.extensions);g.find("div[name="+h.name+"]").each(function(){$(this).find(".input-terminal").each(function(){var i=this.terminal.connectors[0];if(i){j[0].terminal.connectors[0]=i;i.handle2=j[0].terminal}});$(this).remove()});b.append($("<div class='form-row dataRow input-data-row' name='"+h.name+"'>"+h.label+"</div>").prepend(j))}) ;g.replaceWith(b);g.find("div.input-data-row > .terminal").each(function(){this.terminal.destroy()});this.changed();this.redraw()},error:function(d){var a=$(this.element).find(".toolFormBody");a.find("div").remove();var c="<div style='color: red; text-style: italic;'>"+d+"</div>";this.form_html=c;a.html(c);workflow.node_changed(this)},changed:function(){workflow.node_changed(this)}});function Workflow(a){this.canvas_container=a;this.id_counter=0;this.nodes={};this.name=null;this.has_changes=false;this.active_form_has_changes=false}$.extend(Workflow.prototype,{add_node:function(a){a.id=this.id_counter;a.element.attr("id","wf-node-step-"+a.id);this.id_counter++;this.nodes[a.id]=a;this.has_changes=true;a.workflow=this},remove_node:function(a){if(this.active_node==a){this.clear_active_node()}delete this.nodes[a.id];this.has_changes=true},remove_all:function(){wf=this;$.each(this.nodes,function(b,a){a.destroy();wf.remove_node(a)})},to_simple:function(){var a={};$.each(this.nodes, function(b,d){var f={};$.each(d.input_terminals,function(g,h){f[h.name]=null;$.each(h.connectors,function(j,k){f[h.name]={id:k.handle1.node.id,output_name:k.handle1.name}})});var c={id:d.id,type:d.type,tool_id:d.tool_id,tool_state:d.tool_state,tool_errors:d.tool_errors,input_connections:f,position:$(d.element).position(),annotation:d.annotation};a[d.id]=c});return{steps:a}},from_simple:function(a){wf=this;var b=0;wf.name=a.name;$.each(a.steps,function(f,d){var c=prebuild_node("tool",d.name,d.tool_id);c.init_field_data(d);if(d.position){c.element.css({top:d.position.top,left:d.position.left})}c.id=d.id;wf.nodes[c.id]=c;b=Math.max(b,parseInt(f))});wf.id_counter=b+1;$.each(a.steps,function(f,d){var c=wf.nodes[f];$.each(d.input_connections,function(h,g){if(g){var i=wf.nodes[g.id];var j=new Connector();j.connect(i.output_terminals[g.output_name],c.input_terminals[h]);j.redraw()}})})},check_changes_in_active_form:function(){if(this.active_form_has_changes){this.has_changes=true;$( "#right-content").find("form").submit();this.active_form_has_changes=false}},clear_active_node:function(){if(this.active_node){this.active_node.make_inactive();this.active_node=null}parent.show_form_for_tool("<div>No node selected</div>")},activate_node:function(a){if(this.active_node!=a){this.check_changes_in_active_form();this.clear_active_node();parent.show_form_for_tool(a.form_html+a.tooltip,a);a.make_active();this.active_node=a}},node_changed:function(a){this.has_changes=true;if(this.active_node==a){parent.show_form_for_tool(a.form_html+a.tooltip,a)}},layout:function(){this.check_changes_in_active_form();var i={};var b={};$.each(this.nodes,function(l,k){if(i[l]===undefined){i[l]=0}if(b[l]===undefined){b[l]=[]}});$.each(this.nodes,function(l,k){$.each(k.input_terminals,function(m,n){$.each(n.connectors,function(p,q){var o=q.handle1.node;i[k.id]+=1;b[o.id].push(k.id)})})});node_ids_by_level=[];while(true){level_parents=[];for(var a in i){if(i[a]==0){level_parents.push(a)} }if(level_parents.length==0){break}node_ids_by_level.push(level_parents);for(var f in level_parents){var j=level_parents[f];delete i[j];for(var g in b[j]){i[b[j][g]]-=1}}}if(i.length){return}var d=this.nodes;var h=80;v_pad=30;var c=h;$.each(node_ids_by_level,function(k,l){l.sort(function(p,o){return $(d[p].element).position().top-$(d[o].element).position().top});var m=0;var n=v_pad;$.each(l,function(o,r){var q=d[r];var p=$(q.element);$(p).css({top:n,left:c});m=Math.max(m,$(p).width());n+=$(p).height()+v_pad});c+=m+h});$.each(d,function(k,l){l.redraw()})},bounds_for_all_nodes:function(){var d=Infinity,b=-Infinity,c=Infinity,a=-Infinity,f;$.each(this.nodes,function(h,g){e=$(g.element);f=e.position();d=Math.min(d,f.left);b=Math.max(b,f.left+e.width());c=Math.min(c,f.top);a=Math.max(a,f.top+e.width())});return{xmin:d,xmax:b,ymin:c,ymax:a}},fit_canvas_to_nodes:function(){var a=this.bounds_for_all_nodes();var f=this.canvas_container.position();var i=this.canvas_container.parent(); var d=fix_delta(a.xmin,100);var h=fix_delta(a.ymin,100);d=Math.max(d,f.left);h=Math.max(h,f.top);var c=f.left-d;var g=f.top-h;var b=round_up(a.xmax+100,100)+d;var j=round_up(a.ymax+100,100)+h;b=Math.max(b,-c+i.width());j=Math.max(j,-g+i.height());this.canvas_container.css({left:c,top:g,width:b,height:j});this.canvas_container.children().each(function(){var k=$(this).position();$(this).css("left",k.left+d);$(this).css("top",k.top+h)})}});function fix_delta(a,b){if(a<b||a>3*b){new_pos=(Math.ceil(((a%b))/b)+1)*b;return(-(a-new_pos))}return 0}function round_up(a,b){return Math.ceil(a/b)*b}function prebuild_node(l,j,r){var i=$("<div class='toolForm toolFormInCanvas'></div>");var g=new Node(i);g.type=l;if(l=="tool"){g.tool_id=r}var n=$("<div class='toolFormTitle unselectable'>"+j+"</div>");i.append(n);i.css("left",$(window).scrollLeft()+20);i.css("top",$(window).scrollTop()+20);var m=$("<div class='toolFormBody'></div>");var h="<div><img height='16' align='middle' src='../images/l oading_small_white_bg.gif'/> loading tool info...</div>";m.append(h);g.form_html=h;i.append(m);var k=$("<div class='buttons' style='float: right;'></div>");k.append($("<img src='../images/delete_icon.png' />").click(function(b){g.destroy()}).hover(function(){$(this).attr("src","../images/delete_icon_dark.png")},function(){$(this).attr("src","../images/delete_icon.png")}));i.appendTo("#canvas-container");var d=$("#canvas-container").position();var c=$("#canvas-container").parent();var a=i.width();var q=i.height();i.css({left:(-d.left)+(c.width()/2)-(a/2),top:(-d.top)+(c.height()/2)-(q/2)});k.prependTo(n);a+=(k.width()+10);i.css("width",a);$(i).bind("dragstart",function(){workflow.activate_node(g)}).bind("dragend",function(){workflow.node_changed(this);workflow.fit_canvas_to_nodes();canvas_manager.draw_overview()}).bind("dragclickonly",function(){workflow.activate_node(g)}).bind("drag",function(o){var f=$(this).offsetParent().offset(),b=o.offsetX-f.left,p=o.offsetY-f.top;$(thi s).css({left:b,top:p});$(this).find(".terminal").each(function(){this.terminal.redraw()})});return g}var ext_to_type=null;var type_to_type=null;function issubtype(b,a){b=ext_to_type[b];a=ext_to_type[a];return(type_to_type[b])&&(a in type_to_type[b])}function populate_datatype_info(a){ext_to_type=a.ext_to_class_name;type_to_type=a.class_to_classes}function ScrollPanel(a){this.panel=a}$.extend(ScrollPanel.prototype,{test:function(v,d){clearTimeout(this.timeout);var k=v.pageX,j=v.pageY,l=$(this.panel),c=l.position(),b=l.width(),i=l.height(),w=l.parent(),s=w.width(),a=w.height(),r=w.offset(),p=r.left,m=r.top,A=p+w.width(),u=m+w.height(),B=-(b-(s/2)),z=-(i-(a/2)),g=(s/2),f=(a/2),h=false,q=5,o=23;if(k-q<p){if(c.left<g){var n=Math.min(o,g-c.left);l.css("left",c.left+n);h=true}}else{if(k+q>A){if(c.left>B){var n=Math.min(o,c.left-B);l.css("left",c.left-n);h=true}}else{if(j-q<m){if(c.top<f){var n=Math.min(o,f-c.top);l.css("top",c.top+n);h=true}}else{if(j+q>u){if(c.top>z){var n=Math.mi n(o,c.top-B);l.css("top",(c.top-n)+"px");h=true}}}}}if(h){d();var l=this;this.timeout=setTimeout(function(){l.test(v,d)},50)}},stop:function(b,a){clearTimeout(this.timeout)}});function CanvasManager(b,a){this.cv=b;this.cc=this.cv.find("#canvas-container");this.oc=a.find("#overview-canvas");this.ov=a.find("#overview-viewport");this.init_drag()}$.extend(CanvasManager.prototype,{init_drag:function(){var b=this;var a=function(f,g){f=Math.min(f,b.cv.width()/2);f=Math.max(f,-b.cc.width()+b.cv.width()/2);g=Math.min(g,b.cv.height()/2);g=Math.max(g,-b.cc.height()+b.cv.height()/2);b.cc.css({left:f,top:g});b.update_viewport_overlay()};this.cc.each(function(){this.scroll_panel=new ScrollPanel(this)});var d,c;this.cv.bind("dragstart",function(g){var h=$(this).offset();var f=b.cc.position();c=f.top-h.top;d=f.left-h.left}).bind("drag",function(f){a(f.offsetX+d,f.offsetY+c)}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});this.ov.bind("drag",function(k){var j=b .cc.width(),g=b.cc.height(),f=b.oc.width(),h=b.oc.height(),i=$(this).offsetParent().offset(),m=k.offsetX-i.left,l=k.offsetY-i.top;a(-(m/f*j),-(l/h*g))}).bind("dragend",function(){workflow.fit_canvas_to_nodes();b.draw_overview()});$("#overview-border").bind("drag",function(g){var i=$(this).offsetParent();var h=i.offset();var f=Math.max(i.width()-(g.offsetX-h.left),i.height()-(g.offsetY-h.top));$(this).css({width:f,height:f});b.draw_overview()});$("#overview-border div").bind("drag",function(f){})},update_viewport_overlay:function(){var b=this.cc,f=this.cv,a=this.oc,c=this.ov,d=b.width(),j=b.height(),i=a.width(),g=a.height(),h=b.position();c.css({left:-(h.left/d*i),top:-(h.top/j*g),width:(f.width()/d*i)-2,height:(f.height()/j*g)-2})},draw_overview:function(){var j=$("#overview-canvas"),m=j.parent().parent().width(),i=j.get(0).getContext("2d"),d=$("#canvas-container").width(),l=$("#canvas-container").height();var g,a,k,f;var h=this.cv.width();var b=this.cv.height();if(d<h&&l<b) {k=d/h*m;f=(m-k)/2;g=l/b*m;a=(m-g)/2}else{if(d<l){a=0;g=m;k=Math.ceil(g*d/l);f=(m-k)/2}else{k=m;f=0;g=Math.ceil(k*l/d);a=(m-g)/2}}j.parent().css({left:f,top:a,width:k,height:g});j.attr("width",k);j.attr("height",g);i.fillStyle="#D2C099";i.strokeStyle="#D8B365";i.lineWidth=1;$.each(workflow.nodes,function(t,q){var s=$(q.element),n=s.position(),c=n.left/d*k,r=n.top/l*g,o=s.width()/d*k,p=s.height()/l*g;i.fillRect(c,r,o,p);i.strokeRect(c,r,o,p)});this.update_viewport_overlay()}}); \ No newline at end of file diff -r d24f0190c2a8 -r 068d572a2bf8 templates/dataset/edit_attributes.mako --- a/templates/dataset/edit_attributes.mako Mon Feb 01 13:34:44 2010 -0500 +++ b/templates/dataset/edit_attributes.mako Mon Feb 01 16:30:18 2010 -0500 @@ -58,21 +58,32 @@ <div style="clear: both"></div> </div> %if trans.get_user() is not None: - <%namespace file="../tagging_common.mako" import="render_individual_tagging_element" /> - <div class="form-row"> - <label> - Tags: - </label> - <div style="float: left; width: 295px; margin-right: 10px; border-style: inset; border-width: 1px"> - <style> - .tag-area { - border: none; - } - </style> - ${render_individual_tagging_element(user=trans.get_user(), tagged_item=data, elt_context="edit_attributes.mako", use_toggle_link=False, in_form=True, input_size="30")} + <%namespace file="../tagging_common.mako" import="render_individual_tagging_element" /> + <div class="form-row"> + <label> + Tags: + </label> + <div style="float: left; width: 295px; margin-right: 10px; border-style: inset; border-width: 1px; margin-left: 2px"> + <style> + .tag-area { + border: none; + } + </style> + ${render_individual_tagging_element(user=trans.get_user(), tagged_item=data, elt_context="edit_attributes.mako", use_toggle_link=False, in_form=True, input_size="30")} + </div> + <div style="clear: both"></div> + <div class="toolParamHelp">Apply tags to make it easy to search for and find items with the same tag.</div> </div> - <div style="clear: both"></div> - </div> + <div class="form-row"> + <label> + Annotation / Notes: + </label> + <div style="float: left; width: 250px; margin-right: 10px;"> + <textarea name="annotation" cols="40" rows="2">${data_annotation}</textarea> + </div> + <div style="clear: both"></div> + <div class="toolParamHelp">Add an annotation or notes to a dataset; annotations are available when a history is viewed.</div> + </div> %endif %for name, spec in data.metadata.spec.items(): %if spec.visible: diff -r d24f0190c2a8 -r 068d572a2bf8 templates/grid_base.mako --- a/templates/grid_base.mako Mon Feb 01 13:34:44 2010 -0500 +++ b/templates/grid_base.mako Mon Feb 01 16:30:18 2010 -0500 @@ -844,7 +844,7 @@ ## Item selection column %if show_item_checkboxes: <td style="width: 1.5em;"> - <input type="checkbox" id="${encoded_id}" name="id" value="${encoded_id}" class="grid-row-select-checkbox" /> + <input type="checkbox" name="id" value="${encoded_id}" id="${encoded_id}" class="grid-row-select-checkbox" /> </td> %endif ## Data columns diff -r d24f0190c2a8 -r 068d572a2bf8 templates/root/history.mako --- a/templates/root/history.mako Mon Feb 01 13:34:44 2010 -0500 +++ b/templates/root/history.mako Mon Feb 01 16:30:18 2010 -0500 @@ -15,7 +15,7 @@ <meta http-equiv="Pragma" content="no-cache"> ${h.css( "base", "history", "autocomplete_tagging" )} -${h.js( "jquery", "json2", "jquery.jstore-all", "jquery.autocomplete", "autocomplete_tagging" )} +${h.js( "galaxy.base", "jquery", "json2", "jquery.jstore-all", "jquery.autocomplete", "autocomplete_tagging" )} <script type="text/javascript"> $(function() { @@ -42,36 +42,20 @@ $.jStore.remove("history_expand_state"); })); - $("#history-rename").click( function() { - var old_name = $("#history-name").text() - var t = $("<input type='text' value='" + old_name + "'></input>" ); - t.blur( function() { - $(this).remove(); - $("#history-name").show(); - }); - t.keyup( function( e ) { - if ( e.keyCode == 27 ) { - // Escape key - $(this).trigger( "blur" ); - } else if ( e.keyCode == 13 ) { - // Enter key - new_value = this.value; - $(this).trigger( "blur" ); - $.ajax({ - url: "${h.url_for( controller='history', action='rename_async', id=history.id )}", - data: { "_": true, new_name: new_value }, - error: function() { alert( "Rename failed" ) }, - success: function() { - $("#history-name").text( new_value ); - } - }); - } - }); - $("#history-name").hide(); - $("#history-name-area").append( t ); - t.focus(); - return false; - }); + // Rename async. + async_save_text("history-rename", "history-name", "${h.url_for( controller="/history", action="rename_async", id=trans.security.encode_id(history.id) )}", "new_name"); + + // Annotation async. + // Tag async. Simply have the workflow tag 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("history-annotate", "history-annotation", "${h.url_for( controller="/history", action="annotate_async", id=trans.security.encode_id(history.id) )}", "new_annotation", true, 4); + // Updater updater({ <% updateable = [data for data in reversed( datasets ) if data.visible and data.state not in [ "deleted", "empty", "error", "ok" ]] %> @@ -266,6 +250,9 @@ .historyItemBody { display: none; } +div.form-row { + padding: 5px 5px 5px 0px; +} </style> <noscript> @@ -303,12 +290,31 @@ <%namespace file="history_common.mako" import="render_dataset" /> %if trans.get_user() is not None: - <style> - .tag-element { - margin-bottom: 0.5em; - } - </style> - ${render_individual_tagging_element( user=trans.get_user(), tagged_item=history, elt_context='history.mako' )} + <div style="margin: 0px 0px 5px 10px"> + <a href="#" onclick="$('#tags-and-annotation').toggle('fast')">Edit Tags and Annotation/Notes</a> + <div id="tags-and-annotation" style="display: none"> + ## Tagging elt. + <div class="form-row"> + <label>Tags:</label> + <div style="float: right"><a id="workflow-tag" title="Tag" class="icon-button edit" target="galaxy_main" href="${h.url_for( controller='workflow', action='annotate_async' )}"></a></div> + <style> + .tag-area { + border: none; + } + </style> + ${render_individual_tagging_element(user=trans.get_user(), tagged_item=history, elt_context="history.mako", use_toggle_link=False, input_size="20", render_add_tag_button=False)} + <div style="clear: both"></div> + </div> + + ## Annotation elt. + <div id="history-annotation-area" class="form-row"> + <label>Annotation / Notes:</label> + <div style="float: right"><a id="history-annotate" title="Annotate" class="icon-button edit" target="galaxy_main" href="${h.url_for( controller='history', action='annotate_async' )}"></a></div> + <div id="history-annotation">${annotation}</div> + <div style="clear: both"></div> + </div> + </div> + </div> %endif %if not datasets: diff -r d24f0190c2a8 -r 068d572a2bf8 templates/sharing_base.mako --- a/templates/sharing_base.mako Mon Feb 01 13:34:44 2010 -0500 +++ b/templates/sharing_base.mako Mon Feb 01 16:30:18 2010 -0500 @@ -67,7 +67,7 @@ <div class="indent" style="margin-top: 2em"> <h3>Making ${item_class_name} Accessible via Link and Publishing It</h3> - <div class="indent"> + <div> %if item.importable: <% item_status = "accessible via link" @@ -75,7 +75,7 @@ item_status = item_status + " and published" %> This ${item_class_name_lc} <strong>${item_status}</strong>. - <div class="indent"> + <div> <p>Anyone can view and import this ${item_class_name_lc} by visiting the following URL: <% url = h.url_for( action='display_by_username_and_slug', username=trans.get_user().username, slug=item.slug, qualified=True ) %> <blockquote> @@ -88,7 +88,7 @@ </div> <p>You can: - <div class="indent"> + <div> <form action="${h.url_for( action='sharing', id=trans.security.encode_id( item.id ) )}" method="POST"> %if not item.published: @@ -130,7 +130,7 @@ <h3>Sharing ${item_class_name} with Specific Users</h3> - <div class="indent"> + <div> %if item.users_shared_with: <p> diff -r d24f0190c2a8 -r 068d572a2bf8 templates/tagging_common.mako --- a/templates/tagging_common.mako Mon Feb 01 13:34:44 2010 -0500 +++ b/templates/tagging_common.mako Mon Feb 01 16:30:18 2010 -0500 @@ -20,7 +20,7 @@ %endif ## Render HTML for a list of tags. -<%def name="render_tagging_element_html(elt_id=None, tags=None, editable=True, use_toggle_link=True, input_size='15', in_form=False, tag_type='individual')"> +<%def name="render_tagging_element_html(elt_id=None, tags=None, editable=True, use_toggle_link=True, input_size='15', in_form=False, tag_type='individual', render_add_tag_button=True)"> ## Useful attributes. <% num_tags = len( tags ) @@ -79,7 +79,9 @@ <input class="tag-input" type='text' size='${input_size}'/> %endif ## Add "add tag" button. - <img src='${h.url_for('/static/images/add_icon.png')}' rollover='${h.url_for('/static/images/add_icon_dark.png')}' class="add-tag-button"/> + %if render_add_tag_button: + <img src='${h.url_for('/static/images/add_icon.png')}' rollover='${h.url_for('/static/images/add_icon_dark.png')}' class="add-tag-button"/> + %endif %endif </div> </div> @@ -102,7 +104,7 @@ ## Render individual tagging element. -<%def name="render_individual_tagging_element(user=None, tagged_item=None, elt_context=None, use_toggle_link=True, in_form=False, input_size='15', tag_click_fn='default_tag_click_fn', get_toggle_link_text_fn='default_get_toggle_link_text_fn', editable=True)"> +<%def name="render_individual_tagging_element(user=None, tagged_item=None, elt_context=None, use_toggle_link=True, in_form=False, input_size='15', tag_click_fn='default_tag_click_fn', get_toggle_link_text_fn='default_get_toggle_link_text_fn', editable=True, render_add_tag_button=True)"> ## Useful attributes. <% # Useful ids. @@ -114,7 +116,7 @@ %> ## Build HTML. - ${self.render_tagging_element_html(elt_id, item_tags, editable, use_toggle_link, input_size, in_form)} + ${self.render_tagging_element_html(elt_id=elt_id, tags=item_tags, editable=editable, use_toggle_link=use_toggle_link, input_size=input_size, in_form=in_form, render_add_tag_button=render_add_tag_button)} ## Build script that augments tags using progressive javascript. <script type="text/javascript"> diff -r d24f0190c2a8 -r 068d572a2bf8 templates/workflow/edit_attributes.mako --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/workflow/edit_attributes.mako Mon Feb 01 16:30:18 2010 -0500 @@ -0,0 +1,66 @@ +<%inherit file="/base.mako"/> +<%namespace file="/message.mako" import="render_msg" /> + +<%def name="title()">${_('Edit Workflow Attributes')}</%def> + +<%def name="stylesheets()"> + ${h.css( "base", "autocomplete_tagging" )} +</%def> + +<%def name="javascripts()"> + ${parent.javascripts()} + ${h.js( "galaxy.base", "jquery.autocomplete", "autocomplete_tagging" )} +</%def> + +%if msg: + ${render_msg( msg, messagetype )} +%endif + +<%def name="body()"> + <div class="toolForm"> + <div class="toolFormTitle">${_('Edit Workflow Attributes')}</div> + <div class="toolFormBody"> + <form name="edit_attributes" action="${h.url_for( action='edit_attributes' )}" method="post"> + <input type="hidden" name="id" value="${trans.security.encode_id( stored.id )}"/> + <div class="form-row"> + <label> + Name: + </label> + <div style="float: left; width: 225px; margin-right: 10px;"> + <input type="text" name="name" value="${stored.name}" size="30"/> + </div> + <div style="clear: both"></div> + </div> + <%namespace file="/tagging_common.mako" import="render_individual_tagging_element" /> + <div class="form-row"> + <label> + Tags: + </label> + <div style="float: left; width: 265px; margin-right: 10px; border-style: inset; border-width: 1px; margin-left: 2px"> + <style> + .tag-area { + border: none; + } + </style> + ${render_individual_tagging_element(user=trans.get_user(), tagged_item=stored, elt_context="edit_attributes.mako", use_toggle_link=False, in_form=True, input_size="25")} + </div> + <div style="clear: both"></div> + <div class="toolParamHelp">Apply tags to make it easy to search for and find items with the same tag.</div> + </div> + <div class="form-row"> + <label> + Annotation / Notes: + </label> + <div style="float: left; width: 225px; margin-right: 10px;"> + <textarea name="annotation" cols="30" rows="2">${annotation}</textarea> + </div> + <div style="clear: both"></div> + <div class="toolParamHelp">Add an annotation notes to a workflow; annotations are available when a workflow is viewed.</div> + </div> + <div class="form-row"> + <input type="submit" name="save" value="${_('Save')}"/> + </div> + </form> + </div> + </div> +</%def> \ No newline at end of file diff -r d24f0190c2a8 -r 068d572a2bf8 templates/workflow/editor.mako --- a/templates/workflow/editor.mako Mon Feb 01 13:34:44 2010 -0500 +++ b/templates/workflow/editor.mako Mon Feb 01 16:30:18 2010 -0500 @@ -34,7 +34,9 @@ "jquery.jstore-all", "json2", "galaxy.base", - "galaxy.workflow_editor.canvas" )} + "galaxy.workflow_editor.canvas", + "jquery.autocomplete", + "autocomplete_tagging")} <!--[if lt IE 7]> <script type='text/javascript'> @@ -77,7 +79,7 @@ // Load workflow definition $.ajax( { url: "${h.url_for( action='load_workflow' )}", - data: { id: "${trans.security.encode_id( workflow_id )}", "_": "true" }, + data: { id: "${trans.security.encode_id( stored.id )}", "_": "true" }, dataType: 'json', cache: false, success: function( data ) { @@ -142,6 +144,12 @@ canvas_manager.draw_overview(); }); + $('#edit-attributes-button').click( function() { + workflow.clear_active_node(); + $('#edit-attributes').show(); + $('#right-content').hide(); + }); + $.jStore.ready(function(engine) { engine.ready(function() { // On load, set the size to the pref stored in local storage if it exists @@ -209,6 +217,19 @@ } }); }); + + // Rename async. + async_save_text("workflow-rename", "workflow-name", "${h.url_for( action="rename_async", id=trans.security.encode_id(stored.id) )}", "new_name"); + + // Tag async. Simply have the workflow tag 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-annotate", "workflow-annotation", "${h.url_for( action="annotate_async", id=trans.security.encode_id(stored.id) )}", "new_annotation", true, 4); }); // Global state for the whole workflow @@ -285,7 +306,8 @@ }; function show_form_for_tool( text, node ) { - $("#right-content").html( text ); + $("#edit-attributes").hide(); + $("#right-content").show().html( text ); $("#right-content").find( "form" ).ajaxForm( { type: 'POST', dataType: 'json', @@ -316,6 +338,17 @@ $(this).remove(); make_popupmenu( b, options ); }); + // Add annotation field to form. + // TODO: need to set the annotation for this tool. + var annotation_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>"); + $(this).append( annotation_div ); // 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 @@ -366,7 +399,7 @@ url: "${h.url_for( action='save_workflow' )}", type: "POST", data: { - id: "${trans.security.encode_id( workflow_id )}", + id: "${trans.security.encode_id( stored.id )}", workflow_data: function() { return JSON.stringify( workflow.to_simple() ) }, "_": "true" }, @@ -421,10 +454,10 @@ <%def name="stylesheets()"> ## Include "base.css" for styling tool menu and forms (details) - <link href="${h.url_for('/static/style/base.css')}" rel="stylesheet" type="text/css" /> + ${h.css( "base", "autocomplete_tagging")} ## But make sure styles for the layout take precedence - ${parent.stylesheets()} + ${parent.stylesheets()} <style type="text/css"> body { margin: 0; padding: 0; overflow: hidden; } @@ -508,9 +541,8 @@ div.toolFormRow { position: relative; } - - - #right-content { + + .right-content { margin: 5px; } @@ -575,7 +607,6 @@ } .form-row { - } div.toolFormInCanvas div.toolFormBody { padding: 0; @@ -740,9 +771,52 @@ <div class="unified-panel-header" unselectable="on"> <div class="unified-panel-header-inner"> Details + <div style="float: right"> + <a id="edit-attributes-button" class="panel-header-button">Edit Workflow Attributes</a> + </div> </div> </div> <div class="unified-panel-body" style="overflow: auto;"> - <div id="right-content"></div> + ## Div for elements to modify workflow attributes. + <div id="edit-attributes" class="toolForm right-content"> + <div class="toolFormTitle">Edit Workflow Attributes</div> + <div class="toolFormBody"> + ## Workflow name. + <div id="workflow-name-area" class="form-row"> + <label>Name:</label> + <div style="float: right"><a id="workflow-rename" title="Rename" class="icon-button edit" target="galaxy_main" href="${h.url_for( controller='workflow', action='rename_sync' )}"></a></div> + <div id="workflow-name">${stored.name}</div> + <div style="clear: both"></div> + </div> + ## Workflow tags. + <%namespace file="/tagging_common.mako" import="render_individual_tagging_element" /> + <div class="form-row"> + <label> + Tags: + </label> + <div style="float: right"><a id="workflow-tag" title="Tag" class="icon-button edit" target="galaxy_main" href="${h.url_for( controller='workflow', action='annotate_async' )}"></a></div> + <div style="float: left; width: 225px; margin-right: 10px; border-style: inset; border-width: 1px; margin-left: 2px"> + <style> + .tag-area { + border: none; + } + </style> + ${render_individual_tagging_element(user=trans.get_user(), tagged_item=stored, elt_context="edit_attributes.mako", use_toggle_link=False, input_size="20", render_add_tag_button=False)} + </div> + <div style="clear: both"></div> + <div class="toolParamHelp">Apply tags to make it easy to search for and find items with the same tag.</div> + </div> + ## Workflow annotation. + <div id="workflow-annotation-area" class="form-row"> + <label>Annotation / Notes:</label> + <div style="float: right"><a id="workflow-annotate" title="Annotate" class="icon-button edit" target="galaxy_main" href="${h.url_for( controller='workflow', action='annotate_async' )}"></a></div> + <div id="workflow-annotation">${annotation}</div> + <div style="clear: both"></div> + <div class="toolParamHelp">Add an annotation or notes to a workflow; annotations are available when a workflow is viewed.</div> + </div> + </div> + </div> + ## Div where tool details are loaded and modified. + <div id="right-content" class="right-content"></div> </div> </%def>