1 new changeset in galaxy-central: http://bitbucket.org/galaxy/galaxy-central/changeset/7412c7af8859/ changeset: 7412c7af8859 user: jgoecks date: 2011-06-10 19:38:24 summary: Add bookmarking to Trackster. Also group Trackster options into a single menu button. affected #: 7 files (7.0 KB) --- a/lib/galaxy/web/base/controller.py Fri Jun 10 13:15:17 2011 -0400 +++ b/lib/galaxy/web/base/controller.py Fri Jun 10 13:38:24 2011 -0400 @@ -160,6 +160,7 @@ if visualization.type == 'trackster': # Trackster config; taken from tracks/browser latest_revision = visualization.latest_revision + bookmarks = latest_revision.config.get( 'bookmarks', [] ) tracks = [] # Set tracks. @@ -193,7 +194,7 @@ } ) config = { "title": visualization.title, "vis_id": trans.security.encode_id( visualization.id ), - "tracks": tracks, "chrom": "", "dbkey": visualization.dbkey } + "tracks": tracks, "bookmarks": bookmarks, "chrom": "", "dbkey": visualization.dbkey } if 'viewport' in latest_revision.config: config['viewport'] = latest_revision.config['viewport'] --- a/lib/galaxy/web/controllers/tracks.py Fri Jun 10 13:15:17 2011 -0400 +++ b/lib/galaxy/web/controllers/tracks.py Fri Jun 10 13:38:24 2011 -0400 @@ -571,6 +571,8 @@ vis_rev.dbkey = dbkey # Tracks from payload tracks = [] + # TODO: why go through the trouble of unpacking config only to repack and + # put in database? How about sticking JSON directly into database? for track in decoded_payload['tracks']: tracks.append( { "dataset_id": track['dataset_id'], "hda_ldda": track.get('hda_ldda', "hda"), @@ -579,14 +581,15 @@ "prefs": track['prefs'], "is_child": track.get('is_child', False) } ) + bookmarks = decoded_payload[ 'bookmarks' ] + vis_rev.config = { "tracks": tracks, "bookmarks": bookmarks } # Viewport from payload if 'viewport' in decoded_payload: chrom = decoded_payload['viewport']['chrom'] start = decoded_payload['viewport']['start'] end = decoded_payload['viewport']['end'] - vis_rev.config = { "tracks": tracks, "viewport": { 'chrom': chrom, 'start': start, 'end': end } } - else: - vis_rev.config = { "tracks": tracks } + vis_rev.config[ "viewport" ] = { 'chrom': chrom, 'start': start, 'end': end } + vis.latest_revision = vis_rev session.add( vis_rev ) session.flush() --- a/static/june_2007_style/blue/trackster.css Fri Jun 10 13:15:17 2011 -0400 +++ b/static/june_2007_style/blue/trackster.css Fri Jun 10 13:38:24 2011 -0400 @@ -48,3 +48,7 @@ .param-label{float:left;font-weight:bold;padding-top:0.2em;} .child-track-icon{background:url('../images/fugue/arrow-000-small-bw.png') no-repeat;width:30px;cursor:move;} .track-resize{background:white url('../images/visualization/draggable_vertical.png') no-repeat top center;position:absolute;right:3px;bottom:-4px;width:14px;height:7px;border:solid #999 1px;z-index:100;} +.bookmark{background:white;border:solid #999 1px;border-right:none;margin:0.5em;margin-right:0;padding:0.5em} +.bookmark .position{font-weight:bold;} +.icon-button.import{margin-left:0.5em;width:100%;} +.delete-icon-container{float:right;} \ No newline at end of file --- a/static/june_2007_style/trackster.css.tmpl Fri Jun 10 13:15:17 2011 -0400 +++ b/static/june_2007_style/trackster.css.tmpl Fri Jun 10 13:38:24 2011 -0400 @@ -278,4 +278,22 @@ border: solid #999 1px; z-index: 100; } +.bookmark { + background:white; + border:solid #999 1px; + border-right:none; + margin:0.5em; + margin-right:0; + padding:0.5em +} +.bookmark .position { + font-weight:bold; +} +.icon-button.import { + margin-left:0.5em; + width:100%; +} +.delete-icon-container { + float:right; +} --- a/static/scripts/galaxy.base.js Fri Jun 10 13:15:17 2011 -0400 +++ b/static/scripts/galaxy.base.js Fri Jun 10 13:38:24 2011 -0400 @@ -330,7 +330,76 @@ }); } -// Edit and save text asynchronously. +/** + * Returns editable text element. Element is a div with text: (a) when user clicks on text, a textbox/area + * enables user to edit text; (b) when user presses enter key, element's text is set. + */ +// TODO: use this function to implement async_save_text (implemented below). +function get_editable_text_elt(text, use_textarea, num_cols, num_rows, on_finish) { + // Set defaults if necessary. + if (num_cols === undefined) { + num_cols = 30; + } + if (num_rows === undefined) { + num_rows = 4; + } + + // Create div for element. + var container = $("<div/>").addClass("editable-text").text(text).click(function() { + // If there's already an input element, editing is active, so do nothing. + if ($(this).children(":input").length > 0) { + return; + } + + container.removeClass("editable-text"); + + // Set element text. + var set_text = function(new_text) { + if (new_text != "") { + container.text(new_text); + } + else { + // Need a line so that there is a click target. + container.html("<br>"); + } + container.addClass("editable-text"); + }; + + // Create input element for editing. + var cur_text = container.text(), + input_elt = (use_textarea ? + $("<textarea></textarea>").attr({ rows: num_rows, cols: num_cols }).text( $.trim(cur_text) ) : + $("<input type='text'></input>").attr({ value: $.trim(cur_text), size: num_cols }) + ).blur(function() { + $(this).remove(); + set_text(cur_text); + }).keyup(function(e) { + if (e.keyCode === 27) { + // Escape key. + $(this).trigger("blur"); + } else if (e.keyCode === 13) { + // Enter key. + $(this).remove(); + var new_text = $(this).val(); + set_text(new_text); + if (on_finish) { + on_finish(new_text); + } + } + }); + + // Replace text with input object and focus & select. + container.text(""); + container.append(input_elt); + input_elt.focus(); + input_elt.select(); + }); + return container; +} + +/** + * Edit and save text asynchronously. + */ function async_save_text(click_to_edit_elt, text_elt_id, save_url, text_parm_name, num_cols, use_textarea, num_rows, on_start, on_finish) { // Set defaults if necessary. if (num_cols === undefined) { @@ -341,7 +410,7 @@ } // Set up input element. - $("#" + click_to_edit_elt).live( "click", function() { + $("#" + click_to_edit_elt).live("click", function() { // Check if this is already active if ( $("#renaming-active").length > 0) { return; @@ -399,7 +468,7 @@ } // Replace text with input object and focus & select. text_elt.hide(); - t.insertAfter( text_elt ); + t.insertAfter(text_elt); t.focus(); t.select(); --- a/templates/tracks/browser.mako Fri Jun 10 13:15:17 2011 -0400 +++ b/templates/tracks/browser.mako Fri Jun 10 13:38:24 2011 -0400 @@ -3,7 +3,7 @@ <%def name="init()"><% self.has_left_panel=False - self.has_right_panel=False + self.has_right_panel=True self.active_view="visualization" self.message_box_visible=False %> @@ -35,19 +35,6 @@ </style></%def> -<%def name="center_panel()"> -<div class="unified-panel-header" unselectable="on"> - <div class="unified-panel-header-inner"> - <div style="float:left;" id="title"></div> - <a class="panel-header-button right-float" href="${h.url_for( controller='visualization', action='list' )}">Close</a> - <a id="save-button" class="panel-header-button right-float" href="javascript:void(0);">Save</a> - <a id="add-track" class="panel-header-button right-float" href="javascript:void(0);">Add Tracks</a> - </div> -</div> -<div id="browser-container" class="unified-panel-body"></div> - -</%def> - <%def name="javascripts()"> ${parent.javascripts()} @@ -72,8 +59,36 @@ converted_datasets_state_url = "${h.url_for( action='converted_datasets_state' )}", addable_track_types = { "LineTrack": LineTrack, "FeatureTrack": FeatureTrack, "ReadTrack": ReadTrack }, view; + + + /** + * Add bookmark. + */ + var add_bookmark = function(position, annotation) { + var + bookmarks_container = $("#bookmarks-container"), + new_bookmark = $("<div/>").addClass("bookmark").appendTo(bookmarks_container), + delete_icon_container = $("<div/>").addClass("delete-icon-container").appendTo(new_bookmark).click(function (){ + // Remove bookmark. + new_bookmark.slideUp("fast"); + new_bookmark.remove(); + view.has_changes = true; + return false; + }), + delete_icon = $("<a href=''/>").addClass("icon-button delete").appendTo(delete_icon_container), + position_div = $("<div/>").addClass("position").text(position).appendTo(new_bookmark), + annotation_div = get_editable_text_elt(annotation, false).addClass("annotation").appendTo(new_bookmark); + + view.has_changes = true; + return new_bookmark; + } $(function() { + // Hide bookmarks by default right now. + parent.force_right_panel("hide"); + + // Resize view when showing/hiding right panel (bookmarks for now). + $("#right-border").click(function() { view.resize_window(); }); %if config: var callback; @@ -107,6 +122,17 @@ parent_obj.add_track(track); } init(); + + // Load bookmarks. + var bookmarks = JSON.parse('${ h.to_json_string( config.get('bookmarks') ) }'), + bookmark; + for (var i = 0; i < bookmarks.length; i++) { + bookmark = bookmarks[i]; + add_bookmark(bookmark['position'], bookmark['annotation']); + } + + // View has no changes as of yet. + view.has_changes = false; %else: var continue_fn = function() { view = new View( $("#browser-container"), $("#new-title").val(), undefined, $("#new-dbkey").val() ); @@ -149,13 +175,13 @@ track_data.prefs, track_data.filters, track_data.tool ); view.add_track(new_track); // Should replace with live event but can't get working - sortable( new_track.container_div, ".draghandle" ); + sortable(new_track.container_div, ".draghandle"); view.has_changes = true; $("#no-tracks").hide(); }; %if add_dataset is not None: - $.ajax( { + $.ajax({ url: "${h.url_for( action='add_track_async' )}", data: { hda_id: "${add_dataset}" }, dataType: "json", @@ -164,105 +190,132 @@ %endif - // Use a popup grid to add more tracks - $("#add-track").bind("click", function(e) { - $.ajax({ - url: "${h.url_for( action='list_histories' )}", - data: { "f-dbkey": view.dbkey }, - error: function() { alert( "Grid failed" ); }, - success: function(table_html) { - show_modal( - "Select datasets for new tracks", - table_html, { - "Cancel": function() { - hide_modal(); - }, - "Insert": function() { - var requests = []; - $('input[name=id]:checked,input[name=ldda_ids]:checked').each(function() { - var data, - id = $(this).val(); - if ($(this).attr("name") === "id") { - data = { hda_id: id }; - } else { - data = { ldda_id: id}; - } - requests[requests.length] = $.ajax({ - url: "${h.url_for( action='add_track_async' )}", - data: data, - dataType: "json", - }); - }); - // To preserve order, wait until there are definitions for all tracks and then add - // them sequentially. - $.when.apply($, requests).then(function() { - // jQuery always returns an Array for arguments, so need to look at first element - // to determine whether multiple requests were made and consequently how to - // map arguments to track definitions. - var track_defs = (arguments[0] instanceof Array ? - $.map(arguments, function(arg) { return arg[0]; }) : - [ arguments[0] ] - ); - for (var i= 0; i < track_defs.length; i++) { - add_async_success(track_defs[i]); - } - }); - hide_modal(); + $("#viz-options-button").css( "position", "relative" ); + make_popupmenu( $("#viz-options-button"), { + "Add Tracks": function() { + // Use a popup grid to add more tracks + $.ajax({ + url: "${h.url_for( action='list_histories' )}", + data: { "f-dbkey": view.dbkey }, + error: function() { alert( "Grid failed" ); }, + success: function(table_html) { + show_modal( + "Select datasets for new tracks", + table_html, { + "Cancel": function() { + hide_modal(); + }, + "Insert": function() { + var requests = []; + $('input[name=id]:checked,input[name=ldda_ids]:checked').each(function() { + var data, + id = $(this).val(); + if ($(this).attr("name") === "id") { + data = { hda_id: id }; + } else { + data = { ldda_id: id}; + } + requests[requests.length] = $.ajax({ + url: "${h.url_for( action='add_track_async' )}", + data: data, + dataType: "json", + }); + }); + // To preserve order, wait until there are definitions for all tracks and then add + // them sequentially. + $.when.apply($, requests).then(function() { + // jQuery always returns an Array for arguments, so need to look at first element + // to determine whether multiple requests were made and consequently how to + // map arguments to track definitions. + var track_defs = (arguments[0] instanceof Array ? + $.map(arguments, function(arg) { return arg[0]; }) : + [ arguments[0] ] + ); + for (var i= 0; i < track_defs.length; i++) { + add_async_success(track_defs[i]); + } + }); + hide_modal(); + } } - } - ); - } - }); + ); + } + }); + }, + "Save": function() { + // Show saving dialog box + show_modal("Saving...", "<img src='${h.url_for('/static/images/yui/rel_interstitial_loading.gif')}'/>"); + + // Save all tracks. + var tracks = []; + $(".viewport-container .track").each(function () { + // ID has form track_<main_track_id>_<child_track_id> + var + id_split = $(this).attr("id").split("_"), + track_id = id_split[1], + child_id = id_split[2]; + + // Get track. + var track = view.tracks[track_id]; + if (child_id) { + track = track.child_tracks[child_id]; + } + + // Add track. + tracks.push( { + "track_type": track.track_type, + "name": track.name, + "hda_ldda": track.hda_ldda, + "dataset_id": track.dataset_id, + "prefs": track.prefs, + "is_child": (child_id ? true : false ) + }); + }); + + // Save all bookmarks. + var bookmarks = []; + $(".bookmark").each(function() { + bookmarks[bookmarks.length] = { + position: $(this).children(".position").text(), + annotation: $(this).children(".annotation").text() + }; + }); + + var payload = { + 'tracks': tracks, + 'viewport': { 'chrom': view.chrom, 'start': view.low , 'end': view.high }, + 'bookmarks': bookmarks + }; + + $.ajax({ + url: "${h.url_for( action='save' )}", + type: "POST", + data: { + 'vis_id': view.vis_id, + 'vis_title': view.title, + 'dbkey': view.dbkey, + 'payload': JSON.stringify(payload) + }, + success: function(vis_id) { + view.vis_id = vis_id; + view.has_changes = false; + hide_modal(); + }, + error: function() { alert("Could not save visualization"); } + }); + }, + "Bookmarks": function() { + // HACK -- use style to determine if panel is hidden and hide/show accordingly. + parent.force_right_panel(($("div#right").css("right") == "0px" ? "hide" : "show")); + }, + "Close": function() { window.location = "${h.url_for( controller='visualization', action='list' )}"; } }); - $("#save-button").bind("click", function(e) { - // Show saving dialog box - show_modal("Saving...", "<img src='${h.url_for('/static/images/yui/rel_interstitial_loading.gif')}'/>"); - - // Save all tracks. - var tracks = []; - $(".viewport-container .track").each(function () { - // ID has form track_<main_track_id>_<child_track_id> - var - id_split = $(this).attr("id").split("_"), - track_id = id_split[1], - child_id = id_split[2]; - - // Get track. - var track = view.tracks[track_id]; - if (child_id) { - track = track.child_tracks[child_id]; - } - - // Add track. - tracks.push( { - "track_type": track.track_type, - "name": track.name, - "hda_ldda": track.hda_ldda, - "dataset_id": track.dataset_id, - "prefs": track.prefs, - "is_child": (child_id ? true : false ) - }); - }); - - var payload = { 'tracks': tracks, 'viewport': { 'chrom': view.chrom, 'start': view.low , 'end': view.high } }; - - $.ajax({ - url: "${h.url_for( action='save' )}", - type: "POST", - data: { - 'vis_id': view.vis_id, - 'vis_title': view.title, - 'dbkey': view.dbkey, - 'payload': JSON.stringify(payload) - }, - success: function(vis_id) { - view.vis_id = vis_id; - view.has_changes = false; - hide_modal(); - }, - error: function() { alert("Could not save visualization"); } - }); + $("#add-bookmark-button").click(function() { + // Add new bookmark. + var position = view.chrom + ":" + view.low + "-" + view.high, + annotation = "Bookmark description"; + return add_bookmark(position, annotation); }); // @@ -298,3 +351,32 @@ </script></%def> + +<%def name="center_panel()"> +<div class="unified-panel-header" unselectable="on"> + <div class="unified-panel-header-inner"> + <div style="float:left;" id="title"></div> + <div style="float: right"> + <a id="viz-options-button" class='panel-header-button popup' href="javascript:void(0)" target="galaxy_main">${_('Options')}</a> + </div> + </div> +</div> +<div id="browser-container" class="unified-panel-body"></div> + +</%def> + +<%def name="right_panel()"> + +<div class="unified-panel-header" unselectable="on"> + <div class="unified-panel-header-inner"> + Bookmarks + </div> +</div> +<div class="unified-panel-body" style="overflow: auto;"> + <div id="bookmarks-container"></div> + <div> + <a class="icon-button import" original-title="Add Bookmark" id="add-bookmark-button" href="javascript:void(0);">Add Bookmark</a> + </div> +</div> + +</%def> --- a/templates/visualization/display.mako Fri Jun 10 13:15:17 2011 -0400 +++ b/templates/visualization/display.mako Fri Jun 10 13:38:24 2011 -0400 @@ -78,6 +78,7 @@ if (container_element.parents(".item-content").length > 0) { // Embedded viz container_element.parents(".item-content").css( { "max-height": "none", "overflow": "visible" } ); } else { // Viewing just one shared viz + // TODO: need live or just bind click? $("#right-border").live("click", function() { view.resize_window(); }); } Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.