mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Added 'annotate history' functionality to pages. To accomplish this, refactored tagging from pure javascript to progressive javascript. Also fixed unicode and grid filtering bugs.
This commit is contained in:
@@ -7,6 +7,7 @@ import os, time, logging
|
||||
# Pieces of Galaxy to make global in every controller
|
||||
from galaxy import config, tools, web, model, util
|
||||
from galaxy.web import error, form, url_for
|
||||
from galaxy.model.orm import *
|
||||
|
||||
from Cheetah.Template import Template
|
||||
|
||||
@@ -25,6 +26,22 @@ class BaseController( object ):
|
||||
"""Returns the application toolbox"""
|
||||
return self.app.toolbox
|
||||
|
||||
def get_history( self, trans, id, check_ownership=True ):
|
||||
"""Get a History from the database by id, verifying ownership."""
|
||||
# Load history from database
|
||||
id = trans.security.decode_id( id )
|
||||
history = trans.sa_session.query( model.History ).get( id )
|
||||
if not history:
|
||||
err+msg( "History not found" )
|
||||
if check_ownership:
|
||||
# Verify ownership
|
||||
user = trans.get_user()
|
||||
if not user:
|
||||
error( "Must be logged in to manage histories" )
|
||||
if history.user != user:
|
||||
error( "History is not owned by current user" )
|
||||
return history
|
||||
|
||||
Root = BaseController
|
||||
"""
|
||||
Deprecated: `BaseController` used to be available under the name `Root`
|
||||
|
||||
@@ -44,7 +44,7 @@ class HistoryListGrid( grids.Grid ):
|
||||
return ""
|
||||
def get_link( self, trans, grid, item ):
|
||||
if item.users_shared_with or item.importable:
|
||||
return dict( operation="sharing" )
|
||||
return dict( operation="sharing", id=item.id )
|
||||
return None
|
||||
|
||||
class DeletedColumn( grids.GridColumn ):
|
||||
@@ -205,7 +205,7 @@ class HistoryController( BaseController ):
|
||||
# Load the histories and ensure they all belong to the current user
|
||||
histories = []
|
||||
for history_id in history_ids:
|
||||
history = get_history( trans, history_id )
|
||||
history = self.get_history( trans, history_id )
|
||||
if history:
|
||||
# Ensure history is owned by current user
|
||||
if history.user_id != None and trans.user:
|
||||
@@ -237,7 +237,7 @@ class HistoryController( BaseController ):
|
||||
history.importable = True
|
||||
elif operation == "disable import via link":
|
||||
if history_ids:
|
||||
histories = [ get_history( trans, history_id ) for history_id in history_ids ]
|
||||
histories = [ self.get_history( trans, history_id ) for history_id in history_ids ]
|
||||
for history in histories:
|
||||
if history.importable:
|
||||
history.importable = False
|
||||
@@ -332,7 +332,7 @@ class HistoryController( BaseController ):
|
||||
if not ids:
|
||||
message = "Select a history to unshare"
|
||||
return self.shared_list_grid( trans, status='error', message=message, **kwargs )
|
||||
histories = [ get_history( trans, history_id ) for history_id in ids ]
|
||||
histories = [ self.get_history( trans, history_id ) for history_id in ids ]
|
||||
for history in histories:
|
||||
# Current user is the user with which the histories were shared
|
||||
association = trans.sa_session.query( trans.app.model.HistoryUserShareAssociation ).filter_by( user=trans.user, history=history ).one()
|
||||
@@ -375,7 +375,7 @@ class HistoryController( BaseController ):
|
||||
@web.require_login( "get history name" )
|
||||
def get_name_async( self, trans, id=None ):
|
||||
""" Returns the name for a given history. """
|
||||
history = get_history( trans, id, False )
|
||||
history = self.get_history( trans, id, False )
|
||||
|
||||
# To get name: user must own history, history must be importable.
|
||||
if history.user == trans.get_user() or history.importable or trans.get_user() in history.users_shared_with:
|
||||
@@ -386,15 +386,15 @@ class HistoryController( BaseController ):
|
||||
@web.require_login( "set history's importable flag" )
|
||||
def set_importable_async( self, trans, id=None, importable=False ):
|
||||
""" Set history's importable attribute. """
|
||||
history = get_history( trans, id, True )
|
||||
history = self.get_history( trans, id, True )
|
||||
|
||||
# Only set if importable value would change; this prevents a change in the update_time unless attribute really changed.
|
||||
importable = importable in ['True', 'true', 't'];
|
||||
importable = importable in ['True', 'true', 't', 'T'];
|
||||
if history and history.importable != importable:
|
||||
history.importable = importable
|
||||
trans.sa_session.flush()
|
||||
|
||||
return result
|
||||
return
|
||||
|
||||
@web.expose
|
||||
def name_autocomplete_data( self, trans, q=None, limit=None, timestamp=None ):
|
||||
@@ -416,7 +416,7 @@ class HistoryController( BaseController ):
|
||||
user_history = trans.get_history()
|
||||
if not id:
|
||||
return trans.show_error_message( "You must specify a history you want to import." )
|
||||
import_history = get_history( trans, id, check_ownership=False )
|
||||
import_history = self.get_history( trans, id, check_ownership=False )
|
||||
if not import_history:
|
||||
return trans.show_error_message( "The specified history does not exist.")
|
||||
if not import_history.importable:
|
||||
@@ -470,7 +470,7 @@ class HistoryController( BaseController ):
|
||||
# Get history to view.
|
||||
if not id:
|
||||
return trans.show_error_message( "You must specify a history you want to view." )
|
||||
history_to_view = get_history( trans, id, False)
|
||||
history_to_view = self.get_history( trans, id, False)
|
||||
# Integrity checks.
|
||||
if not history_to_view:
|
||||
return trans.show_error_message( "The specified history does not exist." )
|
||||
@@ -512,7 +512,7 @@ class HistoryController( BaseController ):
|
||||
send_to_err = err_msg
|
||||
histories = []
|
||||
for history_id in id:
|
||||
histories.append( get_history( trans, history_id ) )
|
||||
histories.append( self.get_history( trans, history_id ) )
|
||||
return trans.fill_template( "/history/share.mako",
|
||||
histories=histories,
|
||||
email=email,
|
||||
@@ -618,7 +618,7 @@ class HistoryController( BaseController ):
|
||||
send_to_err = ""
|
||||
histories = []
|
||||
for history_id in id:
|
||||
histories.append( get_history( trans, history_id ) )
|
||||
histories.append( self.get_history( trans, history_id ) )
|
||||
send_to_users = []
|
||||
for email_address in util.listify( email ):
|
||||
email_address = email_address.strip()
|
||||
@@ -776,7 +776,7 @@ class HistoryController( BaseController ):
|
||||
if id:
|
||||
ids = util.listify( id )
|
||||
if ids:
|
||||
histories = [ get_history( trans, history_id ) for history_id in ids ]
|
||||
histories = [ self.get_history( trans, history_id ) for history_id in ids ]
|
||||
for history in histories:
|
||||
trans.sa_session.add( history )
|
||||
if params.get( 'enable_import_via_link', False ):
|
||||
@@ -831,7 +831,7 @@ class HistoryController( BaseController ):
|
||||
histories = []
|
||||
cur_names = []
|
||||
for history_id in id:
|
||||
history = get_history( trans, history_id )
|
||||
history = self.get_history( trans, history_id )
|
||||
if history and history.user_id == user.id:
|
||||
histories.append( history )
|
||||
cur_names.append( history.get_display_name() )
|
||||
@@ -872,7 +872,7 @@ class HistoryController( BaseController ):
|
||||
ids = util.listify( id )
|
||||
histories = []
|
||||
for history_id in ids:
|
||||
history = get_history( trans, history_id, check_ownership=False )
|
||||
history = self.get_history( trans, history_id, check_ownership=False )
|
||||
histories.append( history )
|
||||
user = trans.get_user()
|
||||
for history in histories:
|
||||
@@ -896,22 +896,4 @@ class HistoryController( BaseController ):
|
||||
msg = 'Clone with name "%s" is now included in your previously stored histories.' % new_history.name
|
||||
else:
|
||||
msg = '%d cloned histories are now included in your previously stored histories.' % len( histories )
|
||||
return trans.show_ok_message( msg )
|
||||
|
||||
## ---- Utility methods -------------------------------------------------------
|
||||
|
||||
def get_history( trans, id, check_ownership=True ):
|
||||
"""Get a History from the database by id, verifying ownership."""
|
||||
# Load history from database
|
||||
id = trans.security.decode_id( id )
|
||||
history = trans.sa_session.query( model.History ).get( id )
|
||||
if not history:
|
||||
err+msg( "History not found" )
|
||||
if check_ownership:
|
||||
# Verify ownership
|
||||
user = trans.get_user()
|
||||
if not user:
|
||||
error( "Must be logged in to manage histories" )
|
||||
if history.user != user:
|
||||
error( "History is not owned by current user" )
|
||||
return history
|
||||
return trans.show_ok_message( msg )
|
||||
@@ -315,5 +315,36 @@ class PageController( BaseController ):
|
||||
@web.expose
|
||||
@web.require_login("select a history from saved histories")
|
||||
def list_histories_for_selection( self, trans, **kwargs ):
|
||||
""" Returns HTML that enables a user to select one or more histories. """
|
||||
# Render the list view
|
||||
return self._history_selection_grid( trans, **kwargs )
|
||||
return self._history_selection_grid( trans, **kwargs )
|
||||
|
||||
@web.expose
|
||||
@web.require_login("get annotation table for history")
|
||||
def get_history_annotation_table( self, trans, id ):
|
||||
""" Returns HTML for an annotation table for a history. """
|
||||
|
||||
# TODO: users should be able to annotate a history if they own it, it is importable, or it is shared with them. This only
|
||||
# returns a history if a user owns it.
|
||||
history = self.get_history( trans, id, True )
|
||||
|
||||
if history:
|
||||
# TODO: Query taken from root/history; it should be moved either into history or trans object
|
||||
# so that it can reused.
|
||||
query = trans.sa_session.query( model.HistoryDatasetAssociation ) \
|
||||
.filter( model.HistoryDatasetAssociation.history == history ) \
|
||||
.options( eagerload( "children" ) ) \
|
||||
.join( "dataset" ).filter( model.Dataset.purged == False ) \
|
||||
.options( eagerload_all( "dataset.actions" ) ) \
|
||||
.order_by( model.HistoryDatasetAssociation.hid )
|
||||
# For now, do not show deleted datasets.
|
||||
show_deleted = False
|
||||
if not show_deleted:
|
||||
query = query.filter( model.HistoryDatasetAssociation.deleted == False )
|
||||
return trans.fill_template( "page/history_annotation_table.mako", history=history, datasets=query.all(), show_deleted=False )
|
||||
|
||||
@web.expose
|
||||
def get_editor_iframe( self, trans ):
|
||||
""" Returns the document for the page editor's iframe. """
|
||||
return trans.fill_template( "page/wymiframe.mako" )
|
||||
|
||||
@@ -178,8 +178,9 @@ class TagsController ( BaseController ):
|
||||
|
||||
# Create and return autocomplete data.
|
||||
ac_data = "#Header|Your Values for '%s'\n" % (tag_name)
|
||||
tag_uname = self._get_usernames_for_tag(trans.sa_session, trans.get_user(), tag, item_class, item_tag_assoc_class)[0]
|
||||
for row in result_set:
|
||||
ac_data += tag.name + ":" + row[0] + "|" + row[0] + "\n"
|
||||
ac_data += tag_uname + ":" + row[0] + "|" + row[0] + "\n"
|
||||
return ac_data
|
||||
|
||||
def _get_usernames_for_tag(self, db_session, user, tag, item_class, item_tag_assoc_class):
|
||||
|
||||
@@ -360,7 +360,7 @@ class TagsColumn( TextColumn ):
|
||||
elt_id="tagging-elt" + str( self.tag_elt_id_gen )
|
||||
div_elt = "<div id=%s></div>" % elt_id
|
||||
return div_elt + trans.fill_template( "/tagging_common.mako", trans=trans, tagged_item=item, elt_context=self.grid_name,
|
||||
elt_id = elt_id, in_form="true", input_size="20", tag_click_fn="add_tag_to_grid_filter" )
|
||||
elt_id = elt_id, in_form=True, input_size="20", tag_click_fn="add_tag_to_grid_filter" )
|
||||
def filter( self, db_session, query, column_filter ):
|
||||
""" Modify query to filter model_class by tag. Multiple filters are ANDed. """
|
||||
if column_filter == "All":
|
||||
|
||||
@@ -1,486 +1,458 @@
|
||||
/**
|
||||
* JQuery extension for tagging with autocomplete.
|
||||
* @author: Jeremy Goecks
|
||||
* @require: jquery.autocomplete plugin
|
||||
*/
|
||||
var ac_tag_area_id_gen = 1;
|
||||
* JQuery extension for tagging with autocomplete.
|
||||
* @author: Jeremy Goecks
|
||||
* @require: jquery.autocomplete plugin
|
||||
*/
|
||||
jQuery.fn.autocomplete_tagging = function(elt_id, options)
|
||||
{
|
||||
|
||||
jQuery.fn.autocomplete_tagging = function(options) {
|
||||
|
||||
//
|
||||
// Set up function defaults.
|
||||
//
|
||||
var defaults =
|
||||
{
|
||||
get_toggle_link_text_fn: function(tags)
|
||||
{
|
||||
var text = "";
|
||||
var num_tags = array_length(tags);
|
||||
if (num_tags != 0)
|
||||
text = num_tags + (num_tags != 0 ? " Tags" : " Tag");
|
||||
else
|
||||
// No tags.
|
||||
text = "Add tags";
|
||||
return text;
|
||||
},
|
||||
tag_click_fn : function (name, value) { },
|
||||
editable: true,
|
||||
input_size: 20,
|
||||
in_form: false,
|
||||
tags : {},
|
||||
use_toggle_link: true,
|
||||
item_id: "",
|
||||
add_tag_img: "",
|
||||
add_tag_img_rollover: "",
|
||||
delete_tag_img: "",
|
||||
ajax_autocomplete_tag_url: "",
|
||||
ajax_retag_url: "",
|
||||
ajax_delete_tag_url: "",
|
||||
ajax_add_tag_url: ""
|
||||
};
|
||||
|
||||
//
|
||||
// Extend object.
|
||||
//
|
||||
var settings = jQuery.extend(defaults, options);
|
||||
|
||||
//
|
||||
// Create core elements: tag area and TODO.
|
||||
//
|
||||
|
||||
// Tag area.
|
||||
var area_id = "tag-area-" + (ac_tag_area_id_gen)++;
|
||||
var tag_area = $("<div>").attr("id", area_id).addClass("tag-area");
|
||||
this.append(tag_area);
|
||||
|
||||
//
|
||||
// Returns the number of keys (elements) in an array/dictionary.
|
||||
//
|
||||
var array_length = function(an_array)
|
||||
{
|
||||
if (an_array.length)
|
||||
return an_array.length;
|
||||
|
||||
var count = 0;
|
||||
for (element in an_array)
|
||||
count++;
|
||||
return count;
|
||||
};
|
||||
|
||||
//
|
||||
// Function to build toggle link.
|
||||
//
|
||||
var build_toggle_link = function()
|
||||
{
|
||||
var link_text = settings.get_toggle_link_text_fn(settings.tags);
|
||||
var toggle_link = $("<a href='/history/tags'>").text(link_text).addClass("toggle-link");
|
||||
// Link toggles the display state of the tag area.
|
||||
toggle_link.click( function()
|
||||
{
|
||||
// Take special actions depending on whether toggle is showing or hiding link.
|
||||
var showing_tag_area = (tag_area.css("display") == "none");
|
||||
var after_toggle_fn;
|
||||
if (showing_tag_area)
|
||||
{
|
||||
after_toggle_fn = function()
|
||||
{
|
||||
// If there are no tags, go right to editing mode by generating a
|
||||
// click on the area.
|
||||
var num_tags = array_length(settings.tags);
|
||||
if (num_tags == 0)
|
||||
tag_area.click();
|
||||
};
|
||||
}
|
||||
else // Hiding area.
|
||||
{
|
||||
after_toggle_fn = function()
|
||||
{
|
||||
tag_area.blur();
|
||||
};
|
||||
}
|
||||
tag_area.slideToggle("fast", after_toggle_fn);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return toggle_link;
|
||||
};
|
||||
|
||||
// Add toggle link.
|
||||
var toggle_link = build_toggle_link();
|
||||
if (settings.use_toggle_link)
|
||||
//
|
||||
// Set up function defaults.
|
||||
//
|
||||
var defaults =
|
||||
{
|
||||
this.prepend(toggle_link);
|
||||
}
|
||||
|
||||
//
|
||||
// Function to build other elements.
|
||||
//
|
||||
get_toggle_link_text_fn: function(tags)
|
||||
{
|
||||
var text = "";
|
||||
var num_tags = array_length(tags);
|
||||
if (num_tags != 0)
|
||||
text = num_tags + (num_tags != 0 ? " Tags" : " Tag");
|
||||
else
|
||||
// No tags.
|
||||
text = "Add tags";
|
||||
return text;
|
||||
},
|
||||
tag_click_fn : function (name, value) { },
|
||||
editable: true,
|
||||
input_size: 20,
|
||||
in_form: false,
|
||||
tags : {},
|
||||
use_toggle_link: true,
|
||||
item_id: "",
|
||||
add_tag_img: "",
|
||||
add_tag_img_rollover: "",
|
||||
delete_tag_img: "",
|
||||
ajax_autocomplete_tag_url: "",
|
||||
ajax_retag_url: "",
|
||||
ajax_delete_tag_url: "",
|
||||
ajax_add_tag_url: ""
|
||||
};
|
||||
|
||||
//
|
||||
// Return a string that contains the contents of an associative array. This is
|
||||
// a debugging method.
|
||||
//
|
||||
var assoc_array_to_str = function(an_array)
|
||||
{
|
||||
// Convert associative array to simple array and then join array elements.
|
||||
var array_str_list = new Array();
|
||||
for (key in an_array)
|
||||
array_str_list[array_str_list.length] = key + "-->" + an_array[key];
|
||||
|
||||
return "{" + array_str_list.join(",") + "}"
|
||||
};
|
||||
//
|
||||
// Extend object.
|
||||
//
|
||||
var settings = jQuery.extend(defaults, options);
|
||||
|
||||
//
|
||||
// Collapse tag name + value into a single string.
|
||||
//
|
||||
var build_tag_str = function(tag_name, tag_value)
|
||||
{
|
||||
return tag_name + ( (tag_value != "" && tag_value) ? ":" + tag_value : "");
|
||||
};
|
||||
|
||||
//
|
||||
// Get tag name and value from a string.
|
||||
//
|
||||
var get_tag_name_and_value = function(tag_str)
|
||||
{
|
||||
return tag_str.split(":");
|
||||
};
|
||||
|
||||
//
|
||||
// Add "add tag" button.
|
||||
//
|
||||
var build_add_tag_button = function(tag_input_field)
|
||||
{
|
||||
var add_tag_button = $("<img src='" + settings.add_tag_img + "' rollover='" + settings.add_tag_img_rollover + "'/>").addClass("add-tag-button");
|
||||
|
||||
add_tag_button.click( function()
|
||||
//
|
||||
// Returns the number of keys (elements) in an array/dictionary.
|
||||
//
|
||||
var array_length = function(an_array)
|
||||
{
|
||||
// Hide button.
|
||||
$(this).hide();
|
||||
|
||||
// Clicking on button is the same as clicking on the tag area.
|
||||
tag_area.click();
|
||||
|
||||
return false;
|
||||
if (an_array.length)
|
||||
return an_array.length;
|
||||
|
||||
var count = 0;
|
||||
for (element in an_array)
|
||||
count++;
|
||||
return count;
|
||||
};
|
||||
|
||||
//
|
||||
// Initalize object's elements.
|
||||
//
|
||||
|
||||
// Get elements for this object.
|
||||
var this_obj = $('#' + elt_id);
|
||||
var id_parts = $(this).attr('id').split("-");
|
||||
var obj_id = id_parts[ id_parts.length-1 ];
|
||||
var tag_area = this_obj.find('#tag-area-' + obj_id);
|
||||
var toggle_link = this_obj.find('#toggle-link-' + obj_id);
|
||||
var tag_input_field = this_obj.find('#tag-input');
|
||||
var add_tag_button = this_obj.find('.add-tag-button');
|
||||
|
||||
// Initialize toggle link.
|
||||
toggle_link.click( function() {
|
||||
var id = $(this).attr('id').split('-')[2];
|
||||
|
||||
// Take special actions depending on whether toggle is showing or hiding link.
|
||||
var tag_area = $('#tag-area-' + id);
|
||||
var showing_tag_area = (tag_area.css("display") == "none");
|
||||
var after_toggle_fn;
|
||||
if (showing_tag_area)
|
||||
{
|
||||
after_toggle_fn = function()
|
||||
{
|
||||
// If there are no tags, go right to editing mode by generating a
|
||||
// click on the area.
|
||||
var num_tags = $(this).find('.tag-button').length;
|
||||
if (num_tags == 0)
|
||||
tag_area.click();
|
||||
};
|
||||
}
|
||||
else // Hiding area.
|
||||
{
|
||||
after_toggle_fn = function()
|
||||
{
|
||||
tag_area.blur();
|
||||
};
|
||||
}
|
||||
tag_area.slideToggle("fast", after_toggle_fn);
|
||||
|
||||
return $(this);
|
||||
});
|
||||
|
||||
return add_tag_button;
|
||||
};
|
||||
|
||||
//
|
||||
// Function that builds a tag button.
|
||||
//
|
||||
var build_tag_button = function(tag_str)
|
||||
{
|
||||
// Build "delete tag" image and handler.
|
||||
var delete_img = $("<img src='" + settings.delete_tag_img + "'/>").addClass("delete-tag-img");
|
||||
delete_img.mouseenter( function ()
|
||||
{
|
||||
$(this).attr("src", settings.delete_tag_img_rollover);
|
||||
});
|
||||
delete_img.mouseleave( function ()
|
||||
{
|
||||
$(this).attr("src", settings.delete_tag_img);
|
||||
});
|
||||
delete_img.click( function ()
|
||||
{
|
||||
// Tag button is image's parent.
|
||||
var tag_button = $(this).parent();
|
||||
|
||||
// Get tag name, value.
|
||||
var tag_name_elt = tag_button.find(".tag-name").eq(0);
|
||||
var tag_str = tag_name_elt.text();
|
||||
var tag_name_and_value = get_tag_name_and_value(tag_str);
|
||||
var tag_name = tag_name_and_value[0];
|
||||
var tag_value = tag_name_and_value[1];
|
||||
|
||||
var prev_button = tag_button.prev();
|
||||
tag_button.remove();
|
||||
|
||||
// Remove tag from local list for consistency.
|
||||
delete settings.tags[tag_name];
|
||||
|
||||
// Update toggle link text.
|
||||
var new_text = settings.get_toggle_link_text_fn(settings.tags);
|
||||
toggle_link.text(new_text);
|
||||
|
||||
// Delete tag.
|
||||
$.ajax({
|
||||
url: settings.ajax_delete_tag_url,
|
||||
data: { tag_name: tag_name },
|
||||
error: function()
|
||||
{
|
||||
// Failed. Roll back changes and show alert.
|
||||
settings.tags[tag_name] = tag_value;
|
||||
if (prev_button.hasClass("tag-button"))
|
||||
prev_button.after(tag_button);
|
||||
else
|
||||
tag_area.prepend(tag_button);
|
||||
var new_text = settings.get_toggle_link_text_fn(settings.tags);
|
||||
alert( "Remove tag failed" );
|
||||
|
||||
toggle_link.text(new_text);
|
||||
|
||||
// TODO: no idea why it's necessary to set this up again.
|
||||
delete_img.mouseenter( function ()
|
||||
{
|
||||
$(this).attr("src", settings.delete_tag_img_rollover);
|
||||
});
|
||||
delete_img.mouseleave( function ()
|
||||
{
|
||||
$(this).attr("src", settings.delete_tag_img);
|
||||
});
|
||||
},
|
||||
success: function() {}
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// Build tag button.
|
||||
var tag_name_elt = $("<span>").text(tag_str).addClass("tag-name");
|
||||
tag_name_elt.click( function()
|
||||
{
|
||||
tag_name_and_value = tag_str.split(":")
|
||||
settings.tag_click_fn(tag_name_and_value[0], tag_name_and_value[1]);
|
||||
return true;
|
||||
});
|
||||
|
||||
var tag_button = $("<span></span>").addClass("tag-button");
|
||||
tag_button.append(tag_name_elt);
|
||||
// Allow delete only if element is editable.
|
||||
// Initialize tag input field.
|
||||
if (settings.editable)
|
||||
tag_button.append(delete_img);
|
||||
|
||||
return tag_button;
|
||||
};
|
||||
|
||||
//
|
||||
// Build input + autocompete for tag.
|
||||
//
|
||||
var build_tag_input = function(tag_text)
|
||||
{
|
||||
// If element is in form, tag input is a textarea; otherwise element is a input type=text.
|
||||
var t;
|
||||
if (settings.in_form)
|
||||
t = $( "<textarea id='history-tag-input' rows='1' cols='" +
|
||||
settings.input_size + "' value='" + escape(tag_text) + "'></textarea>" );
|
||||
else // element not in form.
|
||||
t = $( "<input id='history-tag-input' type='text' size='" +
|
||||
settings.input_size + "' value='" + escape(tag_text) + "'></input>" );
|
||||
t.keyup( function( e )
|
||||
tag_input_field.hide();
|
||||
tag_input_field.keyup( function( e )
|
||||
{
|
||||
if ( e.keyCode == 27 )
|
||||
{
|
||||
// Escape key
|
||||
$(this).trigger( "blur" );
|
||||
} else if (
|
||||
( e.keyCode == 13 ) || // Return Key
|
||||
( e.keyCode == 188 ) || // Comma
|
||||
( e.keyCode == 32 ) // Space
|
||||
)
|
||||
{
|
||||
//
|
||||
// Check input.
|
||||
//
|
||||
|
||||
new_value = this.value;
|
||||
|
||||
// Do nothing if return key was used to autocomplete.
|
||||
if (return_key_pressed_for_autocomplete == true)
|
||||
{
|
||||
return_key_pressed_for_autocomplete = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Suppress space after a ":"
|
||||
if ( new_value.indexOf(": ", new_value.length - 2) != -1)
|
||||
{
|
||||
this.value = new_value.substring(0, new_value.length-1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove trigger keys from input.
|
||||
if ( (e.keyCode == 188) || (e.keyCode == 32) )
|
||||
new_value = new_value.substring( 0 , new_value.length - 1 );
|
||||
|
||||
// Trim whitespace.
|
||||
new_value = new_value.replace(/^\s+|\s+$/g,"");
|
||||
|
||||
// Too short?
|
||||
if (new_value.length < 2)
|
||||
return false;
|
||||
|
||||
//
|
||||
// New tag OK - apply it.
|
||||
//
|
||||
|
||||
this.value = "";
|
||||
|
||||
// Add button for tag after all other tag buttons.
|
||||
var new_tag_button = build_tag_button(new_value);
|
||||
var tag_buttons = tag_area.children(".tag-button");
|
||||
if (tag_buttons.length != 0)
|
||||
{
|
||||
var last_tag_button = tag_buttons.slice(tag_buttons.length-1);
|
||||
last_tag_button.after(new_tag_button);
|
||||
}
|
||||
else
|
||||
tag_area.prepend(new_tag_button);
|
||||
if ( e.keyCode == 27 )
|
||||
{
|
||||
// Escape key
|
||||
$(this).trigger( "blur" );
|
||||
}
|
||||
else if (
|
||||
( e.keyCode == 13 ) || // Return Key
|
||||
( e.keyCode == 188 ) || // Comma
|
||||
( e.keyCode == 32 ) // Space
|
||||
)
|
||||
{
|
||||
//
|
||||
// Check input.
|
||||
//
|
||||
|
||||
// Add tag to internal list.
|
||||
var tag_name_and_value = new_value.split(":");
|
||||
settings.tags[tag_name_and_value[0]] = tag_name_and_value[1];
|
||||
|
||||
// Update toggle link text.
|
||||
var new_text = settings.get_toggle_link_text_fn(settings.tags);
|
||||
toggle_link.text(new_text);
|
||||
new_value = this.value;
|
||||
|
||||
// Commit tag to server.
|
||||
var $this = $(this);
|
||||
$.ajax({
|
||||
url: settings.ajax_add_tag_url,
|
||||
data: { new_tag: new_value },
|
||||
error: function()
|
||||
{
|
||||
// Failed. Roll back changes and show alert.
|
||||
new_tag_button.remove();
|
||||
delete settings.tags[tag_name_and_value[0]];
|
||||
var new_text = settings.get_toggle_link_text_fn(settings.tags);
|
||||
toggle_link.text(new_text);
|
||||
alert( "Add tag failed" );
|
||||
},
|
||||
success: function()
|
||||
{
|
||||
// Flush autocomplete cache because it's not out of date.
|
||||
// TODO: in the future, we could remove the particular item
|
||||
// that was chosen from the cache rather than flush it.
|
||||
$this.flushCache();
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
// Do nothing if return key was used to autocomplete.
|
||||
if (return_key_pressed_for_autocomplete == true)
|
||||
{
|
||||
return_key_pressed_for_autocomplete = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Suppress space after a ":"
|
||||
if ( new_value.indexOf(": ", new_value.length - 2) != -1)
|
||||
{
|
||||
this.value = new_value.substring(0, new_value.length-1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove trigger keys from input.
|
||||
if ( (e.keyCode == 188) || (e.keyCode == 32) )
|
||||
new_value = new_value.substring( 0 , new_value.length - 1 );
|
||||
|
||||
// Trim whitespace.
|
||||
new_value = new_value.replace(/^\s+|\s+$/g,"");
|
||||
|
||||
// Too short?
|
||||
if (new_value.length < 2)
|
||||
return false;
|
||||
|
||||
//
|
||||
// New tag OK - apply it.
|
||||
//
|
||||
|
||||
this.value = "";
|
||||
|
||||
// Add button for tag after all other tag buttons.
|
||||
var new_tag_button = build_tag_button(new_value);
|
||||
var tag_buttons = tag_area.children(".tag-button");
|
||||
if (tag_buttons.length != 0)
|
||||
{
|
||||
var last_tag_button = tag_buttons.slice(tag_buttons.length-1);
|
||||
last_tag_button.after(new_tag_button);
|
||||
}
|
||||
else
|
||||
tag_area.prepend(new_tag_button);
|
||||
|
||||
// Add tag to internal list.
|
||||
var tag_name_and_value = new_value.split(":");
|
||||
settings.tags[tag_name_and_value[0]] = tag_name_and_value[1];
|
||||
|
||||
// Update toggle link text.
|
||||
var new_text = settings.get_toggle_link_text_fn(settings.tags);
|
||||
toggle_link.text(new_text);
|
||||
|
||||
// Commit tag to server.
|
||||
var $this = $(this);
|
||||
$.ajax({
|
||||
url: settings.ajax_add_tag_url,
|
||||
data: { new_tag: new_value },
|
||||
error: function()
|
||||
{
|
||||
// Failed. Roll back changes and show alert.
|
||||
new_tag_button.remove();
|
||||
delete settings.tags[tag_name_and_value[0]];
|
||||
var new_text = settings.get_toggle_link_text_fn(settings.tags);
|
||||
toggle_link.text(new_text);
|
||||
alert( "Add tag failed" );
|
||||
},
|
||||
success: function()
|
||||
{
|
||||
// Flush autocomplete cache because it's not out of date.
|
||||
// TODO: in the future, we could remove the particular item
|
||||
// that was chosen from the cache rather than flush it.
|
||||
$this.flushCache();
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Add autocomplete to input.
|
||||
var format_item_func = function(key, row_position, num_rows, value, search_term) {
|
||||
tag_name_and_value = value.split(":");
|
||||
return (tag_name_and_value.length == 1 ? tag_name_and_value[0] :tag_name_and_value[1]);
|
||||
//var array = new Array(key, value, row_position, num_rows,
|
||||
//search_term ); return "\"" + array.join("*") + "\"";
|
||||
var format_item_func = function(key, row_position, num_rows, value, search_term)
|
||||
{
|
||||
tag_name_and_value = value.split(":");
|
||||
return (tag_name_and_value.length == 1 ? tag_name_and_value[0] :tag_name_and_value[1]);
|
||||
//var array = new Array(key, value, row_position, num_rows,
|
||||
//search_term ); return "\"" + array.join("*") + "\"";
|
||||
}
|
||||
var autocomplete_options =
|
||||
{ selectFirst: false, formatItem : format_item_func, autoFill: false, highlight: false };
|
||||
|
||||
t.autocomplete(settings.ajax_autocomplete_tag_url, autocomplete_options);
|
||||
|
||||
t.addClass("tag-input");
|
||||
|
||||
return t;
|
||||
};
|
||||
|
||||
//
|
||||
// Build tag area.
|
||||
//
|
||||
{ selectFirst: false, formatItem : format_item_func, autoFill: false, highlight: false };
|
||||
tag_input_field.autocomplete(settings.ajax_autocomplete_tag_url, autocomplete_options);
|
||||
|
||||
// Add tag buttons for each current tag to the tag area.
|
||||
for (tag_name in settings.tags)
|
||||
|
||||
// Initialize delete tag images for current tags.
|
||||
this_obj.find('.delete-tag-img').each(function() {
|
||||
init_delete_tag_image( $(this) );
|
||||
});
|
||||
|
||||
this_obj.find('.tag-name').each( function() {
|
||||
$(this).click( function() {
|
||||
var tag_str = $(this).text();
|
||||
var tag_name_and_value = tag_str.split(":")
|
||||
settings.tag_click_fn(tag_name_and_value[0], tag_name_and_value[1]);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Initialize "add tag" button.
|
||||
add_tag_button.click( function()
|
||||
{
|
||||
var tag_value = settings.tags[tag_name];
|
||||
var tag_str = build_tag_str(tag_name, tag_value);
|
||||
var tag_button = build_tag_button(tag_str, toggle_link, settings.tags);
|
||||
tag_area.append(tag_button);
|
||||
}
|
||||
|
||||
// Add tag input field and "add tag" button.
|
||||
var tag_input_field = build_tag_input("");
|
||||
var add_tag_button = build_add_tag_button(tag_input_field);
|
||||
// Hide button.
|
||||
$(this).hide();
|
||||
|
||||
// When the tag area blurs, go to "view tag" mode.
|
||||
tag_area.blur( function(e)
|
||||
{
|
||||
num_tags = array_length(settings.tags);
|
||||
if (num_tags != 0)
|
||||
{
|
||||
add_tag_button.show();
|
||||
tag_input_field.hide();
|
||||
tag_area.removeClass("active-tag-area");
|
||||
}
|
||||
else
|
||||
{
|
||||
// No tags, so do nothing to ensure that input is still visible.
|
||||
}
|
||||
});
|
||||
|
||||
if (settings.editable)
|
||||
{
|
||||
tag_area.append(add_tag_button);
|
||||
tag_area.append(tag_input_field);
|
||||
tag_input_field.hide();
|
||||
|
||||
// On click, enable user to add tags.
|
||||
tag_area.click( function(e)
|
||||
{
|
||||
var is_active = $(this).hasClass("active-tag-area");
|
||||
// Clicking on button is the same as clicking on the tag area.
|
||||
tag_area.click();
|
||||
|
||||
// If a "delete image" object was pressed and area is inactive, do nothing.
|
||||
if ($(e.target).hasClass("delete-tag-img") && !is_active)
|
||||
return false;
|
||||
|
||||
// If a "tag name" object was pressed and area is inactive, do nothing.
|
||||
if ($(e.target).hasClass("tag-name") && !is_active)
|
||||
return false;
|
||||
|
||||
// Hide add tag button, show tag_input field. Change background to show
|
||||
// area is active.
|
||||
$(this).addClass("active-tag-area");
|
||||
add_tag_button.hide();
|
||||
tag_input_field.show();
|
||||
tag_input_field.focus();
|
||||
|
||||
// Add handler to document that will call blur when the tag area is blurred;
|
||||
// a tag area is blurred when a user clicks on an element outside the area.
|
||||
var handle_document_click = function(e)
|
||||
{
|
||||
var tag_area_id = tag_area.attr("id");
|
||||
// Blur the tag area if the element clicked on is not in the tag area.
|
||||
if (
|
||||
($(e.target).attr("id") != tag_area_id) &&
|
||||
($(e.target).parents().filter(tag_area_id).length == 0)
|
||||
)
|
||||
{
|
||||
tag_area.blur();
|
||||
$(document).unbind("click", handle_document_click);
|
||||
}
|
||||
};
|
||||
// TODO: we should attach the click handler to all frames in order to capture
|
||||
// clicks outside the frame that this element is in.
|
||||
//window.parent.document.onclick = handle_document_click;
|
||||
//var temp = $(window.parent.document.body).contents().find("iframe").html();
|
||||
//alert(temp);
|
||||
//$(document).parent().click(handle_document_click);
|
||||
$(window).click(handle_document_click);
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// If using toggle link, hide the tag area. Otherwise, if there are no tags,
|
||||
// hide the "add tags" button and show the input field.
|
||||
if (settings.use_toggle_link)
|
||||
tag_area.hide();
|
||||
else
|
||||
});
|
||||
|
||||
//
|
||||
// Set up tag area interactions; these are needed only if tags are editable.
|
||||
//
|
||||
if (settings.editable)
|
||||
{
|
||||
var num_tags = array_length(settings.tags);
|
||||
if (num_tags == 0)
|
||||
{
|
||||
add_tag_button.hide();
|
||||
tag_input_field.show();
|
||||
}
|
||||
// When the tag area blurs, go to "view tag" mode.
|
||||
tag_area.blur( function(e)
|
||||
{
|
||||
num_tags = array_length(settings.tags);
|
||||
if (num_tags != 0)
|
||||
{
|
||||
add_tag_button.show();
|
||||
tag_input_field.hide();
|
||||
tag_area.removeClass("active-tag-area");
|
||||
}
|
||||
else
|
||||
{
|
||||
// No tags, so do nothing to ensure that input is still visible.
|
||||
}
|
||||
});
|
||||
|
||||
// On click, enable user to add tags.
|
||||
tag_area.click( function(e)
|
||||
{
|
||||
var is_active = $(this).hasClass("active-tag-area");
|
||||
|
||||
// If a "delete image" object was pressed and area is inactive, do nothing.
|
||||
if ($(e.target).hasClass("delete-tag-img") && !is_active)
|
||||
return false;
|
||||
|
||||
// If a "tag name" object was pressed and area is inactive, do nothing.
|
||||
if ($(e.target).hasClass("tag-name") && !is_active)
|
||||
return false;
|
||||
|
||||
// Hide add tag button, show tag_input field. Change background to show
|
||||
// area is active.
|
||||
$(this).addClass("active-tag-area");
|
||||
add_tag_button.hide();
|
||||
tag_input_field.show();
|
||||
tag_input_field.focus();
|
||||
|
||||
// Add handler to document that will call blur when the tag area is blurred;
|
||||
// a tag area is blurred when a user clicks on an element outside the area.
|
||||
var handle_document_click = function(e)
|
||||
{
|
||||
var tag_area_id = tag_area.attr("id");
|
||||
// Blur the tag area if the element clicked on is not in the tag area.
|
||||
if (
|
||||
($(e.target).attr("id") != tag_area_id) &&
|
||||
($(e.target).parents().filter(tag_area_id).length == 0)
|
||||
)
|
||||
{
|
||||
tag_area.blur();
|
||||
$(document).unbind("click", handle_document_click);
|
||||
}
|
||||
};
|
||||
// TODO: we should attach the click handler to all frames in order to capture
|
||||
// clicks outside the frame that this element is in.
|
||||
//window.parent.document.onclick = handle_document_click;
|
||||
//var temp = $(window.parent.document.body).contents().find("iframe").html();
|
||||
//alert(temp);
|
||||
//$(document).parent().click(handle_document_click);
|
||||
$(window).click(handle_document_click);
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return this.addClass("tag-element");
|
||||
}
|
||||
|
||||
// If using toggle link, hide the tag area. Otherwise, show the tag area.
|
||||
if (settings.use_toggle_link)
|
||||
tag_area.hide();
|
||||
else
|
||||
{
|
||||
var num_tags = array_length(settings.tags);
|
||||
if (num_tags == 0)
|
||||
{
|
||||
add_tag_button.hide();
|
||||
tag_input_field.show();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize tag names.
|
||||
//$('.tag-name').
|
||||
|
||||
//
|
||||
// Helper functions.
|
||||
//
|
||||
|
||||
//
|
||||
// Collapse tag name + value into a single string.
|
||||
//
|
||||
function build_tag_str(tag_name, tag_value)
|
||||
{
|
||||
return tag_name + ( (tag_value != "" && tag_value) ? ":" + tag_value : "");
|
||||
};
|
||||
|
||||
|
||||
// Initialize a "delete tag image": when click, delete tag from UI and send delete request to server.
|
||||
function init_delete_tag_image(delete_img)
|
||||
{
|
||||
$(delete_img).mouseenter( function ()
|
||||
{
|
||||
$(this).attr("src", settings.delete_tag_img_rollover);
|
||||
});
|
||||
$(delete_img).mouseleave( function ()
|
||||
{
|
||||
$(this).attr("src", settings.delete_tag_img);
|
||||
});
|
||||
$(delete_img).click( function ()
|
||||
{
|
||||
// Tag button is image's parent.
|
||||
var tag_button = $(this).parent();
|
||||
|
||||
// Get tag name, value.
|
||||
var tag_name_elt = tag_button.find(".tag-name").eq(0);
|
||||
var tag_str = tag_name_elt.text();
|
||||
var tag_name_and_value = get_tag_name_and_value(tag_str);
|
||||
var tag_name = tag_name_and_value[0];
|
||||
var tag_value = tag_name_and_value[1];
|
||||
|
||||
var prev_button = tag_button.prev();
|
||||
tag_button.remove();
|
||||
|
||||
// Remove tag from local list for consistency.
|
||||
delete settings.tags[tag_name];
|
||||
|
||||
// Update toggle link text.
|
||||
var new_text = settings.get_toggle_link_text_fn(settings.tags);
|
||||
toggle_link.text(new_text);
|
||||
|
||||
// Delete tag.
|
||||
$.ajax({
|
||||
url: settings.ajax_delete_tag_url,
|
||||
data: { tag_name: tag_name },
|
||||
error: function()
|
||||
{
|
||||
// Failed. Roll back changes and show alert.
|
||||
settings.tags[tag_name] = tag_value;
|
||||
if (prev_button.hasClass("tag-button"))
|
||||
prev_button.after(tag_button);
|
||||
else
|
||||
tag_area.prepend(tag_button);
|
||||
var new_text = settings.get_toggle_link_text_fn(settings.tags);
|
||||
alert( "Remove tag failed" );
|
||||
|
||||
toggle_link.text(new_text);
|
||||
|
||||
// TODO: no idea why it's necessary to set this up again.
|
||||
delete_img.mouseenter( function ()
|
||||
{
|
||||
$(this).attr("src", settings.delete_tag_img_rollover);
|
||||
});
|
||||
delete_img.mouseleave( function ()
|
||||
{
|
||||
$(this).attr("src", settings.delete_tag_img);
|
||||
});
|
||||
},
|
||||
success: function() {}
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Return a string that contains the contents of an associative array. This is
|
||||
// a debugging method.
|
||||
//
|
||||
function assoc_array_to_str(an_array)
|
||||
{
|
||||
// Convert associative array to simple array and then join array elements.
|
||||
var array_str_list = new Array();
|
||||
for (key in an_array)
|
||||
array_str_list[array_str_list.length] = key + "-->" + an_array[key];
|
||||
|
||||
return "{" + array_str_list.join(",") + "}"
|
||||
};
|
||||
|
||||
//
|
||||
// Get tag name and value from a string.
|
||||
//
|
||||
function get_tag_name_and_value(tag_str)
|
||||
{
|
||||
return tag_str.split(":");
|
||||
};
|
||||
|
||||
//
|
||||
// Function that builds a tag button.
|
||||
//
|
||||
function build_tag_button(tag_str)
|
||||
{
|
||||
// Build "delete tag" image.
|
||||
var delete_img = $("<img src='" + settings.delete_tag_img + "'/>").addClass("delete-tag-img");
|
||||
init_delete_tag_image(delete_img);
|
||||
|
||||
// Build tag button.
|
||||
var tag_name_elt = $("<span>").text(tag_str).addClass("tag-name");
|
||||
tag_name_elt.click( function()
|
||||
{
|
||||
tag_name_and_value = tag_str.split(":")
|
||||
settings.tag_click_fn(tag_name_and_value[0], tag_name_and_value[1]);
|
||||
return true;
|
||||
});
|
||||
|
||||
var tag_button = $("<span></span>").addClass("tag-button");
|
||||
tag_button.append(tag_name_elt);
|
||||
// Allow delete only if element is editable.
|
||||
if (settings.editable)
|
||||
tag_button.append(delete_img);
|
||||
|
||||
return tag_button;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -370,8 +370,9 @@ jQuery.fn.wymeditor = function(options) {
|
||||
iframeHtml:"<div class='wym_iframe wym_section'>"
|
||||
+ "<iframe "
|
||||
+ "src='"
|
||||
+ WYMeditor.IFRAME_BASE_PATH
|
||||
+ "wymiframe.html' "
|
||||
+ "/page/get_editor_iframe' "
|
||||
//+ WYMeditor.IFRAME_BASE_PATH
|
||||
//+ "wymiframe.html' "
|
||||
+ "onload='this.contentWindow.parent.WYMeditor.INSTANCES["
|
||||
+ WYMeditor.INDEX + "].initIframe(this)'"
|
||||
+ "></iframe>"
|
||||
|
||||
@@ -1 +1 @@
|
||||
var ac_tag_area_id_gen=1;jQuery.fn.autocomplete_tagging=function(c){var e={get_toggle_link_text_fn:function(u){var w="";var v=o(u);if(v!=0){w=v+(v!=0?" Tags":" Tag")}else{w="Add tags"}return w},tag_click_fn:function(u,v){},editable:true,input_size:20,in_form:false,tags:{},use_toggle_link:true,item_id:"",add_tag_img:"",add_tag_img_rollover:"",delete_tag_img:"",ajax_autocomplete_tag_url:"",ajax_retag_url:"",ajax_delete_tag_url:"",ajax_add_tag_url:""};var p=jQuery.extend(e,c);var k="tag-area-"+(ac_tag_area_id_gen)++;var m=$("<div>").attr("id",k).addClass("tag-area");this.append(m);var o=function(u){if(u.length){return u.length}var v=0;for(element in u){v++}return v};var b=function(){var u=p.get_toggle_link_text_fn(p.tags);var v=$("<a href='/history/tags'>").text(u).addClass("toggle-link");v.click(function(){var w=(m.css("display")=="none");var x;if(w){x=function(){var y=o(p.tags);if(y==0){m.click()}}}else{x=function(){m.blur()}}m.slideToggle("fast",x);return false});return v};var s=b();if(p.use_toggle_link){this.prepend(s)}var t=function(u){var v=new Array();for(key in u){v[v.length]=key+"-->"+u[key]}return"{"+v.join(",")+"}"};var a=function(v,u){return v+((u!=""&&u)?":"+u:"")};var h=function(u){return u.split(":")};var i=function(u){var v=$("<img src='"+p.add_tag_img+"' rollover='"+p.add_tag_img_rollover+"'/>").addClass("add-tag-button");v.click(function(){$(this).hide();m.click();return false});return v};var j=function(u){var v=$("<img src='"+p.delete_tag_img+"'/>").addClass("delete-tag-img");v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)});v.click(function(){var D=$(this).parent();var C=D.find(".tag-name").eq(0);var B=C.text();var z=h(B);var F=z[0];var y=z[1];var E=D.prev();D.remove();delete p.tags[F];var A=p.get_toggle_link_text_fn(p.tags);s.text(A);$.ajax({url:p.ajax_delete_tag_url,data:{tag_name:F},error:function(){p.tags[F]=y;if(E.hasClass("tag-button")){E.after(D)}else{m.prepend(D)}var G=p.get_toggle_link_text_fn(p.tags);alert("Remove tag failed");s.text(G);v.mouseenter(function(){$(this).attr("src",p.delete_tag_img_rollover)});v.mouseleave(function(){$(this).attr("src",p.delete_tag_img)})},success:function(){}});return true});var w=$("<span>").text(u).addClass("tag-name");w.click(function(){tag_name_and_value=u.split(":");p.tag_click_fn(tag_name_and_value[0],tag_name_and_value[1]);return true});var x=$("<span></span>").addClass("tag-button");x.append(w);if(p.editable){x.append(v)}return x};var d=function(v){var u;if(p.in_form){u=$("<textarea id='history-tag-input' rows='1' cols='"+p.input_size+"' value='"+escape(v)+"'></textarea>")}else{u=$("<input id='history-tag-input' type='text' size='"+p.input_size+"' value='"+escape(v)+"'></input>")}u.keyup(function(D){if(D.keyCode==27){$(this).trigger("blur")}else{if((D.keyCode==13)||(D.keyCode==188)||(D.keyCode==32)){new_value=this.value;if(return_key_pressed_for_autocomplete==true){return_key_pressed_for_autocomplete=false;return false}if(new_value.indexOf(": ",new_value.length-2)!=-1){this.value=new_value.substring(0,new_value.length-1);return false}if((D.keyCode==188)||(D.keyCode==32)){new_value=new_value.substring(0,new_value.length-1)}new_value=new_value.replace(/^\s+|\s+$/g,"");if(new_value.length<2){return false}this.value="";var A=j(new_value);var z=m.children(".tag-button");if(z.length!=0){var E=z.slice(z.length-1);E.after(A)}else{m.prepend(A)}var y=new_value.split(":");p.tags[y[0]]=y[1];var B=p.get_toggle_link_text_fn(p.tags);s.text(B);var C=$(this);$.ajax({url:p.ajax_add_tag_url,data:{new_tag:new_value},error:function(){A.remove();delete p.tags[y[0]];var F=p.get_toggle_link_text_fn(p.tags);s.text(F);alert("Add tag failed")},success:function(){C.flushCache()}});return false}}});var w=function(A,z,y,C,B){tag_name_and_value=C.split(":");return(tag_name_and_value.length==1?tag_name_and_value[0]:tag_name_and_value[1])};var x={selectFirst:false,formatItem:w,autoFill:false,highlight:false};u.autocomplete(p.ajax_autocomplete_tag_url,x);u.addClass("tag-input");return u};for(tag_name in p.tags){var q=p.tags[tag_name];var l=a(tag_name,q);var g=j(l,s,p.tags);m.append(g)}var n=d("");var f=i(n);m.blur(function(u){r=o(p.tags);if(r!=0){f.show();n.hide();m.removeClass("active-tag-area")}else{}});if(p.editable){m.append(f);m.append(n);n.hide();m.click(function(w){var v=$(this).hasClass("active-tag-area");if($(w.target).hasClass("delete-tag-img")&&!v){return false}if($(w.target).hasClass("tag-name")&&!v){return false}$(this).addClass("active-tag-area");f.hide();n.show();n.focus();var u=function(y){var x=m.attr("id");if(($(y.target).attr("id")!=x)&&($(y.target).parents().filter(x).length==0)){m.blur();$(document).unbind("click",u)}};$(window).click(u);return false})}if(p.use_toggle_link){m.hide()}else{var r=o(p.tags);if(r==0){f.hide();n.show()}}return this.addClass("tag-element")};
|
||||
jQuery.fn.autocomplete_tagging=function(f,d){var g={get_toggle_link_text_fn:function(u){var w="";var v=o(u);if(v!=0){w=v+(v!=0?" Tags":" Tag")}else{w="Add tags"}return w},tag_click_fn:function(u,v){},editable:true,input_size:20,in_form:false,tags:{},use_toggle_link:true,item_id:"",add_tag_img:"",add_tag_img_rollover:"",delete_tag_img:"",ajax_autocomplete_tag_url:"",ajax_retag_url:"",ajax_delete_tag_url:"",ajax_add_tag_url:""};var q=jQuery.extend(g,d);var o=function(u){if(u.length){return u.length}var v=0;for(element in u){v++}return v};var e=$("#"+f);var m=$(this).attr("id").split("-");var a=m[m.length-1];var l=e.find("#tag-area-"+a);var s=e.find("#toggle-link-"+a);var n=e.find("#tag-input");var h=e.find(".add-tag-button");s.click(function(){var x=$(this).attr("id").split("-")[2];var v=$("#tag-area-"+x);var u=(v.css("display")=="none");var w;if(u){w=function(){var y=$(this).find(".tag-button").length;if(y==0){v.click()}}}else{w=function(){v.blur()}}v.slideToggle("fast",w);return $(this)});if(q.editable){n.hide()}n.keyup(function(z){if(z.keyCode==27){$(this).trigger("blur")}else{if((z.keyCode==13)||(z.keyCode==188)||(z.keyCode==32)){new_value=this.value;if(return_key_pressed_for_autocomplete==true){return_key_pressed_for_autocomplete=false;return false}if(new_value.indexOf(": ",new_value.length-2)!=-1){this.value=new_value.substring(0,new_value.length-1);return false}if((z.keyCode==188)||(z.keyCode==32)){new_value=new_value.substring(0,new_value.length-1)}new_value=new_value.replace(/^\s+|\s+$/g,"");if(new_value.length<2){return false}this.value="";var w=j(new_value);var v=l.children(".tag-button");if(v.length!=0){var A=v.slice(v.length-1);A.after(w)}else{l.prepend(w)}var u=new_value.split(":");q.tags[u[0]]=u[1];var x=q.get_toggle_link_text_fn(q.tags);s.text(x);var y=$(this);$.ajax({url:q.ajax_add_tag_url,data:{new_tag:new_value},error:function(){w.remove();delete q.tags[u[0]];var B=q.get_toggle_link_text_fn(q.tags);s.text(B);alert("Add tag failed")},success:function(){y.flushCache()}});return false}}});var c=function(w,v,u,y,x){tag_name_and_value=y.split(":");return(tag_name_and_value.length==1?tag_name_and_value[0]:tag_name_and_value[1])};var k={selectFirst:false,formatItem:c,autoFill:false,highlight:false};n.autocomplete(q.ajax_autocomplete_tag_url,k);e.find(".delete-tag-img").each(function(){p($(this))});e.find(".tag-name").each(function(){$(this).click(function(){var v=$(this).text();var u=v.split(":");q.tag_click_fn(u[0],u[1]);return true})});h.click(function(){$(this).hide();l.click();return false});if(q.editable){l.blur(function(u){r=o(q.tags);if(r!=0){h.show();n.hide();l.removeClass("active-tag-area")}else{}});l.click(function(w){var v=$(this).hasClass("active-tag-area");if($(w.target).hasClass("delete-tag-img")&&!v){return false}if($(w.target).hasClass("tag-name")&&!v){return false}$(this).addClass("active-tag-area");h.hide();n.show();n.focus();var u=function(y){var x=l.attr("id");if(($(y.target).attr("id")!=x)&&($(y.target).parents().filter(x).length==0)){l.blur();$(document).unbind("click",u)}};$(window).click(u);return false})}if(q.use_toggle_link){l.hide()}else{var r=o(q.tags);if(r==0){h.hide();n.show()}}function b(v,u){return v+((u!=""&&u)?":"+u:"")}function p(u){$(u).mouseenter(function(){$(this).attr("src",q.delete_tag_img_rollover)});$(u).mouseleave(function(){$(this).attr("src",q.delete_tag_img)});$(u).click(function(){var A=$(this).parent();var z=A.find(".tag-name").eq(0);var y=z.text();var w=i(y);var C=w[0];var v=w[1];var B=A.prev();A.remove();delete q.tags[C];var x=q.get_toggle_link_text_fn(q.tags);s.text(x);$.ajax({url:q.ajax_delete_tag_url,data:{tag_name:C},error:function(){q.tags[C]=v;if(B.hasClass("tag-button")){B.after(A)}else{l.prepend(A)}var D=q.get_toggle_link_text_fn(q.tags);alert("Remove tag failed");s.text(D);u.mouseenter(function(){$(this).attr("src",q.delete_tag_img_rollover)});u.mouseleave(function(){$(this).attr("src",q.delete_tag_img)})},success:function(){}});return true})}function t(u){var v=new Array();for(key in u){v[v.length]=key+"-->"+u[key]}return"{"+v.join(",")+"}"}function i(u){return u.split(":")}function j(u){var v=$("<img src='"+q.delete_tag_img+"'/>").addClass("delete-tag-img");p(v);var w=$("<span>").text(u).addClass("tag-name");w.click(function(){tag_name_and_value=u.split(":");q.tag_click_fn(tag_name_and_value[0],tag_name_and_value[1]);return true});var x=$("<span></span>").addClass("tag-button");x.append(w);if(q.editable){x.append(v)}return x}};
|
||||
@@ -45,5 +45,6 @@ WYMeditor.STRINGS['en'] = {
|
||||
// Galaxy replacements.
|
||||
Galaxy_History_Link: 'Insert Link to History',
|
||||
Galaxy_Dataset_Link: 'Insert Link to Dataset',
|
||||
Annotate_Galaxy_History: 'Annotate History',
|
||||
};
|
||||
|
||||
|
||||
@@ -54,12 +54,16 @@
|
||||
<label>
|
||||
Tags:
|
||||
</label>
|
||||
<div id="dataset-tag-area"
|
||||
style="float: left; margin-left: 1px; width: 295px; margin-right: 10px; border-style: inset; border-color: #ddd; border-width: 1px">
|
||||
<div style="float: left; width: 295px; margin-right: 10px; border-style: inset; border-width: 1px">
|
||||
<style>
|
||||
.tag-area {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
${render_tagging_element(data, "edit_attributes.mako", use_toggle_link=False, in_form=True, input_size="30")}
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
</div>
|
||||
${render_tagging_element(data, "dataset-tag-area", "edit_attributes.mako", use_toggle_link="false", in_form="true", input_size="30")}
|
||||
%endif
|
||||
%for name, spec in data.metadata.spec.items():
|
||||
%if spec.visible:
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
%else:
|
||||
%for history in histories:
|
||||
<div class="toolForm">
|
||||
<div class="toolFormTitle">History '${history.name}' shared with</div>
|
||||
<div class="toolFormTitle">History '${history.get_display_name()}' shared with</div>
|
||||
<div class="toolFormBody">
|
||||
<div class="form-row">
|
||||
<div style="float: right;">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<%def name="javascripts()">
|
||||
${parent.javascripts()}
|
||||
${h.js( "jquery", "json2", "jquery.jstore-all", "jquery.autocomplete", "autocomplete_tagging" )}
|
||||
${h.js( "galaxy.base", "jquery", "json2", "jquery.jstore-all", "jquery.autocomplete", "autocomplete_tagging" )}
|
||||
</%def>
|
||||
|
||||
<%def name="stylesheets()">
|
||||
@@ -320,14 +320,11 @@
|
||||
<p></p>
|
||||
%endif
|
||||
|
||||
<div id="history-tag-area" style="margin-bottom: 1em">
|
||||
</div>
|
||||
|
||||
<%namespace file="../tagging_common.mako" import="render_tagging_element" />
|
||||
|
||||
%if trans.get_user() is not None:
|
||||
<div id='history-tag-area' class="tag-element"></div>
|
||||
${render_tagging_element(history, "history-tag-area", "history/view.mako", use_toggle_link='false', get_toggle_link_text_fn='get_toggle_link_text', editable=user_owns_history)}
|
||||
${render_tagging_element(tagged_item=history, elt_context="history/view.mako", use_toggle_link=False, get_toggle_link_text_fn='get_toggle_link_text', editable=user_owns_history)}
|
||||
%endif
|
||||
|
||||
%if not datasets:
|
||||
|
||||
@@ -2,6 +2,175 @@
|
||||
|
||||
<%def name="title()">Galaxy :: ${page.user.username} :: ${page.title}</%def>
|
||||
|
||||
<%def name="javascripts()">
|
||||
${parent.javascripts()}
|
||||
${h.js( "jquery", "json2", "jquery.jstore-all", "jquery.autocomplete", "autocomplete_tagging" )}
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
// Load jStore for local storage
|
||||
$.extend(jQuery.jStore.defaults, { project: 'galaxy', flash: '/static/jStore.Flash.html' })
|
||||
$.jStore.load(); // Auto-select best storage
|
||||
|
||||
$.jStore.ready(function(engine) {
|
||||
engine.ready(function() {
|
||||
// Init stuff that requires the local storage to be running
|
||||
//initShowHide();
|
||||
setupHistoryItem( $("div.historyItemWrapper") );
|
||||
|
||||
// Hide all for now.
|
||||
$( "div.historyItemBody:visible" ).each( function() {
|
||||
if ( $.browser.mozilla ) {
|
||||
$(this).find( "pre.peek" ).css( "overflow", "hidden" );
|
||||
}
|
||||
$(this).slideUp( "fast" );
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
// Functionized so AJAX'd datasets can call them
|
||||
function initShowHide() {
|
||||
|
||||
// Load saved state and show as necessary
|
||||
try {
|
||||
var stored = $.jStore.store("history_expand_state");
|
||||
if (stored) {
|
||||
var st = JSON.parse(stored);
|
||||
for (var id in st) {
|
||||
$("#" + id + " div.historyItemBody" ).show();
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
// Something was wrong with values in storage, so clear storage
|
||||
$.jStore.remove("history_expand_state");
|
||||
}
|
||||
|
||||
// If Mozilla, hide scrollbars in hidden items since they cause animation bugs
|
||||
if ( $.browser.mozilla ) {
|
||||
$( "div.historyItemBody" ).each( function() {
|
||||
if ( ! $(this).is( ":visible" ) ) $(this).find( "pre.peek" ).css( "overflow", "hidden" );
|
||||
})
|
||||
}
|
||||
}
|
||||
// Add show/hide link and delete link to a history item
|
||||
function setupHistoryItem( query ) {
|
||||
query.each( function() {
|
||||
var id = this.id;
|
||||
var body = $(this).children( "div.historyItemBody" );
|
||||
var peek = body.find( "pre.peek" )
|
||||
$(this).children( ".historyItemTitleBar" ).find( ".historyItemTitle" ).wrap( "<a href='#'></a>" ).click( function() {
|
||||
if ( body.is(":visible") ) {
|
||||
// Hiding stuff here
|
||||
if ( $.browser.mozilla ) { peek.css( "overflow", "hidden" ) }
|
||||
body.slideUp( "fast" );
|
||||
|
||||
// Save setting
|
||||
var stored = $.jStore.store("history_expand_state")
|
||||
var prefs = stored ? JSON.parse(stored) : null
|
||||
if (prefs) {
|
||||
delete prefs[id];
|
||||
$.jStore.store("history_expand_state", JSON.stringify(prefs));
|
||||
}
|
||||
} else {
|
||||
// Showing stuff here
|
||||
body.slideDown( "fast", function() {
|
||||
if ( $.browser.mozilla ) { peek.css( "overflow", "auto" ); }
|
||||
});
|
||||
|
||||
// Save setting
|
||||
var stored = $.jStore.store("history_expand_state")
|
||||
var prefs = stored ? JSON.parse(stored) : new Object;
|
||||
prefs[id] = true;
|
||||
$.jStore.store("history_expand_state", JSON.stringify(prefs));
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// Delete link
|
||||
$(this).find( "div.historyItemButtons > .delete" ).each( function() {
|
||||
var data_id = this.id.split( "-" )[1];
|
||||
$(this).click( function() {
|
||||
$( '#historyItem-' + data_id + "> div.historyItemTitleBar" ).addClass( "spinner" );
|
||||
$.ajax({
|
||||
url: "${h.url_for( action='delete_async', id='XXX' )}".replace( 'XXX', data_id ),
|
||||
error: function() { alert( "Delete failed" ) },
|
||||
success: function() {
|
||||
%if show_deleted:
|
||||
var to_update = {};
|
||||
to_update[data_id] = "none";
|
||||
updater( to_update );
|
||||
%else:
|
||||
$( "#historyItem-" + data_id ).fadeOut( "fast", function() {
|
||||
$( "#historyItemContainer-" + data_id ).remove();
|
||||
if ( $( "div.historyItemContainer" ).length < 1 ) {
|
||||
$( "#emptyHistoryMessage" ).show();
|
||||
}
|
||||
});
|
||||
%endif
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
// Undelete link
|
||||
$(this).find( "a.historyItemUndelete" ).each( function() {
|
||||
var data_id = this.id.split( "-" )[1];
|
||||
$(this).click( function() {
|
||||
$( '#historyItem-' + data_id + " > div.historyItemTitleBar" ).addClass( "spinner" );
|
||||
$.ajax({
|
||||
url: "${h.url_for( controller='dataset', action='undelete_async', id='XXX' )}".replace( 'XXX', data_id ),
|
||||
error: function() { alert( "Undelete failed" ) },
|
||||
success: function() {
|
||||
var to_update = {};
|
||||
to_update[data_id] = "none";
|
||||
updater( to_update );
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
//TODO: this function is a duplicate of array_length defined in galaxy.base.js ; not sure why it needs to be redefined here (due to streaming?).
|
||||
// Returns the number of keys (elements) in an array/dictionary.
|
||||
var array_length = function(an_array)
|
||||
{
|
||||
if (an_array.length)
|
||||
return an_array.length;
|
||||
|
||||
var count = 0;
|
||||
for (element in an_array)
|
||||
count++;
|
||||
return count;
|
||||
};
|
||||
|
||||
//
|
||||
// Function provides text for tagging toggle link.
|
||||
//
|
||||
var get_toggle_link_text = function(tags)
|
||||
{
|
||||
var text = "";
|
||||
var num_tags = array_length(tags);
|
||||
if (num_tags != 0)
|
||||
{
|
||||
text = num_tags + (num_tags != 1 ? " Tags" : " Tag");
|
||||
}
|
||||
else
|
||||
{
|
||||
// No tags.
|
||||
text = "Add tags to history";
|
||||
}
|
||||
return text;
|
||||
};
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="stylesheets()">
|
||||
${parent.stylesheets()}
|
||||
${h.css( "base", "history", "autocomplete_tagging" )}
|
||||
</%def>
|
||||
|
||||
<%def name="init()">
|
||||
<%
|
||||
self.has_left_panel=False
|
||||
|
||||
+83
-22
@@ -21,31 +21,37 @@
|
||||
</%def>
|
||||
|
||||
<%def name="javascripts()">
|
||||
|
||||
${parent.javascripts()}
|
||||
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.event.drag.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.event.drop.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.event.hover.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.form.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.jstore-all.js')}"> </script>
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/json2.js')}"> </script>
|
||||
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/galaxy.base.js')}"> </script>
|
||||
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.wymeditor.js')}"> </script>
|
||||
|
||||
<script type='text/javascript' src="${h.url_for('/static/scripts/jquery.autocomplete.js')}"> </script>
|
||||
|
||||
${h.js( "jquery", "jquery.event.drag", "jquery.event.drop", "jquery.event.hover", "jquery.form", "jquery.jstore-all", "json2",
|
||||
"galaxy.base", "jquery.wymeditor", "jquery.autocomplete", "autocomplete_tagging")}
|
||||
<script type="text/javascript">
|
||||
|
||||
// Useful Galaxy stuff.
|
||||
var Galaxy =
|
||||
{
|
||||
DIALOG_HISTORY_LINK : "history_link",
|
||||
DIALOG_HISTORY_ANNOTATE : "history_annotate",
|
||||
};
|
||||
|
||||
|
||||
// Initialize Galaxy elements.
|
||||
function init_galaxy_elts(wym)
|
||||
{
|
||||
// Set up events to make annotation easy.
|
||||
$('.annotation', wym._doc.body).each( function()
|
||||
{
|
||||
$(this).click( function() {
|
||||
// Works in Safari, not in Firefox.
|
||||
var range = wym._doc.createRange();
|
||||
range.selectNodeContents( this );
|
||||
var selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
var t = "";
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
## Completely replace WYM's dialog handling
|
||||
WYMeditor.editor.prototype.dialog = function( dialogType, dialogFeatures, bodyHtml ) {
|
||||
|
||||
@@ -193,7 +199,7 @@
|
||||
);
|
||||
}
|
||||
|
||||
// HISTORY DIALOG
|
||||
// INSERT HISTORY LINK DIALOG
|
||||
if ( dialogType == Galaxy.DIALOG_HISTORY_LINK ) {
|
||||
$.ajax(
|
||||
{
|
||||
@@ -244,7 +250,7 @@
|
||||
// Get history name.
|
||||
$.get( '${h.url_for( controller='history', action='get_name_async' )}?id=' + item_id, function( history_name ) {
|
||||
var href = '${h.url_for( controller='history', action='view' )}?id=' + item_id;
|
||||
wym.insert("<a href='" + href + "'>History '" + history_name + "'</a>nbsp;");
|
||||
wym.insert("<a href='" + href + "'>History '" + history_name + "'</a>");
|
||||
});
|
||||
}
|
||||
else
|
||||
@@ -268,6 +274,54 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
// ANNOTATE HISTORY DIALOG
|
||||
if ( dialogType == Galaxy.DIALOG_ANNOTATE_HISTORY ) {
|
||||
$.ajax(
|
||||
{
|
||||
url: "${h.url_for( action='list_histories_for_selection' )}",
|
||||
data: {},
|
||||
error: function() { alert( "Grid refresh failed" ) },
|
||||
success: function(table_html)
|
||||
{
|
||||
show_modal(
|
||||
"Insert Link to History",
|
||||
table_html,
|
||||
{
|
||||
"Annotate": function()
|
||||
{
|
||||
// Insert links to history for each checked item.
|
||||
var item_ids = new Array();
|
||||
$('input[name=id]:checked').each(function() {
|
||||
var item_id = $(this).val();
|
||||
|
||||
// Get annotation table for history.
|
||||
$.ajax(
|
||||
{
|
||||
url: "${h.url_for( action='get_history_annotation_table' )}",
|
||||
data: { id : item_id },
|
||||
error: function() { alert( "Grid refresh failed" ) },
|
||||
success: function(result)
|
||||
{
|
||||
// Insert into document.
|
||||
wym.insert(result);
|
||||
|
||||
init_galaxy_elts(wym);
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
hide_modal();
|
||||
},
|
||||
"Cancel": function()
|
||||
{
|
||||
hide_modal();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -313,7 +367,8 @@
|
||||
{'name': 'Unlink', 'title': 'Unlink', 'css': 'wym_tools_unlink'},
|
||||
{'name': 'InsertImage', 'title': 'Image', 'css': 'wym_tools_image'},
|
||||
{'name': 'InsertTable', 'title': 'Table', 'css': 'wym_tools_table'},
|
||||
{'name': 'Insert Galaxy History Link', 'title' : 'Galaxy_History_Link', 'css' : 'galaxy_tools_insert_history_link'}
|
||||
{'name': 'Insert Galaxy History Link', 'title' : 'Galaxy_History_Link', 'css' : 'galaxy_tools_insert_history_link'},
|
||||
{'name': 'Annonate Galaxy History', 'title' : 'Annotate_Galaxy_History', 'css' : 'galaxy_tools_annotate_history'},
|
||||
]
|
||||
});
|
||||
## Get the editor object
|
||||
@@ -367,13 +422,19 @@
|
||||
$('.galaxy_tools_insert_history_link').children().click( function() {
|
||||
editor.dialog(Galaxy.DIALOG_HISTORY_LINK);
|
||||
});
|
||||
// Initialize 'Annotate history' button.
|
||||
$('.galaxy_tools_annotate_history').children().click( function() {
|
||||
editor.dialog(Galaxy.ANNOTATE_HISTORY);
|
||||
});
|
||||
// Initialize galaxy elements.
|
||||
//init_galaxy_elts(editor);
|
||||
});
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="stylesheets()">
|
||||
${parent.stylesheets()}
|
||||
${h.css( "autocomplete_tagging" )}
|
||||
${h.css( "base", "history", "autocomplete_tagging" )}
|
||||
</%def>
|
||||
|
||||
<%def name="center_panel()">
|
||||
@@ -384,7 +445,7 @@
|
||||
<a id="close-button" class="panel-header-button">Close</a>
|
||||
</div>
|
||||
<div class="unified-panel-header-inner">
|
||||
Page editor
|
||||
Page Editor <span style="font-weight: normal">| Title : ${page.title}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<%namespace file="../tagging_common.mako" import="render_tagging_element_html" />
|
||||
<%namespace file="../root/history_common.mako" import="render_dataset" />
|
||||
|
||||
<table>
|
||||
## Table header.
|
||||
<tr>
|
||||
<th colspan='2'>History '${history.get_display_name()}'</th>
|
||||
</tr>
|
||||
<tr>
|
||||
## Status messages and tags.
|
||||
<th colspan='2'>
|
||||
%if history.deleted:
|
||||
<div class="warningmessagesmall">
|
||||
${_('This is a deleted history.')}
|
||||
</div>
|
||||
%endif
|
||||
%if trans.get_user() is not None:
|
||||
Tags: ${render_tagging_element_html( tagged_item=history, editable=False, use_toggle_link=False )}
|
||||
%endif
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan="2">Description of History:
|
||||
<ol>
|
||||
<li>What was the motivation for this history?
|
||||
<li>What is the outcome of this history?
|
||||
<li>What are unresolved questions from this history?
|
||||
<li>What new questions arise from this history?
|
||||
</ol>
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
## Table body. For each dataset, there is an area to annotate the dataset.
|
||||
%if not datasets:
|
||||
<tr>
|
||||
<td>
|
||||
<div class="infomessagesmall" id="emptyHistoryMessage">
|
||||
${_("Your history is empty. Click 'Get Data' on the left pane to start")}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
%else:
|
||||
## Render requested datasets.
|
||||
%for data in datasets:
|
||||
%if data.visible:
|
||||
<tr>
|
||||
<td valign="top"><span class="annotation">Describe this step: why was it done? what data does it produce?</span></td>
|
||||
##<td valign="top" class="annotation">Describe this step: why was it done? what data does it produce?</td>
|
||||
<td>
|
||||
<div class="historyItemContainer" id="historyItemContainer-${data.id}">
|
||||
${render_dataset( data, data.hid, show_deleted_on_refresh = show_deleted, user_owns_dataset = False )}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
%endfor
|
||||
%endif
|
||||
</table>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<!--
|
||||
* WYMeditor : what you see is What You Mean web-based editor
|
||||
* Copyright (c) 2005 - 2009 Jean-Francois Hovinne, http://www.wymeditor.org/
|
||||
* Dual licensed under the MIT (MIT-license.txt)
|
||||
* and GPL (GPL-license.txt) licenses.
|
||||
*
|
||||
* For further information visit:
|
||||
* http://www.wymeditor.org/
|
||||
*
|
||||
* File Name:
|
||||
* wymiframe.html
|
||||
* Iframe used by designMode.
|
||||
* See the documentation for more info.
|
||||
*
|
||||
* File Authors:
|
||||
* Jean-Francois Hovinne (jf.hovinne a-t wymeditor dotorg)
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>WYMeditor iframe</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="/static/wymeditor/iframe/galaxy/wymiframe.css" />
|
||||
${h.css("base", "history", "autocomplete_tagging")}
|
||||
</head>
|
||||
<body class="wym_iframe text-content"></body>
|
||||
</html>
|
||||
@@ -77,6 +77,7 @@ $(function() {
|
||||
<% updateable = [data for data in reversed( datasets ) if data.visible and data.state not in [ "deleted", "empty", "error", "ok" ]] %>
|
||||
${ ",".join( map(lambda data: "\"%s\" : \"%s\"" % (data.id, data.state), updateable) ) }
|
||||
});
|
||||
|
||||
// Navigate to a dataset.
|
||||
%if hda_id:
|
||||
self.location = "#${hda_id}";
|
||||
@@ -317,15 +318,16 @@ var updater_callback = function ( tracked_datasets ) {
|
||||
<p></p>
|
||||
%endif
|
||||
|
||||
<div id="history-tag-area" style="margin-bottom: 1em">
|
||||
</div>
|
||||
|
||||
<%namespace file="../tagging_common.mako" import="render_tagging_element" />
|
||||
<%namespace file="history_common.mako" import="render_dataset" />
|
||||
|
||||
%if trans.get_user() is not None:
|
||||
<div id='history-tag-area' class="tag-element"></div>
|
||||
${render_tagging_element( tagged_item=history, elt_id="history-tag-area", elt_context="history.mako", get_toggle_link_text_fn='get_toggle_link_text' )}
|
||||
<style>
|
||||
.tag-element {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
</style>
|
||||
${render_tagging_element( tagged_item=history, elt_context='history.mako', get_toggle_link_text_fn='get_toggle_link_text' )}
|
||||
%endif
|
||||
|
||||
%if not datasets:
|
||||
|
||||
@@ -1,10 +1,76 @@
|
||||
<%!
|
||||
from cgi import escape
|
||||
from galaxy.web.framework.helpers import iff
|
||||
%>
|
||||
## Render a tagging element if there is a tagged_item.
|
||||
%if tagged_item is not None and elt_id is not None:
|
||||
${render_tagging_element(tagged_item=tagged_item, elt_id=elt_id, elt_context=elt_context, in_form=in_form, input_size=input_size, tag_click_fn=tag_click_fn)}
|
||||
%if tagged_item is not None:
|
||||
${render_tagging_element(tagged_item=tagged_item, elt_context=elt_context, in_form=in_form, input_size=input_size, tag_click_fn=tag_click_fn)}
|
||||
%endif
|
||||
|
||||
## Render HTML for a tagging element.
|
||||
<%def name="render_tagging_element_html(tagged_item=None, editable=True, use_toggle_link=True, input_size='15', in_form=False)">
|
||||
## Useful attributes.
|
||||
<%
|
||||
tagged_item_id = str( trans.security.encode_id (tagged_item.id) )
|
||||
elt_id = "tag-element-" + tagged_item_id
|
||||
%>
|
||||
<div id="${elt_id}" class="tag-element">
|
||||
%if use_toggle_link:
|
||||
<a id="toggle-link-${tagged_item_id}" class="toggle-link" href="#">${len(tagged_item.tags)} Tags</a>
|
||||
%endif
|
||||
<div id="tag-area-${tagged_item_id}" class="tag-area">
|
||||
|
||||
## Build buttons for current tags.
|
||||
%for tag in tagged_item.tags:
|
||||
<%
|
||||
tag_name = tag.user_tname
|
||||
tag_value = None
|
||||
if tag.value is not None:
|
||||
tag_value = tag.user_value
|
||||
## Convert tag name, value to unicode.
|
||||
if isinstance( tag_name, str ):
|
||||
tag_name = unicode( escape( tag_name ), 'utf-8' )
|
||||
if tag_value:
|
||||
tag_value = unicode( escape( tag_value ), 'utf-8' )
|
||||
tag_str = tag_name + ":" + tag_value
|
||||
else:
|
||||
tag_str = tag_name
|
||||
%>
|
||||
<span class="tag-button">
|
||||
<span class="tag-name">${tag_str}</span>
|
||||
%if editable:
|
||||
<img class="delete-tag-img" src="${h.url_for('/static/images/delete_tag_icon_gray.png')}"/>
|
||||
%endif
|
||||
</span>
|
||||
%endfor
|
||||
|
||||
## Add tag input field. If element is in form, tag input is a textarea; otherwise element is a input type=text.
|
||||
%if editable:
|
||||
%if in_form:
|
||||
<textarea id='tag-input' class="tag-input" rows='1' cols='${input_size}'></textarea>
|
||||
%else:
|
||||
<input id='tag-input' class="tag-input" type='text' size='${input_size}'></input>
|
||||
%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"/>
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
|
||||
## Render the tags 'tags' as an autocomplete element.
|
||||
<%def name="render_tagging_element(tagged_item, elt_id, elt_context, 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_tagging_element(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)">
|
||||
## Useful attributes.
|
||||
<%
|
||||
tagged_item_id = str( trans.security.encode_id (tagged_item.id) )
|
||||
elt_id = "tag-element-" + tagged_item_id
|
||||
%>
|
||||
|
||||
## Build HTML.
|
||||
${self.render_tagging_element_html(tagged_item, editable, use_toggle_link, input_size, in_form)}
|
||||
|
||||
## Build script that augments tags using progressive javascript.
|
||||
<script type="text/javascript">
|
||||
//
|
||||
// Set up autocomplete tagger.
|
||||
@@ -70,22 +136,32 @@
|
||||
var options =
|
||||
{
|
||||
tags : ${h.to_json_string(tag_names_and_values)},
|
||||
editable : ${str(editable).lower()},
|
||||
editable : ${iff( editable, 'true', 'false' )},
|
||||
get_toggle_link_text_fn: ${get_toggle_link_text_fn},
|
||||
tag_click_fn: ${tag_click_fn},
|
||||
<% tagged_item_id = trans.security.encode_id(tagged_item.id) %>
|
||||
ajax_autocomplete_tag_url: "${h.url_for( controller='tag', action='tag_autocomplete_data', id=tagged_item_id, item_class=tagged_item.__class__.__name__ )}",
|
||||
ajax_add_tag_url: "${h.url_for( controller='tag', action='add_tag_async', id=tagged_item_id, item_class=tagged_item.__class__.__name__, context=elt_context )}",
|
||||
ajax_delete_tag_url: "${h.url_for( controller='tag', action='remove_tag_async', id=tagged_item_id, item_class=tagged_item.__class__.__name__, context=elt_context )}",
|
||||
delete_tag_img: "${h.url_for('/static/images/delete_tag_icon_gray.png')}",
|
||||
delete_tag_img_rollover: "${h.url_for('/static/images/delete_tag_icon_white.png')}",
|
||||
add_tag_img: "${h.url_for('/static/images/add_icon.png')}",
|
||||
add_tag_img_rollover: "${h.url_for('/static/images/add_icon_dark.png')}",
|
||||
input_size: ${input_size},
|
||||
in_form: ${in_form},
|
||||
use_toggle_link: ${use_toggle_link}
|
||||
use_toggle_link: ${iff( use_toggle_link, 'true', 'false' )},
|
||||
};
|
||||
|
||||
$("#${elt_id}").autocomplete_tagging(options)
|
||||
$("#${elt_id}").autocomplete_tagging('${elt_id}', options);
|
||||
</script>
|
||||
|
||||
## Use style to hide/display the tag area.
|
||||
<style>
|
||||
.tag-area {
|
||||
display: ${iff( use_toggle_link, "none", "block" )};
|
||||
}
|
||||
</style>
|
||||
|
||||
<noscript>
|
||||
<style>
|
||||
.tag-area {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</noscript>
|
||||
</%def>
|
||||
Reference in New Issue
Block a user