mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Add bookmarking to Trackster. Also group Trackster options into a single menu button.
This commit is contained in:
@@ -160,6 +160,7 @@ class UsesVisualization( SharableItemSecurity ):
|
||||
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 @@ class UsesVisualization( SharableItemSecurity ):
|
||||
} )
|
||||
|
||||
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']
|
||||
|
||||
@@ -571,6 +571,8 @@ class TracksController( BaseController, UsesVisualization, UsesHistoryDatasetAss
|
||||
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 @@ class TracksController( BaseController, UsesVisualization, UsesHistoryDatasetAss
|
||||
"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()
|
||||
|
||||
@@ -48,3 +48,7 @@ input{font:10px verdana;}
|
||||
.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;}
|
||||
@@ -278,4 +278,22 @@ input {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -330,7 +330,76 @@ function replace_big_select_inputs(min_length, max_length) {
|
||||
});
|
||||
}
|
||||
|
||||
// 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 @@ function async_save_text(click_to_edit_elt, text_elt_id, save_url, text_parm_nam
|
||||
}
|
||||
|
||||
// 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 @@ function async_save_text(click_to_edit_elt, text_elt_id, save_url, text_parm_nam
|
||||
}
|
||||
// Replace text with input object and focus & select.
|
||||
text_elt.hide();
|
||||
t.insertAfter( text_elt );
|
||||
t.insertAfter(text_elt);
|
||||
t.focus();
|
||||
t.select();
|
||||
|
||||
|
||||
+194
-112
@@ -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 @@ ${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.e
|
||||
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 @@ ${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.e
|
||||
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 @@ ${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.e
|
||||
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 @@ ${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.e
|
||||
|
||||
%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 @@ ${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jstorage", "jquery.e
|
||||
|
||||
</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>
|
||||
|
||||
@@ -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(); });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user