galaxy-commits
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
August 2013
- 1 participants
- 149 discussions
commit/galaxy-central: fubar: Patch for missing options in FromDataTableOutputActionOption when these are used in an output filter
by commits-noreply@bitbucket.org 09 Aug '13
by commits-noreply@bitbucket.org 09 Aug '13
09 Aug '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/009088d5e76f/
Changeset: 009088d5e76f
User: fubar
Date: 2013-08-10 03:28:28
Summary: Patch for missing options in FromDataTableOutputActionOption when these are used in an output filter
Affected #: 1 file
diff -r 31d2d58ebf102f87c21f170b16452d06fd510a14 -r 009088d5e76fb00794da78dc0ee3cdaa8524b7d8 lib/galaxy/tools/parameters/output.py
--- a/lib/galaxy/tools/parameters/output.py
+++ b/lib/galaxy/tools/parameters/output.py
@@ -217,7 +217,10 @@
else:
self.missing_tool_data_table_name = self.name
def get_value( self, other_values ):
- options = self.options
+ try:
+ options = self.options
+ except:
+ options = []
for filter in self.filters:
options = filter.filter_options( options, other_values )
try:
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jgoecks: Unify Trackster tool objects with generic tool objects. This unification enhances the generic tool framework and simplifies the trackster tool framework. Also, backbone-ify Trackster tool handling.
by commits-noreply@bitbucket.org 08 Aug '13
by commits-noreply@bitbucket.org 08 Aug '13
08 Aug '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/31d2d58ebf10/
Changeset: 31d2d58ebf10
User: jgoecks
Date: 2013-08-08 23:05:51
Summary: Unify Trackster tool objects with generic tool objects. This unification enhances the generic tool framework and simplifies the trackster tool framework. Also, backbone-ify Trackster tool handling.
Affected #: 3 files
diff -r 8c3b553871bbcce9170d235f5188b9803094ad3b -r 31d2d58ebf102f87c21f170b16452d06fd510a14 lib/galaxy/visualization/genome/visual_analytics.py
--- a/lib/galaxy/visualization/genome/visual_analytics.py
+++ b/lib/galaxy/visualization/genome/visual_analytics.py
@@ -38,6 +38,8 @@
def get_tool_def( trans, hda ):
""" Returns definition of an interactive tool for an HDA. """
+
+ #FIXME: use tools.to_dict rather than custom code here.
job = get_dataset_job( hda )
# TODO: could use this assertion to provide more information.
@@ -71,10 +73,10 @@
tool_params.append( param_dict )
elif type( input ) == SelectToolParameter and type( input.options ) != DynamicOptions:
tool_params.append( { 'name' : name, 'label' : input.label, 'type' : 'select', \
- 'value' : tool_param_values.get( name, None ), \
+ 'value' : tool_param_values.get( name, '' ), \
'html' : urllib.quote( input.get_html() ) } )
# If tool has parameters that can be interactively modified, return tool.
if len( tool_params ) != 0:
- return { 'id': tool.id, 'name' : tool.name, 'params' : tool_params }
+ return { 'id': tool.id, 'name' : tool.name, 'inputs' : tool_params }
return None
\ No newline at end of file
diff -r 8c3b553871bbcce9170d235f5188b9803094ad3b -r 31d2d58ebf102f87c21f170b16452d06fd510a14 static/scripts/mvc/tools.js
--- a/static/scripts/mvc/tools.js
+++ b/static/scripts/mvc/tools.js
@@ -41,6 +41,7 @@
label: null,
type: null,
value: null,
+ html: null,
num_samples: 5
},
@@ -70,7 +71,51 @@
}
return samples;
- }
+ },
+
+ set_value: function(value) {
+ this.set('value', value || '');
+ }
+},
+{
+ /**
+ * Dictionary mapping parameter type strings to parameter classes.
+ */
+ TYPE_DICT: {
+ 'number': IntegerToolParameter
+ },
+
+ /**
+ * Create new parameter from a dictionary.
+ */
+ create: function(options) {
+ var param_class = ToolParameter.TYPE_DICT[options.type] || ToolParameter;
+ return new param_class(options);
+ }
+});
+
+/**
+ * A number tool parameter.
+ */
+var IntegerToolParameter = ToolParameter.extend({
+ defaults: _.extend({}, ToolParameter.prototype.defaults, {
+ min: null,
+ max: null
+ }),
+
+ initialize: function() {
+ ToolParameter.prototype.initialize.call(this);
+ if (this.attributes.min) {
+ this.attributes.min = parseInt(this.attributes.min, 10);
+ }
+ if (this.attributes.max) {
+ this.attributes.max = parseInt(this.attributes.max, 10);
+ }
+ },
+
+ set_value: function(value) {
+ this.set('value', parseInt(value, 10))
+ }
});
/**
@@ -79,22 +124,22 @@
var Tool = BaseModel.extend({
// Default attributes.
defaults: {
+ id: null,
+ name: null,
description: null,
target: null,
inputs: []
},
-
- relations: [
- {
- type: Backbone.HasMany,
- key: 'inputs',
- relatedModel: ToolParameter,
- reverseRelation: {
- key: 'tool',
- includeInJSON: false
- }
- }
- ],
+
+ initialize: function(options) {
+ // Unpack parameters manually so that different parameter types can be created.
+ this.attributes.inputs = new Backbone.Collection( _.map(options.inputs, function(param_dict) {
+ // FIXME: it is still useful to be able to save/restore tool state?
+ // Update parameter value from tool state dict.
+ //param_dict.value = tool_state_dict[ param_dict[name] ] || param_dict.value;
+ return ToolParameter.create(param_dict);
+ }));
+ },
urlRoot: galaxy_paths.get('tool_url'),
@@ -209,6 +254,13 @@
});
/**
+ * Tool view.
+ */
+var ToolView = Backbone.View.extend({
+
+});
+
+/**
* Wrap collection of tools for fast access/manipulation.
*/
var ToolCollection = Backbone.Collection.extend({
@@ -697,6 +749,8 @@
// Exports
return {
+ ToolParameter: ToolParameter,
+ IntegerToolParameter: IntegerToolParameter,
Tool: Tool,
ToolSearch: ToolSearch,
ToolPanel: ToolPanel,
diff -r 8c3b553871bbcce9170d235f5188b9803094ad3b -r 31d2d58ebf102f87c21f170b16452d06fd510a14 static/scripts/viz/trackster/tracks.js
--- a/static/scripts/viz/trackster/tracks.js
+++ b/static/scripts/viz/trackster/tracks.js
@@ -1,7 +1,7 @@
define( ["libs/underscore", "viz/visualization", "viz/trackster/util",
- "viz/trackster/slotting", "viz/trackster/painters", "mvc/data",
- "viz/trackster/filters" ],
- function( _, visualization, util, slotting, painters, data, filters_mod ) {
+ "viz/trackster/slotting", "viz/trackster/painters", "viz/trackster/filters",
+ "mvc/data", "mvc/tools" ],
+ function(_, visualization, util, slotting, painters, filters_mod, data, tools_mod) {
var extend = _.extend;
@@ -1622,89 +1622,103 @@
/**
* Encapsulation of a tool that users can apply to tracks/datasets.
*/
-var Tool = function(track, tool_dict, tool_state_dict) {
- //
- // Unpack tool information from dictionary.
- //
- this.track = track;
- this.id = tool_dict.id;
- this.name = tool_dict.name;
- this.params = [];
- var params_dict = tool_dict.params;
- for (var i = 0; i < params_dict.length; i++) {
- // FIXME: use dict for creating parameters.
- var param_dict = params_dict[i],
- name = param_dict.name,
- label = param_dict.label,
- html = unescape(param_dict.html),
- value = param_dict.value,
- type = param_dict.type;
- if (type === "number") {
- this.params.push(
- new NumberParameter(name, label, html,
- (name in tool_state_dict ? tool_state_dict[name] : value),
- param_dict.min, param_dict.max)
- );
+var TracksterTool = Backbone.RelationalModel.extend({
+ defaults: {
+ track: null,
+ tool: null,
+ },
+
+ relations: [
+ {
+ type: Backbone.HasOne,
+ key: 'tool',
+ relatedModel: tools_mod.Tool
}
- else if (type === "select") {
- this.params.push(
- new ToolParameter(name, label, html,
- (name in tool_state_dict ? tool_state_dict[name] : value))
- );
- }
- else {
- console.log("WARNING: unrecognized tool parameter type:", name, type);
- }
- }
-
- //
- // Create div elt for tool UI.
- //
- this.parent_div = $("<div/>").addClass("dynamic-tool").hide();
- // Disable dragging, clicking, double clicking on div so that actions on slider do not impact viz.
- this.parent_div.bind("drag", function(e) {
- e.stopPropagation();
- }).click(function(e) {
- e.stopPropagation();
- }).bind("dblclick", function(e) {
- e.stopPropagation();
- });
- var name_div = $("<div class='tool-name'>").appendTo(this.parent_div).text(this.name);
- var tool_params = this.params;
- var tool = this;
- $.each(this.params, function(index, param) {
- var param_div = $("<div>").addClass("param-row").appendTo(tool.parent_div);
+ ]
+
+});
+
+/**
+ * View renders tool parameter HTML and updates parameter value as it is changed in the HTML.
+ */
+ var ToolParameterView = Backbone.View.extend({
+
+ events: {
+ 'change input': 'update_value'
+ },
+
+ render: function() {
+ var param_div = this.$el.addClass("param-row"),
+ param = this.model;
+
// Param label.
- var label_div = $("<div>").addClass("param-label").text(param.label).appendTo(param_div);
+ var label_div = $("<div>").addClass("param-label").text(param.get('label')).appendTo(param_div);
// Param HTML.
- var html_div = $("<div/>").addClass("param-input").html(param.html).appendTo(param_div);
+ var html_div = $("<div/>").addClass("param-input").html(param.get('html')).appendTo(param_div);
// Set initial value.
- html_div.find(":input").val(param.value);
+ html_div.find(":input").val(param.get('value'));
// Add to clear floating layout.
$("<div style='clear: both;'/>").appendTo(param_div);
- });
-
- // Highlight value for inputs for easy replacement.
- this.parent_div.find("input").click(function() { $(this).select(); });
-
- // Add buttons for running on dataset, region.
- var run_tool_row = $("<div>").addClass("param-row").appendTo(this.parent_div);
- var run_on_dataset_button = $("<input type='submit'>").attr("value", "Run on complete dataset").appendTo(run_tool_row);
- var run_on_region_button = $("<input type='submit'>").attr("value", "Run on visible region").css("margin-left", "3em").appendTo(run_tool_row);
- run_on_region_button.click( function() {
- // Run tool to create new track.
- tool.run_on_region();
- });
- run_on_dataset_button.click( function() {
- tool.run_on_dataset();
- });
-
- if ('visible' in tool_state_dict && tool_state_dict.visible) {
- this.parent_div.show();
+ },
+
+ update_value: function(update_event) {
+ this.model.set_value($(update_event.target).val());
}
-};
-extend(Tool.prototype, {
+ });
+
+/**
+ * View for TracksterTool.
+ */
+var TracksterToolView = Backbone.View.extend({
+
+ /**
+ * Render tool UI.
+ */
+ render: function() {
+ var self = this;
+ tool = this.model.get('tool'),
+ parent_div = this.$el.addClass("dynamic-tool").hide();
+
+ // Prevent div events from propogating to other elements.
+ parent_div.bind("drag", function(e) {
+ e.stopPropagation();
+ }).click(function(e) {
+ e.stopPropagation();
+ }).bind("dblclick", function(e) {
+ e.stopPropagation();
+ }).keydown(function(e) { e.stopPropagation(); });
+
+ // Add name, inputs.
+ var name_div = $("<div class='tool-name'>").appendTo(parent_div).text(tool.get('name'));
+ tool.get('inputs').each(function(param) {
+ var param_view = new ToolParameterView({ model: param });
+ param_view.render();
+ parent_div.append(param_view.$el);
+ });
+
+ // Highlight value for inputs for easy replacement.
+ parent_div.find("input").click(function() { $(this).select(); });
+
+ // Add buttons for running on dataset, region.
+ var run_tool_row = $("<div>").addClass("param-row").appendTo(parent_div);
+ var run_on_dataset_button = $("<input type='submit'>").attr("value", "Run on complete dataset").appendTo(run_tool_row);
+ var run_on_region_button = $("<input type='submit'>").attr("value", "Run on visible region").css("margin-left", "3em").appendTo(run_tool_row);
+ run_on_region_button.click( function() {
+ // Run tool to create new track.
+ self.run_on_region();
+ });
+ run_on_dataset_button.click( function() {
+ self.run_on_dataset();
+ });
+
+ /*
+ if ('visible' in tool_state_dict && tool_state_dict.visible) {
+ this.parent_div.show();
+ }
+ */
+ },
+
/**
* Update tool parameters.
*/
@@ -1713,68 +1727,42 @@
this.params[i].update_value();
}
},
+
/**
* Returns a dict with tool state information.
*/
state_dict: function() {
// Save parameter values.
- var tool_state = {};
- for (var i = 0; i < this.params.length; i++) {
- tool_state[this.params[i].name] = this.params[i].value;
- }
+ var tool_state = this.model.get('tool').get_param_values_dict();
// Save visibility.
tool_state.visible = this.parent_div.is(":visible");
return tool_state;
},
- /**
- * Returns dictionary of parameter name-values.
- */
- get_param_values_dict: function() {
- var param_dict = {};
- this.parent_div.find(":input").each(function() {
- var name = $(this).attr("name"), value = $(this).val();
- param_dict[name] = value;
- });
- return param_dict;
- },
- /**
- * Returns array of parameter values.
- */
- get_param_values: function() {
- var param_values = [];
- this.parent_div.find(":input").each(function() {
- // Only include inputs with names; this excludes Run button.
- var name = $(this).attr("name"), value = $(this).val();
- if (name) {
- param_values[param_values.length] = value;
- }
- });
- return param_values;
- },
+
/**
* Run tool on dataset. Output is placed in dataset's history and no changes to viz are made.
*/
run_on_dataset: function() {
- var tool = this;
- tool.run(
- // URL params.
- {
- target_dataset_id: this.track.dataset.id,
- action: 'rerun',
- tool_id: tool.id
- },
- null,
- // Success callback.
- function(track_data) {
- show_modal(tool.name + " is Running",
- tool.name + " is running on the complete dataset. Tool outputs are in dataset's history.",
- { "Close" : hide_modal } );
- }
- );
-
+ var tool = this.model.get('tool');
+ this.run(
+ // URL params.
+ {
+ target_dataset_id: this.model.get('track').dataset.id,
+ action: 'rerun',
+ tool_id: tool.id
+ },
+ null,
+ // Success callback.
+ function(track_data) {
+ show_modal(tool.get('name') + " is Running",
+ tool.get('name') + " is running on the complete dataset. Tool outputs are in dataset's history.",
+ { "Close" : hide_modal } );
+ }
+ );
},
+
/**
* Run dataset on visible region. This creates a new track and sets the track's contents
* to the tool's output.
@@ -1783,21 +1771,23 @@
//
// Create track for tool's output immediately to provide user feedback.
//
- var region = new visualization.GenomeRegion({
- chrom: this.track.view.chrom,
- start: this.track.view.low,
- end: this.track.view.high
+ var track = this.model.get('track'),
+ tool = this.model.get('tool'),
+ region = new visualization.GenomeRegion({
+ chrom: track.view.chrom,
+ start: track.view.low,
+ end: track.view.high
}),
url_params =
{
- target_dataset_id: this.track.dataset.id,
+ target_dataset_id: track.dataset.id,
action: 'rerun',
- tool_id: this.id,
+ tool_id: tool.id,
regions: [
region.toJSON()
]
},
- current_track = this.track,
+ current_track = track,
// Set name of track to include tool name, parameters, and region used.
track_name = url_params.tool_id +
current_track.tool_region_and_parameters_str(region),
@@ -1838,25 +1828,25 @@
new_track.tiles_div.text("Starting job.");
// Run tool.
- this.update_params();
this.run(url_params, new_track,
- // Success callback.
- function(track_data) {
- new_track.set_dataset(new data.Dataset(track_data));
- new_track.tiles_div.text("Running job.");
- new_track.init();
- }
- );
+ // Success callback.
+ function(track_data) {
+ new_track.set_dataset(new data.Dataset(track_data));
+ new_track.tiles_div.text("Running job.");
+ new_track.init();
+ }
+ );
},
+
/**
* Run tool using a set of URL params and a success callback.
*/
run: function(url_params, new_track, success_callback) {
// Run tool.
- url_params.inputs = this.get_param_values_dict();
+ url_params.inputs = this.model.get('tool').get_inputs_dict();
var ss_deferred = new util.ServerStateDeferred({
ajax_settings: {
- url: config.root + "/api/tools",
+ url: config.root + "api/tools",
data: JSON.stringify(url_params),
dataType: "json",
contentType: 'application/json',
@@ -1889,36 +1879,7 @@
}
});
}
-});
-
-/**
- * Tool parameters.
- */
-var ToolParameter = function(name, label, html, value) {
- this.name = name;
- this.label = label;
- // Need to use jQuery for HTML so that value can be queried and updated dynamically.
- this.html = $(html);
- this.value = value;
-};
-
-extend(ToolParameter.prototype, {
- update_value: function() {
- this.value = $(this.html).val();
- }
-});
-
-var NumberParameter = function(name, label, html, value, min, max) {
- ToolParameter.call(this, name, label, html, value);
- this.min = min;
- this.max = max;
-};
-
-extend(NumberParameter.prototype, ToolParameter.prototype, {
- update_value: function() {
- ToolParameter.prototype.update_value.call(this);
- this.value = parseFloat(this.value);
- }
+
});
/**
@@ -2819,7 +2780,12 @@
// FIXME: prolly need function to set filters and update data_manager reference.
this.data_manager.set('filters_manager', this.filters_manager);
this.filters_available = false;
- this.tool = ('tool' in obj_dict && obj_dict.tool ? new Tool(this, obj_dict.tool, obj_dict.tool_state) : null);
+ this.tool = (obj_dict.tool ? new TracksterTool({
+ 'track': this,
+ 'tool': obj_dict.tool,
+ 'tool_state': obj_dict.tool_state
+ })
+ : null);
this.tile_cache = new visualization.Cache(TILE_CACHE_SIZE);
this.left_offset = 0;
@@ -2830,10 +2796,12 @@
this.set_filters_manager(this.filters_manager);
//
- // Create dynamic tool div.
+ // Create dynamic tool view.
//
- if (this.tool) {
- this.dynamic_tool_div = this.tool.parent_div;
+ if (this.tool) {
+ var tool_view = new TracksterToolView({ model: this.tool });
+ tool_view.render();
+ this.dynamic_tool_div = tool_view.$el;
this.header_div.after(this.dynamic_tool_div);
}
}
@@ -3427,8 +3395,9 @@
*/
tool_region_and_parameters_str: function(region) {
var track = this,
- region_str = (region !== undefined ? region.toString() : "all");
- return " - region=[" + region_str + "], parameters=[" + track.tool.get_param_values().join(", ") + "]";
+ region_str = (region !== undefined ? region.toString() : "all"),
+ param_str = _.values( track.tool.get('tool').get_inputs_dict()).join(', ');
+ return " - region=[" + region_str + "], parameters=[" + param_str + "]";
},
/**
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: pull request #168 from Kyle Ellrott: add graphview visualization as visualization plugin
by commits-noreply@bitbucket.org 08 Aug '13
by commits-noreply@bitbucket.org 08 Aug '13
08 Aug '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/8c3b553871bb/
Changeset: 8c3b553871bb
User: carlfeberhard
Date: 2013-08-08 22:48:56
Summary: pull request #168 from Kyle Ellrott: add graphview visualization as visualization plugin
Affected #: 5 files
diff -r 6d3d5e571a43dc2defa94c0baef8c96461818a53 -r 8c3b553871bbcce9170d235f5188b9803094ad3b config/plugins/visualizations/graphview/config/graphview.xml
--- /dev/null
+++ b/config/plugins/visualizations/graphview/config/graphview.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE visualization SYSTEM "../../visualization.dtd">
+<visualization name="Graphview">
+ <data_sources>
+ <data_source>
+ <model_class>HistoryDatasetAssociation</model_class>
+ <test type="has_dataprovider" test_attr="datatype">node-edge</test>
+ <to_param param_attr="id">dataset_id</to_param>
+ </data_source>
+ <data_source>
+ <model_class>HistoryDatasetAssociation</model_class>
+ <test type="isinstance" test_attr="datatype" result_type="datatype">graph.Rdf</test>
+ <to_param param_attr="id">dataset_id</to_param>
+ </data_source>
+ </data_sources>
+ <params>
+ <param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
+ </params>
+ <template>graphview/templates/graphview.mako</template>
+</visualization>
diff -r 6d3d5e571a43dc2defa94c0baef8c96461818a53 -r 8c3b553871bbcce9170d235f5188b9803094ad3b config/plugins/visualizations/graphview/static/graphview.css
--- /dev/null
+++ b/config/plugins/visualizations/graphview/static/graphview.css
@@ -0,0 +1,70 @@
+svg {
+ border: 1px solid lightgrey;
+ background-color: #FFF;
+ cursor: default;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+
+svg:not(.active):not(.ctrl) {
+ cursor: crosshair;
+}
+
+path.link {
+ fill: none;
+ stroke: #000;
+ stroke-width: 4px;
+ cursor: default;
+}
+
+svg:not(.active):not(.ctrl) path.link {
+ cursor: pointer;
+}
+
+path.link.selected {
+ stroke-dasharray: 10,2;
+}
+
+path.link.dragline {
+ pointer-events: none;
+}
+
+path.link.hidden {
+ stroke-width: 0;
+}
+
+circle.node {
+ stroke-width: 1.0px;
+ cursor: pointer;
+}
+
+circle.node.reflexive {
+ stroke: #000 !important;
+ stroke-width: 2.5px;
+}
+
+text {
+ font: 12px sans-serif;
+ pointer-events: none;
+}
+
+text.id {
+ text-anchor: middle;
+ font-weight: bold;
+}
+
+div.tooltip {
+ position: absolute;
+ text-align: center;
+ width: 60px;
+ height: 28px;
+ padding: 2px;
+ font: 12px sans-serif;
+ background: lightsteelblue;
+ border: 0px;
+ border-radius: 8px;
+ pointer-events: none;
+}
diff -r 6d3d5e571a43dc2defa94c0baef8c96461818a53 -r 8c3b553871bbcce9170d235f5188b9803094ad3b config/plugins/visualizations/graphview/static/graphview.js
--- /dev/null
+++ b/config/plugins/visualizations/graphview/static/graphview.js
@@ -0,0 +1,175 @@
+function get_firstChild(n) {
+ y=n.firstChild;
+ while (y && y.nodeType!=1) {
+ y=y.nextSibling;
+ }
+ return y;
+}
+
+function get_nextSibling(n) {
+ y=n.nextSibling;
+ while (y) {
+ if (y.nodeType==1) {
+ return y;
+ }
+ y=y.nextSibling;
+ }
+ return y;
+}
+
+function parse_xgmml_attr(elm) {
+ out = {};
+ var c = get_firstChild(elm);
+ while (c) {
+ if (c.nodeName == "att") {
+ if (c.attributes['type'].value == "string") {
+ out[c.attributes['name'].value] = c.attributes['value'].value;
+ }
+ }
+ c = get_nextSibling(c);
+ }
+ return out
+}
+
+function parse_xgmml(root, add_node, add_edge) {
+ graph=root.getElementsByTagName("graph")[0];
+ elm = get_firstChild(graph);
+ while (elm) {
+ if (elm.nodeName == "node") {
+ var attr = parse_xgmml_attr(elm);
+ add_node( elm.attributes['id'].value, elm.attributes['label'].value, attr );
+ }
+ if (elm.nodeName == "edge") {
+ var attr = parse_xgmml_attr(elm);
+ add_edge( elm.attributes['source'].value, elm.attributes['target'].value, attr );
+ }
+
+ elm = get_nextSibling(elm);
+ }
+}
+
+function parse_sif(data, add_node, add_edge) {
+ var lines = data.split("\n")
+ for (var i in lines) {
+ var tmp = lines[i].split("\t");
+ if (tmp.length == 3) {
+ add_edge(tmp[0], tmp[2], {type:tmp[1]});
+ }
+ }
+
+}
+
+jQuery.fn.graphViewer = function(config) {
+
+ var svg, colors;
+ var nodes = [],
+ links = [];
+
+ var height = config.height;
+ var width = config.width;
+
+
+ colors = d3.scale.category10();
+
+ this.each(function() {
+ svg = d3.select(this)
+ .append('svg')
+ .attr('width', width)
+ .attr('height', height);
+ }
+ );
+
+ var tooltip_div = d3.select("#tooltip").attr("class", "tooltip").style("opacity", 0);;
+
+ this.add_node = function(node_id, node_label, attr) {
+ nodes.push( {id: node_id, label:node_label, attr:attr} );
+ }
+
+ this.add_edge = function(src_id, dst_id, attr) {
+ var src, target;
+ for (var i in nodes) {
+ if (nodes[i].id == src_id) {
+ src = nodes[i];
+ }
+ if (nodes[i].id == dst_id) {
+ target = nodes[i];
+ }
+ }
+ if (typeof src==="undefined") {
+ i = nodes.length
+ nodes.push( {id:src_id, label:src_id, attr:{}} )
+ src = nodes[i]
+ }
+ if (typeof target==="undefined") {
+ i = nodes.length
+ nodes.push( {id:dst_id, label:dst_id, attr:{}} )
+ target = nodes[i]
+ }
+ if (src && target) {
+ links.push( {source: src, target: target, left: false, right: true } );
+ }
+ }
+
+ this.render = function() {
+ var path = svg.append('svg:g').selectAll('path'),
+ circle = svg.append('svg:g').selectAll('g');
+
+
+ circle = circle.data(nodes, function(d) { return d.id; });
+ var g = circle.enter().append('svg:g');
+
+ circle.on('mouseover', function(d) {
+ tooltip_div.transition()
+ .duration(200)
+ .style("opacity", .9);
+ tooltip_div.html( "<div>" + d.label + "</div><div>" + d.attr.type + "</div>" ).style("left", (d3.event.pageX + 40) + "px")
+ .style("top", (d3.event.pageY - 35) + "px");
+ })
+ .on("mouseout", function(d) {
+ tooltip_div.transition()
+ .duration(500)
+ .style("opacity", 0);
+ });
+
+ path = path.data(links);
+
+
+ function tick() {
+ path.attr('d', function(d) {
+ return 'M' + d.source.x + ',' + d.source.y + 'L' + d.target.x + ',' + d.target.y;
+ });
+ circle.attr('transform', function(d) {
+ return 'translate(' + d.x + ',' + d.y + ')';
+ });
+ }
+
+
+ g.append('svg:circle')
+ .attr('class', 'node')
+ .attr('r', 8)
+ .style('stroke', 'black')
+ .attr('fill', function(d) {
+ return colors(d['attr']['type'])
+ });
+
+ path.enter().append('svg:path')
+ .attr('class', 'link')
+
+
+ // init D3 force layout
+ var force = d3.layout.force()
+ .nodes(nodes)
+ .links(links)
+ .size([width, height])
+ .linkDistance(25)
+ .charge(-50)
+ .on('tick', tick)
+ circle.call(force.drag)
+ force.start()
+
+ }
+
+ return this
+
+}
+
This diff is so big that we needed to truncate the remainder.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: jgoecks: Update jquery.mousewheel plugin from version 3.0.4 to 3.1.3, introducing a necessary (for now) hack. This makes the plugin compatable with the most recent version of jquery.
by commits-noreply@bitbucket.org 08 Aug '13
by commits-noreply@bitbucket.org 08 Aug '13
08 Aug '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/6d3d5e571a43/
Changeset: 6d3d5e571a43
User: jgoecks
Date: 2013-08-08 22:41:18
Summary: Update jquery.mousewheel plugin from version 3.0.4 to 3.1.3, introducing a necessary (for now) hack. This makes the plugin compatable with the most recent version of jquery.
Affected #: 1 file
diff -r 61ddd0d07950fafc18fed95354459e7715e6b1d7 -r 6d3d5e571a43dc2defa94c0baef8c96461818a53 static/scripts/libs/jquery/jquery.mousewheel.js
--- a/static/scripts/libs/jquery/jquery.mousewheel.js
+++ b/static/scripts/libs/jquery/jquery.mousewheel.js
@@ -1,78 +1,125 @@
-/* Copyright (c) 2010 Brandon Aaron (http://brandonaaron.net)
+/*! Copyright (c) 2013 Brandon Aaron (http://brandonaaron.net)
* Licensed under the MIT License (LICENSE.txt).
*
* Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
* Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
* Thanks to: Seamus Leahy for adding deltaX and deltaY
*
- * Version: 3.0.4
- *
+ * Version: 3.1.3
+ *
* Requires: 1.2.2+
*/
-(function($) {
+(function (factory) {
+ // GALAXY HACK
+ // (JG): Galaxy's mixing of a global jQuery and require modules doesn't work with
+ // the logic below. Instead, do the right thing for this configuration without any checks.
+ factory(jQuery);
+ // END HACK
-var types = ['DOMMouseScroll', 'mousewheel'];
+ /*
+ if ( typeof define === 'function' && define.amd ) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else if (typeof exports === 'object') {
+ // Node/CommonJS style for Browserify
+ module.exports = factory;
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+ */
+}(function ($) {
-$.event.special.mousewheel = {
- setup: function() {
- if ( this.addEventListener ) {
- for ( var i=types.length; i; ) {
- this.addEventListener( types[--i], handler, false );
- }
- } else {
- this.onmousewheel = handler;
- }
- },
-
- teardown: function() {
- if ( this.removeEventListener ) {
- for ( var i=types.length; i; ) {
- this.removeEventListener( types[--i], handler, false );
- }
- } else {
- this.onmousewheel = null;
+ var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll'];
+ var toBind = 'onwheel' in document || document.documentMode >= 9 ? ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll'];
+ var lowestDelta, lowestDeltaXY;
+
+ if ( $.event.fixHooks ) {
+ for ( var i = toFix.length; i; ) {
+ $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks;
}
}
-};
-$.fn.extend({
- mousewheel: function(fn) {
- return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
- },
-
- unmousewheel: function(fn) {
- return this.unbind("mousewheel", fn);
+ $.event.special.mousewheel = {
+ setup: function() {
+ if ( this.addEventListener ) {
+ for ( var i = toBind.length; i; ) {
+ this.addEventListener( toBind[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = handler;
+ }
+ },
+
+ teardown: function() {
+ if ( this.removeEventListener ) {
+ for ( var i = toBind.length; i; ) {
+ this.removeEventListener( toBind[--i], handler, false );
+ }
+ } else {
+ this.onmousewheel = null;
+ }
+ }
+ };
+
+ $.fn.extend({
+ mousewheel: function(fn) {
+ return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
+ },
+
+ unmousewheel: function(fn) {
+ return this.unbind("mousewheel", fn);
+ }
+ });
+
+
+ function handler(event) {
+ var orgEvent = event || window.event,
+ args = [].slice.call(arguments, 1),
+ delta = 0,
+ deltaX = 0,
+ deltaY = 0,
+ absDelta = 0,
+ absDeltaXY = 0,
+ fn;
+ event = $.event.fix(orgEvent);
+ event.type = "mousewheel";
+
+ // Old school scrollwheel delta
+ if ( orgEvent.wheelDelta ) { delta = orgEvent.wheelDelta; }
+ if ( orgEvent.detail ) { delta = orgEvent.detail * -1; }
+
+ // New school wheel delta (wheel event)
+ if ( orgEvent.deltaY ) {
+ deltaY = orgEvent.deltaY * -1;
+ delta = deltaY;
+ }
+ if ( orgEvent.deltaX ) {
+ deltaX = orgEvent.deltaX;
+ delta = deltaX * -1;
+ }
+
+ // Webkit
+ if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY; }
+ if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = orgEvent.wheelDeltaX * -1; }
+
+ // Look for lowest delta to normalize the delta values
+ absDelta = Math.abs(delta);
+ if ( !lowestDelta || absDelta < lowestDelta ) { lowestDelta = absDelta; }
+ absDeltaXY = Math.max(Math.abs(deltaY), Math.abs(deltaX));
+ if ( !lowestDeltaXY || absDeltaXY < lowestDeltaXY ) { lowestDeltaXY = absDeltaXY; }
+
+ // Get a whole value for the deltas
+ fn = delta > 0 ? 'floor' : 'ceil';
+ delta = Math[fn](delta / lowestDelta);
+ deltaX = Math[fn](deltaX / lowestDeltaXY);
+ deltaY = Math[fn](deltaY / lowestDeltaXY);
+
+ // Add event and delta to the front of the arguments
+ args.unshift(event, delta, deltaX, deltaY);
+
+ return ($.event.dispatch || $.event.handle).apply(this, args);
}
-});
-
-function handler(event) {
- var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true, deltaX = 0, deltaY = 0;
- event = $.event.fix(orgEvent);
- event.type = "mousewheel";
-
- // Old school scrollwheel delta
- if ( event.wheelDelta ) { delta = event.wheelDelta/120; }
- if ( event.detail ) { delta = -event.detail/3; }
-
- // New school multidimensional scroll (touchpads) deltas
- deltaY = delta;
-
- // Gecko
- if ( orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS ) {
- deltaY = 0;
- deltaX = -1*delta;
- }
-
- // Webkit
- if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY/120; }
- if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = -1*orgEvent.wheelDeltaX/120; }
-
- // Add event and delta to the front of the arguments
- args.unshift(event, delta, deltaX, deltaY);
-
- return $.event.handle.apply(this, args);
-}
-
-})(jQuery);
+}));
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/e7e82003f235/
Changeset: e7e82003f235
Branch: next-stable
User: natefoo
Date: 2013-08-08 21:40:00
Summary: Define the ActionInputError exception that was being caught in the quotas API controller. Thanks Jim Johnson.
Affected #: 3 files
diff -r 8d8429484ac3ffef21fedd1077acf050c8aed459 -r e7e82003f235c4cca84939e04aa432d128093866 lib/galaxy/actions/admin.py
--- a/lib/galaxy/actions/admin.py
+++ b/lib/galaxy/actions/admin.py
@@ -4,7 +4,7 @@
import logging
from galaxy import util
-from galaxy.exceptions import MessageException
+from galaxy.exceptions import ActionInputError
log = logging.getLogger( __name__ )
@@ -21,21 +21,21 @@
except AssertionError:
create_amount = False
if not params.name or not params.description:
- raise MessageException( "Enter a valid name and a description.", type='error' )
+ raise ActionInputError( "Enter a valid name and a description." )
elif self.sa_session.query( self.app.model.Quota ).filter( self.app.model.Quota.table.c.name==params.name ).first():
- raise MessageException( "Quota names must be unique and a quota with that name already exists, so choose another name.", type='error' )
+ raise ActionInputError( "Quota names must be unique and a quota with that name already exists, so choose another name." )
elif not params.get( 'amount', None ):
- raise MessageException( "Enter a valid quota amount.", type='error' )
+ raise ActionInputError( "Enter a valid quota amount." )
elif create_amount is False:
- raise MessageException( "Unable to parse the provided amount.", type='error' )
+ raise ActionInputError( "Unable to parse the provided amount." )
elif params.operation not in self.app.model.Quota.valid_operations:
- raise MessageException( "Enter a valid operation.", type='error' )
+ raise ActionInputError( "Enter a valid operation." )
elif params.default != 'no' and params.default not in self.app.model.DefaultQuotaAssociation.types.__dict__.values():
- raise MessageException( "Enter a valid default type.", type='error' )
+ raise ActionInputError( "Enter a valid default type." )
elif params.default != 'no' and params.operation != '=':
- raise MessageException( "Operation for a default quota must be '='.", type='error' )
+ raise ActionInputError( "Operation for a default quota must be '='." )
elif create_amount is None and params.operation != '=':
- raise MessageException( "Operation for an unlimited quota must be '='.", type='error' )
+ raise ActionInputError( "Operation for an unlimited quota must be '='." )
else:
# Create the quota
quota = self.app.model.Quota( name=params.name, description=params.description, amount=create_amount, operation=params.operation )
@@ -59,9 +59,9 @@
def _rename_quota( self, quota, params ):
if not params.name:
- raise MessageException( 'Enter a valid name', type='error' )
+ raise ActionInputError( 'Enter a valid name' )
elif params.name != quota.name and self.sa_session.query( self.app.model.Quota ).filter( self.app.model.Quota.table.c.name==params.name ).first():
- raise MessageException( 'A quota with that name already exists', type='error' )
+ raise ActionInputError( 'A quota with that name already exists' )
else:
old_name = quota.name
quota.name = params.name
@@ -73,7 +73,7 @@
def _manage_users_and_groups_for_quota( self, quota, params ):
if quota.default:
- raise MessageException( 'Default quotas cannot be associated with specific users and groups', type='error' )
+ raise ActionInputError( 'Default quotas cannot be associated with specific users and groups' )
else:
in_users = [ self.sa_session.query( self.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
in_groups = [ self.sa_session.query( self.app.model.Group ).get( x ) for x in util.listify( params.in_groups ) ]
@@ -91,11 +91,11 @@
except AssertionError:
new_amount = False
if not params.amount:
- raise MessageException( 'Enter a valid amount', type='error' )
+ raise ActionInputError( 'Enter a valid amount' )
elif new_amount is False:
- raise MessageException( 'Unable to parse the provided amount', type='error' )
+ raise ActionInputError( 'Unable to parse the provided amount' )
elif params.operation not in self.app.model.Quota.valid_operations:
- raise MessageException( 'Enter a valid operation', type='error' )
+ raise ActionInputError( 'Enter a valid operation' )
else:
quota.amount = new_amount
quota.operation = params.operation
@@ -106,7 +106,7 @@
def _set_quota_default( self, quota, params ):
if params.default != 'no' and params.default not in self.app.model.DefaultQuotaAssociation.types.__dict__.values():
- raise MessageException( 'Enter a valid default type.', type='error' )
+ raise ActionInputError( 'Enter a valid default type.' )
else:
if params.default != 'no':
self.app.quota_agent.set_default_quota( params.default, quota )
@@ -123,7 +123,7 @@
def _unset_quota_default( self, quota, params ):
if not quota.default:
- raise MessageException( "Quota '%s' is not a default." % quota.name, type='error' )
+ raise ActionInputError( "Quota '%s' is not a default." % quota.name )
else:
message = "Quota '%s' is no longer the default for %s users." % ( quota.name, quota.default[0].type )
for dqa in quota.default:
@@ -138,9 +138,9 @@
if q.default:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' is a default, please unset it as a default before deleting it" % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' is a default, please unset it as a default before deleting it" % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas are defaults, please unset them as defaults before deleting them: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas are defaults, please unset them as defaults before deleting them: " + ', '.join( names ) )
message = "Deleted %d quotas: " % len( quotas )
for q in quotas:
q.deleted = True
@@ -157,9 +157,9 @@
if not q.deleted:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' has not been deleted, so it cannot be undeleted." % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' has not been deleted, so it cannot be undeleted." % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ) )
message = "Undeleted %d quotas: " % len( quotas )
for q in quotas:
q.deleted = False
@@ -182,9 +182,9 @@
if not q.deleted:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' has not been deleted, so it cannot be purged." % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' has not been deleted, so it cannot be purged." % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ) )
message = "Purged %d quotas: " % len( quotas )
for q in quotas:
# Delete UserQuotaAssociations
diff -r 8d8429484ac3ffef21fedd1077acf050c8aed459 -r e7e82003f235c4cca84939e04aa432d128093866 lib/galaxy/exceptions/__init__.py
--- a/lib/galaxy/exceptions/__init__.py
+++ b/lib/galaxy/exceptions/__init__.py
@@ -21,6 +21,10 @@
class ItemOwnershipException( MessageException ):
pass
+class ActionInputError( MessageException ):
+ def __init__( self, err_msg, type="error" ):
+ super( ActionInputError, self ).__init__( err_msg, type )
+
class ObjectNotFound( Exception ):
""" Accessed object was not found """
pass
diff -r 8d8429484ac3ffef21fedd1077acf050c8aed459 -r e7e82003f235c4cca84939e04aa432d128093866 lib/galaxy/webapps/galaxy/api/quotas.py
--- a/lib/galaxy/webapps/galaxy/api/quotas.py
+++ b/lib/galaxy/webapps/galaxy/api/quotas.py
@@ -11,7 +11,7 @@
from galaxy.actions.admin import AdminActions
from paste.httpexceptions import HTTPBadRequest
-from galaxy.exceptions import *
+from galaxy.exceptions import ActionInputError
log = logging.getLogger( __name__ )
https://bitbucket.org/galaxy/galaxy-central/commits/61ddd0d07950/
Changeset: 61ddd0d07950
User: natefoo
Date: 2013-08-08 21:50:05
Summary: Merge next-stable.
Affected #: 3 files
diff -r 3778811f053a5f71c68408a9e8beb627f6ccdede -r 61ddd0d07950fafc18fed95354459e7715e6b1d7 lib/galaxy/actions/admin.py
--- a/lib/galaxy/actions/admin.py
+++ b/lib/galaxy/actions/admin.py
@@ -4,7 +4,7 @@
import logging
from galaxy import util
-from galaxy.exceptions import MessageException
+from galaxy.exceptions import ActionInputError
log = logging.getLogger( __name__ )
@@ -21,21 +21,21 @@
except AssertionError:
create_amount = False
if not params.name or not params.description:
- raise MessageException( "Enter a valid name and a description.", type='error' )
+ raise ActionInputError( "Enter a valid name and a description." )
elif self.sa_session.query( self.app.model.Quota ).filter( self.app.model.Quota.table.c.name==params.name ).first():
- raise MessageException( "Quota names must be unique and a quota with that name already exists, so choose another name.", type='error' )
+ raise ActionInputError( "Quota names must be unique and a quota with that name already exists, so choose another name." )
elif not params.get( 'amount', None ):
- raise MessageException( "Enter a valid quota amount.", type='error' )
+ raise ActionInputError( "Enter a valid quota amount." )
elif create_amount is False:
- raise MessageException( "Unable to parse the provided amount.", type='error' )
+ raise ActionInputError( "Unable to parse the provided amount." )
elif params.operation not in self.app.model.Quota.valid_operations:
- raise MessageException( "Enter a valid operation.", type='error' )
+ raise ActionInputError( "Enter a valid operation." )
elif params.default != 'no' and params.default not in self.app.model.DefaultQuotaAssociation.types.__dict__.values():
- raise MessageException( "Enter a valid default type.", type='error' )
+ raise ActionInputError( "Enter a valid default type." )
elif params.default != 'no' and params.operation != '=':
- raise MessageException( "Operation for a default quota must be '='.", type='error' )
+ raise ActionInputError( "Operation for a default quota must be '='." )
elif create_amount is None and params.operation != '=':
- raise MessageException( "Operation for an unlimited quota must be '='.", type='error' )
+ raise ActionInputError( "Operation for an unlimited quota must be '='." )
else:
# Create the quota
quota = self.app.model.Quota( name=params.name, description=params.description, amount=create_amount, operation=params.operation )
@@ -59,9 +59,9 @@
def _rename_quota( self, quota, params ):
if not params.name:
- raise MessageException( 'Enter a valid name', type='error' )
+ raise ActionInputError( 'Enter a valid name' )
elif params.name != quota.name and self.sa_session.query( self.app.model.Quota ).filter( self.app.model.Quota.table.c.name==params.name ).first():
- raise MessageException( 'A quota with that name already exists', type='error' )
+ raise ActionInputError( 'A quota with that name already exists' )
else:
old_name = quota.name
quota.name = params.name
@@ -73,7 +73,7 @@
def _manage_users_and_groups_for_quota( self, quota, params ):
if quota.default:
- raise MessageException( 'Default quotas cannot be associated with specific users and groups', type='error' )
+ raise ActionInputError( 'Default quotas cannot be associated with specific users and groups' )
else:
in_users = [ self.sa_session.query( self.app.model.User ).get( x ) for x in util.listify( params.in_users ) ]
in_groups = [ self.sa_session.query( self.app.model.Group ).get( x ) for x in util.listify( params.in_groups ) ]
@@ -91,11 +91,11 @@
except AssertionError:
new_amount = False
if not params.amount:
- raise MessageException( 'Enter a valid amount', type='error' )
+ raise ActionInputError( 'Enter a valid amount' )
elif new_amount is False:
- raise MessageException( 'Unable to parse the provided amount', type='error' )
+ raise ActionInputError( 'Unable to parse the provided amount' )
elif params.operation not in self.app.model.Quota.valid_operations:
- raise MessageException( 'Enter a valid operation', type='error' )
+ raise ActionInputError( 'Enter a valid operation' )
else:
quota.amount = new_amount
quota.operation = params.operation
@@ -106,7 +106,7 @@
def _set_quota_default( self, quota, params ):
if params.default != 'no' and params.default not in self.app.model.DefaultQuotaAssociation.types.__dict__.values():
- raise MessageException( 'Enter a valid default type.', type='error' )
+ raise ActionInputError( 'Enter a valid default type.' )
else:
if params.default != 'no':
self.app.quota_agent.set_default_quota( params.default, quota )
@@ -123,7 +123,7 @@
def _unset_quota_default( self, quota, params ):
if not quota.default:
- raise MessageException( "Quota '%s' is not a default." % quota.name, type='error' )
+ raise ActionInputError( "Quota '%s' is not a default." % quota.name )
else:
message = "Quota '%s' is no longer the default for %s users." % ( quota.name, quota.default[0].type )
for dqa in quota.default:
@@ -138,9 +138,9 @@
if q.default:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' is a default, please unset it as a default before deleting it" % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' is a default, please unset it as a default before deleting it" % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas are defaults, please unset them as defaults before deleting them: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas are defaults, please unset them as defaults before deleting them: " + ', '.join( names ) )
message = "Deleted %d quotas: " % len( quotas )
for q in quotas:
q.deleted = True
@@ -157,9 +157,9 @@
if not q.deleted:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' has not been deleted, so it cannot be undeleted." % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' has not been deleted, so it cannot be undeleted." % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ) )
message = "Undeleted %d quotas: " % len( quotas )
for q in quotas:
q.deleted = False
@@ -182,9 +182,9 @@
if not q.deleted:
names.append( q.name )
if len( names ) == 1:
- raise MessageException( "Quota '%s' has not been deleted, so it cannot be purged." % ( names[0] ), type='error' )
+ raise ActionInputError( "Quota '%s' has not been deleted, so it cannot be purged." % ( names[0] ) )
elif len( names ) > 1:
- raise MessageException( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ), type='error' )
+ raise ActionInputError( "Quotas have not been deleted so they cannot be undeleted: " + ', '.join( names ) )
message = "Purged %d quotas: " % len( quotas )
for q in quotas:
# Delete UserQuotaAssociations
diff -r 3778811f053a5f71c68408a9e8beb627f6ccdede -r 61ddd0d07950fafc18fed95354459e7715e6b1d7 lib/galaxy/exceptions/__init__.py
--- a/lib/galaxy/exceptions/__init__.py
+++ b/lib/galaxy/exceptions/__init__.py
@@ -21,6 +21,10 @@
class ItemOwnershipException( MessageException ):
pass
+class ActionInputError( MessageException ):
+ def __init__( self, err_msg, type="error" ):
+ super( ActionInputError, self ).__init__( err_msg, type )
+
class ObjectNotFound( Exception ):
""" Accessed object was not found """
pass
diff -r 3778811f053a5f71c68408a9e8beb627f6ccdede -r 61ddd0d07950fafc18fed95354459e7715e6b1d7 lib/galaxy/webapps/galaxy/api/quotas.py
--- a/lib/galaxy/webapps/galaxy/api/quotas.py
+++ b/lib/galaxy/webapps/galaxy/api/quotas.py
@@ -11,7 +11,7 @@
from galaxy.actions.admin import AdminActions
from paste.httpexceptions import HTTPBadRequest
-from galaxy.exceptions import *
+from galaxy.exceptions import ActionInputError
log = logging.getLogger( __name__ )
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: Graph datatype: add data providers for SIF & XGMML; simple graph data structure to util
by commits-noreply@bitbucket.org 08 Aug '13
by commits-noreply@bitbucket.org 08 Aug '13
08 Aug '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3778811f053a/
Changeset: 3778811f053a
User: carlfeberhard
Date: 2013-08-08 21:04:53
Summary: Graph datatype: add data providers for SIF & XGMML; simple graph data structure to util
Affected #: 2 files
diff -r 2e0abb7f9b04540e616458a90a87191ed98a3ba4 -r 3778811f053a5f71c68408a9e8beb627f6ccdede lib/galaxy/datatypes/graph.py
--- a/lib/galaxy/datatypes/graph.py
+++ b/lib/galaxy/datatypes/graph.py
@@ -2,12 +2,18 @@
Graph content classes.
"""
-import data, tabular, xml
+import data
+import tabular
+import xml
+
+import dataproviders
+from galaxy.util import simplegraph
import logging
log = logging.getLogger( __name__ )
+(a)dataproviders.decorators.has_dataproviders
class Xgmml( xml.GenericXml ):
"""
XGMML graph format
@@ -48,7 +54,13 @@
#For one file only, use base class method (move/copy)
data.Text.merge( split_files, output_file )
+ @dataproviders.decorators.dataprovider_factory( 'node-edge', dataproviders.hierarchy.XMLDataProvider.settings )
+ def node_edge_dataprovider( self, dataset, **settings ):
+ dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ return XGMMLGraphDataProvider( dataset_source, **settings )
+
+(a)dataproviders.decorators.has_dataproviders
class Sif( tabular.Tabular ):
"""
SIF graph format
@@ -75,7 +87,6 @@
"""
Determines whether the file is SIF
"""
- print '---------------------------------------- sniffing Siffing'
line = ''
with open( filename ) as infile:
correct = True
@@ -92,6 +103,11 @@
def merge( split_files, output_file ):
data.Text.merge( split_files, output_file )
+ @dataproviders.decorators.dataprovider_factory( 'node-edge', dataproviders.column.ColumnarDataProvider.settings )
+ def node_edge_dataprovider( self, dataset, **settings ):
+ dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ return SIFGraphDataProvider( dataset_source, **settings )
+
#TODO: we might want to look at rdflib or a similar, larger lib/egg
class Rdf( xml.GenericXml ):
@@ -108,3 +124,75 @@
else:
dataset.peek = 'file does not exist'
dataset.blurb = 'file purged from disk'
+
+ #TODO: won't be as simple
+ #(a)dataproviders.decorators.dataprovider_factory( 'node-edge', dataproviders.column.ColumnarDataProvider.settings )
+ #def node_edge_dataprovider( self, dataset, **settings ):
+ # dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ # return None
+
+
+# ----------------------------------------------------------------------------- graph specific data providers
+class XGMMLGraphDataProvider( dataproviders.hierarchy.XMLDataProvider ):
+ """
+ Provide two lists: nodes, edges::
+
+ 'nodes': contains objects of the form:
+ { 'id' : <some string id>, 'data': <any extra data> }
+ 'edges': contains objects of the form:
+ { 'source' : <an index into nodes>, 'target': <an index into nodes>, 'data': <any extra data> }
+ """
+ def __iter__( self ):
+ # use simple graph to store nodes and links, later providing them as a dict
+ # essentially this is a form of aggregation
+ graph = simplegraph.SimpleGraph()
+
+ parent_gen = super( XGMMLGraphDataProvider, self ).__iter__()
+ for graph_elem in parent_gen:
+ if 'children' not in graph_elem:
+ continue
+ for elem in graph_elem[ 'children' ]:
+ # use endswith to work around Elementtree namespaces
+ if elem[ 'tag' ].endswith( 'node' ):
+ node_id = elem[ 'attrib' ][ 'id' ]
+ # pass the entire, parsed xml element as the data
+ graph.add_node( node_id, **elem )
+
+ elif elem[ 'tag' ].endswith( 'edge' ):
+ source_id = elem[ 'attrib' ][ 'source' ]
+ target_id = elem[ 'attrib' ][ 'target' ]
+ graph.add_edge( source_id, target_id, **elem )
+
+ yield graph.as_dict()
+
+
+class SIFGraphDataProvider( dataproviders.column.ColumnarDataProvider ):
+ """
+ Provide two lists: nodes, edges::
+
+ 'nodes': contains objects of the form:
+ { 'id' : <some string id>, 'data': <any extra data> }
+ 'edges': contains objects of the form:
+ { 'source' : <an index into nodes>, 'target': <an index into nodes>, 'data': <any extra data> }
+ """
+ def __iter__( self ):
+ # use simple graph to store nodes and links, later providing them as a dict
+ # essentially this is a form of aggregation
+ graph = simplegraph.SimpleGraph()
+ # SIF is tabular with the source, link-type, and all targets in the columns
+ parent_gen = super( SIFGraphDataProvider, self ).__iter__()
+ for columns in parent_gen:
+ if columns:
+ source_id = columns[0]
+ # there's no extra data for nodes (or links) in the examples I've seen
+ graph.add_node( source_id )
+
+ # targets are the (variadic) remaining columns
+ if len( columns ) >= 3:
+ relation = columns[1]
+ targets = columns[2:]
+ for target_id in targets:
+ graph.add_node( target_id )
+ graph.add_edge( source_id, target_id, type=relation )
+
+ yield graph.as_dict()
diff -r 2e0abb7f9b04540e616458a90a87191ed98a3ba4 -r 3778811f053a5f71c68408a9e8beb627f6ccdede lib/galaxy/util/simplegraph.py
--- /dev/null
+++ b/lib/galaxy/util/simplegraph.py
@@ -0,0 +1,127 @@
+"""
+Fencepost-simple graph structure implementation.
+"""
+# Currently (2013.7.12) only used in easing the parsing of graph datatype data.
+
+from galaxy.util.odict import odict
+
+
+class SimpleGraphNode( object ):
+ """
+ Node representation.
+ """
+ def __init__( self, index, **data ):
+ """
+ :param index: index of this node in some parent list
+ :type index: int
+ :param data: any extra data that needs to be saved
+ :type data: (variadic dictionary)
+ """
+ # a bit application specific (could be 'id')
+ self.index = index
+ self.data = data
+
+
+class SimpleGraphEdge( object ):
+ """
+ Edge representation.
+ """
+ def __init__( self, source_index, target_index, **data ):
+ """
+ :param source_index: index of the edge's source node in some parent list
+ :type source_index: int
+ :param target_index: index of the edge's target node in some parent list
+ :type target_index: int
+ :param data: any extra data that needs to be saved
+ :type data: (variadic dictionary)
+ """
+ self.source_index = source_index
+ self.target_index = target_index
+ self.data = data
+
+
+class SimpleGraph( object ):
+ """
+ Each node is unique (by id) and stores it's own index in the node list/odict.
+ Each edge is represented as two indeces into the node list/odict.
+ Both nodes and edges allow storing extra information if needed.
+
+ Allows:
+ multiple edges between two nodes
+ self referential edges (an edge from a node to itself)
+
+ These graphs are not specifically directed but since source and targets on the
+ edges are listed - it could easily be used that way.
+ """
+ def __init__( self, nodes=None, edges=None ):
+ # use an odict so that edge indeces actually match the final node list indeces
+ self.nodes = nodes or odict()
+ self.edges = edges or []
+
+ def add_node( self, node_id, **data ):
+ """
+ Adds a new node only if it doesn't already exist.
+ :param node_id: some unique identifier
+ :type node_id: (hashable)
+ :param data: any extra data that needs to be saved
+ :type data: (variadic dictionary)
+ :returns: the new node
+ """
+ if node_id in self.nodes:
+ return self.nodes[ node_id ]
+ node_index = len( self.nodes )
+ new_node = SimpleGraphNode( node_index, **data )
+ self.nodes[ node_id ] = new_node
+ return new_node
+
+ def add_edge( self, source_id, target_id, **data ):
+ """
+ Adds a new node only if it doesn't already exist.
+ :param source_id: the id of the source node
+ :type source_id: (hashable)
+ :param target_id: the id of the target node
+ :type target_id: (hashable)
+ :param data: any extra data that needs to be saved for the edge
+ :type data: (variadic dictionary)
+ :returns: the new node
+
+ ..note: that, although this will create new nodes if necessary, there's
+ no way to pass `data` to them - so if you need to assoc. more data with
+ the nodes, use `add_node` first.
+ """
+ # adds target_id to source_id's edge list
+ # adding source_id and/or target_id to nodes if not there already
+ if source_id not in self.nodes:
+ self.add_node( source_id )
+ if target_id not in self.nodes:
+ self.add_node( target_id )
+ new_edge = SimpleGraphEdge( self.nodes[ source_id ].index, self.nodes[ target_id ].index, **data )
+ self.edges.append( new_edge )
+ return new_edge
+
+ def gen_node_dicts( self ):
+ """
+ Returns a generator that yields node dictionaries in the form:
+ { 'id': <the nodes unique id>, 'data': <any additional node data> }
+ """
+ for node_id, node in self.nodes.items():
+ yield { 'id': node_id, 'data': node.data }
+
+ def gen_edge_dicts( self ):
+ """
+ Returns a generator that yields node dictionaries in the form:
+ {
+ 'source': <the index of the source node in the graph's node list>,
+ 'target': <the index of the target node in the graph's node list>,
+ 'data' : <any additional edge data>
+ }
+ """
+ for edge in self.edges:
+ yield { 'source': edge.source_index, 'target': edge.target_index, 'data': edge.data }
+
+ def as_dict( self ):
+ """
+ Returns a dictionary of the form
+ { 'nodes': <a list of node dictionaries>, 'edges': <a list of node dictionaries> }
+ """
+ return { 'nodes': list( self.gen_node_dicts() ), 'edges': list( self.gen_edge_dicts() ) }
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: guerler: Update jquery.autocomplete.js to 2.4.4
by commits-noreply@bitbucket.org 08 Aug '13
by commits-noreply@bitbucket.org 08 Aug '13
08 Aug '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/2e0abb7f9b04/
Changeset: 2e0abb7f9b04
User: guerler
Date: 2013-08-08 20:22:42
Summary: Update jquery.autocomplete.js to 2.4.4
Affected #: 2 files
diff -r bf6bf3b5edfdc409f235cb91edcc9d91246df816 -r 2e0abb7f9b04540e616458a90a87191ed98a3ba4 static/scripts/galaxy.autocom_tagging.js
--- a/static/scripts/galaxy.autocom_tagging.js
+++ b/static/scripts/galaxy.autocom_tagging.js
@@ -100,12 +100,6 @@
var 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);
@@ -166,7 +160,7 @@
// 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.
- zz.flushCache();
+ zz.data('autocompleter').cacheFlush();
}
});
diff -r bf6bf3b5edfdc409f235cb91edcc9d91246df816 -r 2e0abb7f9b04540e616458a90a87191ed98a3ba4 static/scripts/libs/jquery/jquery.autocomplete.js
--- a/static/scripts/libs/jquery/jquery.autocomplete.js
+++ b/static/scripts/libs/jquery/jquery.autocomplete.js
@@ -1,832 +1,1152 @@
-/*
- * Autocomplete - jQuery plugin 1.0.2
- *
- * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer
- *
- * Dual licensed under the MIT and GPL licenses:
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
- *
- * Revision: $Id: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $
- *
+/**
+ * @fileOverview jquery-autocomplete, the jQuery Autocompleter
+ * @author <a href="mailto:dylan@dyve.net">Dylan Verheul</a>
+ * @version 2.4.4
+ * @requires jQuery 1.6+
+ * @license MIT | GPL | Apache 2.0, see LICENSE.txt
+ * @see https://github.com/dyve/jquery-autocomplete
*/
+(function($) {
+ "use strict";
-String.prototype.endsWith = function(str) {return (this.match(str+"$")==str)}
-
-// JG HACK: each autocomplete object should have its own return_key flag.
-var return_key_pressed_for_autocomplete = false;
-
-;(function($) {
-
-$.fn.extend({
- autocomplete: function(urlOrData, options) {
- var isUrl = typeof urlOrData == "string";
- options = $.extend({}, $.Autocompleter.defaults, {
- url: isUrl ? urlOrData : null,
- data: isUrl ? null : urlOrData,
- delay: isUrl ? $.Autocompleter.defaults.delay : 10,
- max: options && !options.scroll ? 10 : 150
- }, options);
-
- // if highlight is set to false, replace it with a do-nothing function
- options.highlight = options.highlight || function(value) { return value; };
-
- // if the formatMatch option is not specified, then use formatItem for backwards compatibility
- options.formatMatch = options.formatMatch || options.formatItem;
-
+ /**
+ * jQuery autocomplete plugin
+ * @param {object|string} options
+ * @returns (object} jQuery object
+ */
+ $.fn.autocomplete = function(options) {
+ var url;
+ if (arguments.length > 1) {
+ url = options;
+ options = arguments[1];
+ options.url = url;
+ } else if (typeof options === 'string') {
+ url = options;
+ options = { url: url };
+ }
+ var opts = $.extend({}, $.fn.autocomplete.defaults, options);
return this.each(function() {
- new $.Autocompleter(this, options);
+ var $this = $(this);
+ $this.data('autocompleter', new $.Autocompleter(
+ $this,
+ $.meta ? $.extend({}, opts, $this.data()) : opts
+ ));
});
- },
- result: function(handler) {
- return this.bind("result", handler);
- },
- search: function(handler) {
- return this.trigger("search", [handler]);
- },
- flushCache: function() {
- return this.trigger("flushCache");
- },
- setOptions: function(options){
- return this.trigger("setOptions", [options]);
- },
- unautocomplete: function() {
- return this.trigger("unautocomplete");
- },
- // JG: add method to show all data in cache.
- showAllInCache: function() {
- return this.trigger("showAllInCache");
- }
-});
-
-$.Autocompleter = function(input, options) {
-
- var KEY = {
- UP: 38,
- DOWN: 40,
- DEL: 46,
- TAB: 9,
- RETURN: 13,
- ESC: 27,
- COMMA: 188,
- PAGEUP: 33,
- PAGEDOWN: 34,
- BACKSPACE: 8,
- COLON: 16
};
- // Create $ object for input element
- var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
+ /**
+ * Store default options
+ * @type {object}
+ */
+ $.fn.autocomplete.defaults = {
+ inputClass: 'acInput',
+ loadingClass: 'acLoading',
+ resultsClass: 'acResults',
+ selectClass: 'acSelect',
+ queryParamName: 'q',
+ extraParams: {},
+ remoteDataType: false,
+ lineSeparator: '\n',
+ cellSeparator: '|',
+ minChars: 2,
+ maxItemsToShow: 10,
+ delay: 400,
+ useCache: true,
+ maxCacheLength: 10,
+ matchSubset: true,
+ matchCase: false,
+ matchInside: true,
+ mustMatch: false,
+ selectFirst: false,
+ selectOnly: false,
+ showResult: null,
+ preventDefaultReturn: 1,
+ preventDefaultTab: 0,
+ autoFill: false,
+ filterResults: true,
+ filter: true,
+ sortResults: true,
+ sortFunction: null,
+ onItemSelect: null,
+ onNoMatch: null,
+ onFinish: null,
+ matchStringConverter: null,
+ beforeUseConverter: null,
+ autoWidth: 'min-width',
+ useDelimiter: false,
+ delimiterChar: ',',
+ delimiterKeyCode: 188,
+ processData: null,
+ onError: null,
+ enabled: true
+ };
- var timeout;
- var previousValue = "";
- var cache = $.Autocompleter.Cache(options);
- var hasFocus = 0;
- var lastKeyPressCode;
- var config = {
- mouseDownOnSelect: false
+ /**
+ * Sanitize result
+ * @param {Object} result
+ * @returns {Object} object with members value (String) and data (Object)
+ * @private
+ */
+ var sanitizeResult = function(result) {
+ var value, data;
+ var type = typeof result;
+ if (type === 'string') {
+ value = result;
+ data = {};
+ } else if ($.isArray(result)) {
+ value = result[0];
+ data = result.slice(1);
+ } else if (type === 'object') {
+ value = result.value;
+ data = result.data;
+ }
+ value = String(value);
+ if (typeof data !== 'object') {
+ data = {};
+ }
+ return {
+ value: value,
+ data: data
+ };
};
- var select = $.Autocompleter.Select(options, input, selectCurrent, config);
-
- var blockSubmit;
-
- // prevent form submit in opera when selecting with return key
- $.browser.opera && $(input.form).bind("submit.autocomplete", function() {
- if (blockSubmit) {
- blockSubmit = false;
- return false;
+
+ /**
+ * Sanitize integer
+ * @param {mixed} value
+ * @param {Object} options
+ * @returns {Number} integer
+ * @private
+ */
+ var sanitizeInteger = function(value, stdValue, options) {
+ var num = parseInt(value, 10);
+ options = options || {};
+ if (isNaN(num) || (options.min && num < options.min)) {
+ num = stdValue;
}
- });
-
- // Firefox only triggers holding down a key with keypress
- $input.bind(($.browser.mozilla ? "keypress" : "keydown") + ".autocomplete", function(event) {
- // track last key pressed
- lastKeyPressCode = event.keyCode;
- switch(event.keyCode) {
-
- case KEY.UP:
- event.preventDefault();
- if ( select.visible() ) {
- select.prev();
- } else {
- onChange(0, true);
- }
+ return num;
+ };
+
+ /**
+ * Create partial url for a name/value pair
+ */
+ var makeUrlParam = function(name, value) {
+ return [name, encodeURIComponent(value)].join('=');
+ };
+
+ /**
+ * Build an url
+ * @param {string} url Base url
+ * @param {object} [params] Dictionary of parameters
+ */
+ var makeUrl = function(url, params) {
+ var urlAppend = [];
+ $.each(params, function(index, value) {
+ urlAppend.push(makeUrlParam(index, value));
+ });
+ if (urlAppend.length) {
+ url += url.indexOf('?') === -1 ? '?' : '&';
+ url += urlAppend.join('&');
+ }
+ return url;
+ };
+
+ /**
+ * Default sort filter
+ * @param {object} a
+ * @param {object} b
+ * @param {boolean} matchCase
+ * @returns {number}
+ */
+ var sortValueAlpha = function(a, b, matchCase) {
+ a = String(a.value);
+ b = String(b.value);
+ if (!matchCase) {
+ a = a.toLowerCase();
+ b = b.toLowerCase();
+ }
+ if (a > b) {
+ return 1;
+ }
+ if (a < b) {
+ return -1;
+ }
+ return 0;
+ };
+
+ /**
+ * Parse data received in text format
+ * @param {string} text Plain text input
+ * @param {string} lineSeparator String that separates lines
+ * @param {string} cellSeparator String that separates cells
+ * @returns {array} Array of autocomplete data objects
+ */
+ var plainTextParser = function(text, lineSeparator, cellSeparator) {
+ var results = [];
+ var i, j, data, line, value, lines;
+ // Be nice, fix linebreaks before splitting on lineSeparator
+ lines = String(text).replace('\r\n', '\n').split(lineSeparator);
+ for (i = 0; i < lines.length; i++) {
+ line = lines[i].split(cellSeparator);
+ data = [];
+ for (j = 0; j < line.length; j++) {
+ data.push(decodeURIComponent(line[j]));
+ }
+ value = data.shift();
+ results.push({ value: value, data: data });
+ }
+ return results;
+ };
+
+ /**
+ * Autocompleter class
+ * @param {object} $elem jQuery object with one input tag
+ * @param {object} options Settings
+ * @constructor
+ */
+ $.Autocompleter = function($elem, options) {
+
+ /**
+ * Assert parameters
+ */
+ if (!$elem || !($elem instanceof $) || $elem.length !== 1 || ($elem.get(0).tagName.toUpperCase() !== 'INPUT' && $elem.get(0).tagName.toUpperCase() !== 'TEXTAREA')) {
+ throw new Error('Invalid parameter for jquery.Autocompleter, jQuery object with one element with INPUT or TEXTAREA tag expected.');
+ }
+
+ /**
+ * @constant Link to this instance
+ * @type object
+ * @private
+ */
+ var self = this;
+
+ /**
+ * @property {object} Options for this instance
+ * @public
+ */
+ this.options = options;
+
+ /**
+ * @property object Cached data for this instance
+ * @private
+ */
+ this.cacheData_ = {};
+
+ /**
+ * @property {number} Number of cached data items
+ * @private
+ */
+ this.cacheLength_ = 0;
+
+ /**
+ * @property {string} Class name to mark selected item
+ * @private
+ */
+ this.selectClass_ = 'jquery-autocomplete-selected-item';
+
+ /**
+ * @property {number} Handler to activation timeout
+ * @private
+ */
+ this.keyTimeout_ = null;
+
+ /**
+ * @property {number} Handler to finish timeout
+ * @private
+ */
+ this.finishTimeout_ = null;
+
+ /**
+ * @property {number} Last key pressed in the input field (store for behavior)
+ * @private
+ */
+ this.lastKeyPressed_ = null;
+
+ /**
+ * @property {string} Last value processed by the autocompleter
+ * @private
+ */
+ this.lastProcessedValue_ = null;
+
+ /**
+ * @property {string} Last value selected by the user
+ * @private
+ */
+ this.lastSelectedValue_ = null;
+
+ /**
+ * @property {boolean} Is this autocompleter active (showing results)?
+ * @see showResults
+ * @private
+ */
+ this.active_ = false;
+
+ /**
+ * @property {boolean} Is this autocompleter allowed to finish on blur?
+ * @private
+ */
+ this.finishOnBlur_ = true;
+
+ /**
+ * Sanitize options
+ */
+ this.options.minChars = sanitizeInteger(this.options.minChars, $.fn.autocomplete.defaults.minChars, { min: 0 });
+ this.options.maxItemsToShow = sanitizeInteger(this.options.maxItemsToShow, $.fn.autocomplete.defaults.maxItemsToShow, { min: 0 });
+ this.options.maxCacheLength = sanitizeInteger(this.options.maxCacheLength, $.fn.autocomplete.defaults.maxCacheLength, { min: 1 });
+ this.options.delay = sanitizeInteger(this.options.delay, $.fn.autocomplete.defaults.delay, { min: 0 });
+ if (this.options.preventDefaultReturn != 2) {
+ this.options.preventDefaultReturn = this.options.preventDefaultReturn ? 1 : 0;
+ }
+ if (this.options.preventDefaultTab != 2) {
+ this.options.preventDefaultTab = this.options.preventDefaultTab ? 1 : 0;
+ }
+
+ /**
+ * Init DOM elements repository
+ */
+ this.dom = {};
+
+ /**
+ * Store the input element we're attached to in the repository
+ */
+ this.dom.$elem = $elem;
+
+ /**
+ * Switch off the native autocomplete and add the input class
+ */
+ this.dom.$elem.attr('autocomplete', 'off').addClass(this.options.inputClass);
+
+ /**
+ * Create DOM element to hold results, and force absolute position
+ */
+ this.dom.$results = $('<div></div>').hide().addClass(this.options.resultsClass).css({
+ position: 'absolute'
+ });
+ $('body').append(this.dom.$results);
+
+ /**
+ * Attach keyboard monitoring to $elem
+ */
+ $elem.keydown(function(e) {
+ self.lastKeyPressed_ = e.keyCode;
+ switch(self.lastKeyPressed_) {
+
+ case self.options.delimiterKeyCode: // comma = 188
+ if (self.options.useDelimiter && self.active_) {
+ self.selectCurrent();
+ }
+ break;
+
+ // ignore navigational & special keys
+ case 35: // end
+ case 36: // home
+ case 16: // shift
+ case 17: // ctrl
+ case 18: // alt
+ case 37: // left
+ case 39: // right
+ break;
+
+ case 38: // up
+ e.preventDefault();
+ if (self.active_) {
+ self.focusPrev();
+ } else {
+ self.activate();
+ }
+ return false;
+
+ case 40: // down
+ e.preventDefault();
+ if (self.active_) {
+ self.focusNext();
+ } else {
+ self.activate();
+ }
+ return false;
+
+ case 9: // tab
+ if (self.active_) {
+ self.selectCurrent();
+ if (self.options.preventDefaultTab) {
+ e.preventDefault();
+ return false;
+ }
+ }
+ if (self.options.preventDefaultTab === 2) {
+ e.preventDefault();
+ return false;
+ }
break;
-
- case KEY.DOWN:
- event.preventDefault();
- if ( select.visible() ) {
- select.next();
- } else {
- onChange(0, true);
- }
+
+ case 13: // return
+ if (self.active_) {
+ self.selectCurrent();
+ if (self.options.preventDefaultReturn) {
+ e.preventDefault();
+ return false;
+ }
+ }
+ if (self.options.preventDefaultReturn === 2) {
+ e.preventDefault();
+ return false;
+ }
break;
-
- case KEY.PAGEUP:
- event.preventDefault();
- if ( select.visible() ) {
- select.pageUp();
- } else {
- onChange(0, true);
- }
+
+ case 27: // escape
+ if (self.active_) {
+ e.preventDefault();
+ self.deactivate(true);
+ return false;
+ }
break;
-
- case KEY.PAGEDOWN:
- event.preventDefault();
- if ( select.visible() ) {
- select.pageDown();
- } else {
- onChange(0, true);
- }
- break;
-
- // matches also semicolon
- case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
- case KEY.TAB:
- case KEY.RETURN:
- if (event.keyCode == KEY.RETURN)
- return_key_pressed_for_autocomplete = false;
- if( selectCurrent() ) {
- // stop default to prevent a form submit, Opera needs special handling
- event.preventDefault();
- blockSubmit = true;
-
- // JG: set flag to indicate that a selection just occurred using the return key. FYI:
- // event.stopPropagation() does not work.
- if (event.keyCode == KEY.RETURN)
- return_key_pressed_for_autocomplete = true;
-
- return false;
- }
-
- case KEY.ESC:
- select.hide();
- break;
- case KEY.COLON:
- break;
-
- default:
- clearTimeout(timeout);
- timeout = setTimeout(onChange, options.delay);
- break;
- }
- }).focus(function(){
- // track whether the field has focus, we shouldn't process any
- // results if the field no longer has focus
- hasFocus++;
- }).blur(function() {
- hasFocus = 0;
- if (!config.mouseDownOnSelect) {
- // JG: if blur and user is not selecting with mouse, hide
- // object.
- select.hide();
- }
- return this;
- }).click(function() {
- // show select when clicking in a focused field
- if ( hasFocus++ > 1 && !select.visible() ) {
- onChange(0, true);
- }
- return this;
- }).bind("search", function() {
- // TODO why not just specifying both arguments?
- var fn = (arguments.length > 1) ? arguments[1] : null;
- function findValueCallback(q, data) {
- var result;
- if( data && data.length ) {
- for (var i=0; i < data.length; i++) {
- if( data[i].result.toLowerCase() == q.toLowerCase() ) {
- result = data[i];
- break;
- }
- }
+
+ default:
+ self.activate();
+
}
- if( typeof fn == "function" ) fn(result);
- else $input.trigger("result", result && [result.data, result.value]);
- }
- $.each(trimWords($input.val()), function(i, value) {
- request(value, findValueCallback, findValueCallback);
});
- return this;
- }).bind("flushCache", function() {
- cache.flush();
- }).bind("setOptions", function() {
- $.extend(options, arguments[1]);
- // if we've updated the data, repopulate
- if ( "data" in arguments[1] )
- cache.populate();
- }).bind("unautocomplete", function() {
- select.unbind();
- $input.unbind();
- $(input.form).unbind(".autocomplete");
- })
- // JG: Show all data in cache.
- .bind("showAllInCache", function() {
- receiveData('', cache.load(''));
- });
-
- function selectCurrent() {
- var selected = select.selected();
- if( !selected )
- return false;
-
- var v = selected.result;
- previousValue = v;
-
- if ( options.multiple ) {
- var words = trimWords($input.val());
- if ( words.length > 1 ) {
- v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;
+ /**
+ * Attach paste event listener because paste may occur much later then keydown or even without a keydown at all
+ */
+ $elem.on('paste', function() {
+ self.activate();
+ });
+
+ /**
+ * Finish on blur event
+ * Use a timeout because instant blur gives race conditions
+ */
+ var onBlurFunction = function() {
+ self.deactivate(true);
+ }
+ $elem.blur(function() {
+ if (self.finishOnBlur_) {
+ self.finishTimeout_ = setTimeout(onBlurFunction, 200);
}
- v += options.multipleSeparator;
+ });
+ /**
+ * Catch a race condition on form submit
+ */
+ $elem.parents('form').on('submit', onBlurFunction);
+
+ };
+
+ /**
+ * Position output DOM elements
+ * @private
+ */
+ $.Autocompleter.prototype.position = function() {
+ var offset = this.dom.$elem.offset();
+ var height = this.dom.$results.outerHeight();
+ var totalHeight = $(window).outerHeight();
+ var inputBottom = offset.top + this.dom.$elem.outerHeight();
+ var bottomIfDown = inputBottom + height;
+ // Set autocomplete results at the bottom of input
+ var position = {top: inputBottom, left: offset.left};
+ if (bottomIfDown > totalHeight) {
+ // Try to set autocomplete results at the top of input
+ var topIfUp = offset.top - height;
+ if (topIfUp >= 0) {
+ position.top = topIfUp;
+ }
}
-
- $input.val(v);
- hideResultsNow();
- $input.trigger("result", [selected.data, selected.value]);
- return true;
- }
-
- function onChange(crap, skipPrevCheck) {
- if( lastKeyPressCode == KEY.DEL ) {
- select.hide();
- return;
+ this.dom.$results.css(position);
+ };
+
+ /**
+ * Read from cache
+ * @private
+ */
+ $.Autocompleter.prototype.cacheRead = function(filter) {
+ var filterLength, searchLength, search, maxPos, pos;
+ if (this.options.useCache) {
+ filter = String(filter);
+ filterLength = filter.length;
+ if (this.options.matchSubset) {
+ searchLength = 1;
+ } else {
+ searchLength = filterLength;
+ }
+ while (searchLength <= filterLength) {
+ if (this.options.matchInside) {
+ maxPos = filterLength - searchLength;
+ } else {
+ maxPos = 0;
+ }
+ pos = 0;
+ while (pos <= maxPos) {
+ search = filter.substr(0, searchLength);
+ if (this.cacheData_[search] !== undefined) {
+ return this.cacheData_[search];
+ }
+ pos++;
+ }
+ searchLength++;
+ }
}
+ return false;
+ };
- var currentValue = $input.val();
-
- if ( !skipPrevCheck && currentValue == previousValue )
- return;
-
- previousValue = currentValue;
-
- currentValue = lastWord(currentValue);
- if ( currentValue.length >= options.minChars) {
- $input.addClass(options.loadingClass);
- if (!options.matchCase)
- currentValue = currentValue.toLowerCase();
- request(currentValue, receiveData, hideResultsNow);
- } else {
- stopLoading();
- select.hide();
+ /**
+ * Write to cache
+ * @private
+ */
+ $.Autocompleter.prototype.cacheWrite = function(filter, data) {
+ if (this.options.useCache) {
+ if (this.cacheLength_ >= this.options.maxCacheLength) {
+ this.cacheFlush();
+ }
+ filter = String(filter);
+ if (this.cacheData_[filter] !== undefined) {
+ this.cacheLength_++;
+ }
+ this.cacheData_[filter] = data;
+ return this.cacheData_[filter];
}
+ return false;
};
-
- function trimWords(value) {
- if ( !value ) {
- return [""];
+
+ /**
+ * Flush cache
+ * @public
+ */
+ $.Autocompleter.prototype.cacheFlush = function() {
+ this.cacheData_ = {};
+ this.cacheLength_ = 0;
+ };
+
+ /**
+ * Call hook
+ * Note that all called hooks are passed the autocompleter object
+ * @param {string} hook
+ * @param data
+ * @returns Result of called hook, false if hook is undefined
+ */
+ $.Autocompleter.prototype.callHook = function(hook, data) {
+ var f = this.options[hook];
+ if (f && $.isFunction(f)) {
+ return f(data, this);
}
- var words = value.split( options.multipleSeparator );
- var result = [];
- $.each(words, function(i, value) {
- if ( $.trim(value) )
- result[i] = $.trim(value);
- });
- return result;
- }
-
- function lastWord(value) {
- if ( !options.multiple )
- return value;
- var words = trimWords(value);
- return words[words.length - 1];
- }
-
- // fills in the input box w/the first match (assumed to be the best match)
- // q: the term entered
- // sValue: the first matching result
- function autoFill(q, sValue){
- // autofill in the complete box w/the first match as long as the user hasn't entered in more data
- // if the last user key pressed was backspace, don't autofill
- if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
- // fill in the value (keep the case the user has typed)
- $input.val($input.val() + sValue.substring(lastWord(previousValue).length));
- // select the portion of the value not typed by the user (so the next character will erase)
- $.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);
+ return false;
+ };
+
+ /**
+ * Set timeout to activate autocompleter
+ */
+ $.Autocompleter.prototype.activate = function() {
+ if (!this.options.enabled) return;
+ var self = this;
+ if (this.keyTimeout_) {
+ clearTimeout(this.keyTimeout_);
+ }
+ this.keyTimeout_ = setTimeout(function() {
+ self.activateNow();
+ }, this.options.delay);
+ };
+
+ /**
+ * Activate autocompleter immediately
+ */
+ $.Autocompleter.prototype.activateNow = function() {
+ var value = this.beforeUseConverter(this.dom.$elem.val());
+ if (value !== this.lastProcessedValue_ && value !== this.lastSelectedValue_) {
+ this.fetchData(value);
}
};
- function hideResults() {
- clearTimeout(timeout);
- timeout = setTimeout(hideResultsNow, 200);
- };
-
- function hideResultsNow() {
- var wasVisible = select.visible();
- select.hide();
- clearTimeout(timeout);
- stopLoading();
- if (options.mustMatch) {
- // call search and run callback
- $input.search(
- function (result){
- // if no value found, clear the input box
- if( !result ) {
- if (options.multiple) {
- var words = trimWords($input.val()).slice(0, -1);
- $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
- }
- else
- $input.val( "" );
- }
- }
- );
- }
- if (wasVisible)
- // position cursor at end of input field
- $.Autocompleter.Selection(input, input.value.length, input.value.length);
- };
-
- function receiveData(q, data) {
- if ( data && data.length && hasFocus ) {
- stopLoading();
- select.display(data, q);
- autoFill(q, data[0].value);
- select.show();
+ /**
+ * Get autocomplete data for a given value
+ * @param {string} value Value to base autocompletion on
+ * @private
+ */
+ $.Autocompleter.prototype.fetchData = function(value) {
+ var self = this;
+ var processResults = function(results, filter) {
+ if (self.options.processData) {
+ results = self.options.processData(results);
+ }
+ self.showResults(self.filterResults(results, filter), filter);
+ };
+ this.lastProcessedValue_ = value;
+ if (value.length < this.options.minChars) {
+ processResults([], value);
+ } else if (this.options.data) {
+ processResults(this.options.data, value);
} else {
- hideResultsNow();
+ this.fetchRemoteData(value, function(remoteData) {
+ processResults(remoteData, value);
+ });
}
};
- function request(term, success, failure) {
- if (!options.matchCase)
- term = term.toLowerCase();
- var data = cache.load(term);
-
- // JG: hack: if term ends with ':', kill data to force an ajax request.
- if (term.endsWith(":"))
- data = null;
-
- // recieve the cached data
- if (data && data.length) {
- success(term, data);
- // if an AJAX url has been supplied, try loading the data now
- } else if( (typeof options.url == "string") && (options.url.length > 0) ){
- var extraParams = {
- timestamp: +new Date()
+ /**
+ * Get remote autocomplete data for a given value
+ * @param {string} filter The filter to base remote data on
+ * @param {function} callback The function to call after data retrieval
+ * @private
+ */
+ $.Autocompleter.prototype.fetchRemoteData = function(filter, callback) {
+ var data = this.cacheRead(filter);
+ if (data) {
+ callback(data);
+ } else {
+ var self = this;
+ var dataType = self.options.remoteDataType === 'json' ? 'json' : 'text';
+ var ajaxCallback = function(data) {
+ var parsed = false;
+ if (data !== false) {
+ parsed = self.parseRemoteData(data);
+ self.cacheWrite(filter, parsed);
+ }
+ self.dom.$elem.removeClass(self.options.loadingClass);
+ callback(parsed);
};
- $.each(options.extraParams, function(key, param) {
- extraParams[key] = typeof param == "function" ? param() : param;
+ this.dom.$elem.addClass(this.options.loadingClass);
+ $.ajax({
+ url: this.makeUrl(filter),
+ success: ajaxCallback,
+ error: function(jqXHR, textStatus, errorThrown) {
+ if($.isFunction(self.options.onError)) {
+ self.options.onError(jqXHR, textStatus, errorThrown);
+ } else {
+ ajaxCallback(false);
+ }
+ },
+ dataType: dataType
});
-
- $.ajax({
- // try to leverage ajaxQueue plugin to abort previous requests
- mode: "abort",
- // limit abortion to this input
- port: "autocomplete" + input.name,
- dataType: options.dataType,
- url: options.url,
- data: $.extend({
- q: lastWord(term),
- limit: options.max
- }, extraParams),
- success: function(data) {
- var parsed = options.parse && options.parse(data) || parse(data);
- cache.add(term, parsed);
- success(term, parsed);
- }
- });
- } else {
- // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
- select.emptyList();
- failure(term);
}
};
-
- function parse(data) {
- var parsed = [];
- var rows = data.split("\n");
- for (var i=0; i < rows.length; i++) {
- var row = $.trim(rows[i]);
- if (row) {
- row = row.split("|");
- parsed[parsed.length] = {
- data: row,
- value: row[0],
- result: options.formatResult && options.formatResult(row, row[0]) || row[0]
- };
+
+ /**
+ * Create or update an extra parameter for the remote request
+ * @param {string} name Parameter name
+ * @param {string} value Parameter value
+ * @public
+ */
+ $.Autocompleter.prototype.setExtraParam = function(name, value) {
+ var index = $.trim(String(name));
+ if (index) {
+ if (!this.options.extraParams) {
+ this.options.extraParams = {};
+ }
+ if (this.options.extraParams[index] !== value) {
+ this.options.extraParams[index] = value;
+ this.cacheFlush();
}
}
- return parsed;
+
+ return this;
};
- function stopLoading() {
- $input.removeClass(options.loadingClass);
+ /**
+ * Build the url for a remote request
+ * If options.queryParamName === false, append query to url instead of using a GET parameter
+ * @param {string} param The value parameter to pass to the backend
+ * @returns {string} The finished url with parameters
+ */
+ $.Autocompleter.prototype.makeUrl = function(param) {
+ var self = this;
+ var url = this.options.url;
+ var params = $.extend({}, this.options.extraParams);
+
+ if (this.options.queryParamName === false) {
+ url += encodeURIComponent(param);
+ } else {
+ params[this.options.queryParamName] = param;
+ }
+
+ return makeUrl(url, params);
};
-};
+ /**
+ * Parse data received from server
+ * @param remoteData Data received from remote server
+ * @returns {array} Parsed data
+ */
+ $.Autocompleter.prototype.parseRemoteData = function(remoteData) {
+ var remoteDataType;
+ var data = remoteData;
+ if (this.options.remoteDataType === 'json') {
+ remoteDataType = typeof(remoteData);
+ switch (remoteDataType) {
+ case 'object':
+ data = remoteData;
+ break;
+ case 'string':
+ data = $.parseJSON(remoteData);
+ break;
+ default:
+ throw new Error("Unexpected remote data type: " + remoteDataType);
+ }
+ return data;
+ }
+ return plainTextParser(data, this.options.lineSeparator, this.options.cellSeparator);
+ };
-$.Autocompleter.defaults = {
- inputClass: "ac_input",
- resultsClass: "ac_results",
- loadingClass: "ac_loading",
- minChars: 1,
- delay: 400,
- matchCase: false,
- matchSubset: true,
- matchContains: false,
- cacheLength: 10,
- max: 100,
- mustMatch: false,
- extraParams: {},
- selectFirst: true,
- formatItem: function(row) { return row[0]; },
- formatMatch: null,
- autoFill: false,
- width: 0,
- multiple: false,
- multipleSeparator: ", ",
- highlight: function(value, term) {
- // JG: short-circuit highlighting if term is empty string.
- if (term == "")
- return value;
- return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
- },
- scroll: true,
- scrollHeight: 180
-};
+ /**
+ * Default filter for results
+ * @param {Object} result
+ * @param {String} filter
+ * @returns {boolean} Include this result
+ * @private
+ */
+ $.Autocompleter.prototype.defaultFilter = function(result, filter) {
+ if (!result.value) {
+ return false;
+ }
+ if (this.options.filterResults) {
+ var pattern = this.matchStringConverter(filter);
+ var testValue = this.matchStringConverter(result.value);
+ if (!this.options.matchCase) {
+ pattern = pattern.toLowerCase();
+ testValue = testValue.toLowerCase();
+ }
+ var patternIndex = testValue.indexOf(pattern);
+ if (this.options.matchInside) {
+ return patternIndex > -1;
+ } else {
+ return patternIndex === 0;
+ }
+ }
+ return true;
+ };
-$.Autocompleter.Cache = function(options) {
+ /**
+ * Filter result
+ * @param {Object} result
+ * @param {String} filter
+ * @returns {boolean} Include this result
+ * @private
+ */
+ $.Autocompleter.prototype.filterResult = function(result, filter) {
+ // No filter
+ if (this.options.filter === false) {
+ return true;
+ }
+ // Custom filter
+ if ($.isFunction(this.options.filter)) {
+ return this.options.filter(result, filter);
+ }
+ // Default filter
+ return this.defaultFilter(result, filter);
+ };
- var data = {};
- var length = 0;
+ /**
+ * Filter results
+ * @param results
+ * @param filter
+ */
+ $.Autocompleter.prototype.filterResults = function(results, filter) {
+ var filtered = [];
+ var i, result;
- function matchSubset(s, sub) {
- if (!options.matchCase)
- s = s.toLowerCase();
- var i = s.indexOf(sub);
- if (i == -1) return false;
- return i == 0 || options.matchContains;
+ for (i = 0; i < results.length; i++) {
+ result = sanitizeResult(results[i]);
+ if (this.filterResult(result, filter)) {
+ filtered.push(result);
+ }
+ }
+ if (this.options.sortResults) {
+ filtered = this.sortResults(filtered, filter);
+ }
+ if (this.options.maxItemsToShow > 0 && this.options.maxItemsToShow < filtered.length) {
+ filtered.length = this.options.maxItemsToShow;
+ }
+ return filtered;
};
-
- function add(q, value) {
- if (length > options.cacheLength){
- flush();
+
+ /**
+ * Sort results
+ * @param results
+ * @param filter
+ */
+ $.Autocompleter.prototype.sortResults = function(results, filter) {
+ var self = this;
+ var sortFunction = this.options.sortFunction;
+ if (!$.isFunction(sortFunction)) {
+ sortFunction = function(a, b, f) {
+ return sortValueAlpha(a, b, self.options.matchCase);
+ };
}
- if (!data[q]){
- length++;
+ results.sort(function(a, b) {
+ return sortFunction(a, b, filter, self.options);
+ });
+ return results;
+ };
+
+ /**
+ * Convert string before matching
+ * @param s
+ * @param a
+ * @param b
+ */
+ $.Autocompleter.prototype.matchStringConverter = function(s, a, b) {
+ var converter = this.options.matchStringConverter;
+ if ($.isFunction(converter)) {
+ s = converter(s, a, b);
}
- data[q] = value;
- }
-
- function populate(){
- if( !options.data ) return false;
- // track the matches
- var stMatchSets = {},
- nullData = 0;
+ return s;
+ };
- // no url was specified, we need to adjust the cache length to make sure it fits the local data store
- if( !options.url ) options.cacheLength = 1;
-
- // track all options for minChars = 0
- stMatchSets[""] = [];
-
- // loop through the array and create a lookup structure
- for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
- var rawValue = options.data[i];
- // if rawValue is a string, make an array otherwise just reference the array
- rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
-
- var value = options.formatMatch(rawValue, i+1, options.data.length);
- if ( value === false )
- continue;
-
- if( !stMatchSets[value] ) {
- stMatchSets[value] = [];
+ /**
+ * Convert string before use
+ * @param {String} s
+ */
+ $.Autocompleter.prototype.beforeUseConverter = function(s) {
+ s = this.getValue(s);
+ var converter = this.options.beforeUseConverter;
+ if ($.isFunction(converter)) {
+ s = converter(s);
+ }
+ return s;
+ };
+
+ /**
+ * Enable finish on blur event
+ */
+ $.Autocompleter.prototype.enableFinishOnBlur = function() {
+ this.finishOnBlur_ = true;
+ };
+
+ /**
+ * Disable finish on blur event
+ */
+ $.Autocompleter.prototype.disableFinishOnBlur = function() {
+ this.finishOnBlur_ = false;
+ };
+
+ /**
+ * Create a results item (LI element) from a result
+ * @param result
+ */
+ $.Autocompleter.prototype.createItemFromResult = function(result) {
+ var self = this;
+ var $li = $('<li/>');
+ $li.html(this.showResult(result.value, result.data));
+ $li.data({value: result.value, data: result.data})
+ .click(function() {
+ self.selectItem($li);
+ })
+ .mousedown(self.disableFinishOnBlur)
+ .mouseup(self.enableFinishOnBlur)
+ ;
+ return $li;
+ };
+
+ /**
+ * Get all items from the results list
+ * @param result
+ */
+ $.Autocompleter.prototype.getItems = function() {
+ return $('>ul>li', this.dom.$results);
+ };
+
+ /**
+ * Show all results
+ * @param results
+ * @param filter
+ */
+ $.Autocompleter.prototype.showResults = function(results, filter) {
+ var numResults = results.length;
+ var self = this;
+ var $ul = $('<ul></ul>');
+ var i, result, $li, autoWidth, first = false, $first = false;
+
+ if (numResults) {
+ for (i = 0; i < numResults; i++) {
+ result = results[i];
+ $li = this.createItemFromResult(result);
+ $ul.append($li);
+ if (first === false) {
+ first = String(result.value);
+ $first = $li;
+ $li.addClass(this.options.firstItemClass);
+ }
+ if (i === numResults - 1) {
+ $li.addClass(this.options.lastItemClass);
+ }
}
- // if the match is a string
- var row = {
- value: value,
- data: rawValue,
- result: options.formatResult && options.formatResult(rawValue) || value
- };
-
- // push the current match into the set list
- stMatchSets[value].push(row);
+ this.dom.$results.html($ul).show();
- // keep track of minChars zero items
- if ( nullData++ < options.max ) {
- stMatchSets[""].push(row);
+ // Always recalculate position since window size or
+ // input element location may have changed.
+ this.position();
+ if (this.options.autoWidth) {
+ autoWidth = this.dom.$elem.outerWidth() - this.dom.$results.outerWidth() + this.dom.$results.width();
+ this.dom.$results.css(this.options.autoWidth, autoWidth);
}
- };
-
- // add the data items to the cache
- $.each(stMatchSets, function(i, value) {
- // increase the cache size
- options.cacheLength++;
- // add to the cache
- add(i, value);
- });
- }
-
- // populate any existing data
- setTimeout(populate, 25);
-
- function flush(){
- data = {};
- length = 0;
- }
-
- return {
- flush: flush,
- add: add,
- populate: populate,
- load: function(q) {
- if (!options.cacheLength || !length)
- return null;
-
- /*
- * if dealing w/local data and matchContains than we must make sure
- * to loop through all the data collections looking for matches
- */
- if( !options.url && options.matchContains ) {
- // track all matches
- var csub = [];
- // loop through all the data grids for matches
- for( var k in data ) {
- // don't search through the stMatchSets[""] (minChars: 0) cache
- // this prevents duplicates
- if( k.length > 0 ){
- var c = data[k];
- $.each(c, function(i, x) {
- // if we've got a match, add it to the array
- if (matchSubset(x.value, q)) {
- csub.push(x);
- }
- });
- }
- }
- return csub;
- } else
- // if the exact item exists, use it
- if (data[q]) {
- return data[q];
- } else
- if (options.matchSubset) {
- for (var i = q.length - 1; i >= options.minChars; i--) {
- var c = data[q.substr(0, i)];
- if (c) {
- var csub = [];
- $.each(c, function(i, x) {
- if ( (x.data.indexOf("#Header") == 0) ||
- (matchSubset(x.value, q)) ) {
- csub[csub.length] = x;
- }
- });
- return csub;
- }
- }
-
+ this.getItems().hover(
+ function() { self.focusItem(this); },
+ function() { /* void */ }
+ );
+ if (this.autoFill(first, filter) || this.options.selectFirst || (this.options.selectOnly && numResults === 1)) {
+ this.focusItem($first);
}
- return null;
+ this.active_ = true;
+ } else {
+ this.hideResults();
+ this.active_ = false;
}
};
-};
-$.Autocompleter.Select = function (options, input, select, config) {
- var CLASSES = {
- ACTIVE: "ac_over"
+ $.Autocompleter.prototype.showResult = function(value, data) {
+ if ($.isFunction(this.options.showResult)) {
+ return this.options.showResult(value, data);
+ } else {
+ return $('<p></p>').text(value).html();
+ }
};
-
- var listItems,
- active = -1,
- data,
- term = "",
- needsInit = true,
- element,
- list;
-
- // Create results
- function init() {
- if (!needsInit)
- return;
-
- element = $("<div/>")
- .hide()
- .addClass(options.resultsClass)
- .css("position", "absolute")
- .bind("mouseleave", function() {
- element.hide();
- })
- .appendTo(document.body);
- list = $("<ul/>").appendTo(element).mouseover( function(event) {
- if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
- active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
- // JG: Only add active class if target is not a header.
- if (!headerAtPosition(active))
- $(target(event)).addClass(CLASSES.ACTIVE);
+ $.Autocompleter.prototype.autoFill = function(value, filter) {
+ var lcValue, lcFilter, valueLength, filterLength;
+ if (this.options.autoFill && this.lastKeyPressed_ !== 8) {
+ lcValue = String(value).toLowerCase();
+ lcFilter = String(filter).toLowerCase();
+ valueLength = value.length;
+ filterLength = filter.length;
+ if (lcValue.substr(0, filterLength) === lcFilter) {
+ var d = this.getDelimiterOffsets();
+ var pad = d.start ? ' ' : ''; // if there is a preceding delimiter
+ this.setValue( pad + value );
+ var start = filterLength + d.start + pad.length;
+ var end = valueLength + d.start + pad.length;
+ this.selectRange(start, end);
+ return true;
}
- }).click(function(event) {
- // JG: Ignore click on header.
- active = $("li", list).index(target(event));
- if (headerAtPosition(active))
- return;
+ }
+ return false;
+ };
- // Handle click on autocomplete options.
- $(target(event)).addClass(CLASSES.ACTIVE);
- select();
- // TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
- input.focus();
- return false;
- }).mousedown(function() {
- config.mouseDownOnSelect = true;
- }).mouseup(function() {
- config.mouseDownOnSelect = false;
- });
+ $.Autocompleter.prototype.focusNext = function() {
+ this.focusMove(+1);
+ };
- if( options.width > 0 )
- element.css("width", options.width);
+ $.Autocompleter.prototype.focusPrev = function() {
+ this.focusMove(-1);
+ };
- needsInit = false;
- }
-
- function target(event) {
- var element = event.target;
- while(element && element.tagName != "LI")
- element = element.parentNode;
- // more fun with IE, sometimes event.target is empty, just ignore it then
- if(!element)
- return [];
- return element;
- }
+ $.Autocompleter.prototype.focusMove = function(modifier) {
+ var $items = this.getItems();
+ modifier = sanitizeInteger(modifier, 0);
+ if (modifier) {
+ for (var i = 0; i < $items.length; i++) {
+ if ($($items[i]).hasClass(this.selectClass_)) {
+ this.focusItem(i + modifier);
+ return;
+ }
+ }
+ }
+ this.focusItem(0);
+ };
- // JG: Returns true iff there is a header element at the given position.
- function headerAtPosition(position) {
- dataAtPosition = data[position].data;
- return (dataAtPosition[0].indexOf("#Header") == 0);
- }
-
- function moveSelect(step) {
- listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
- // JG: while active item is a header, continue stepping.
- var isHeader = false;
- do {
- movePosition(step);
- isHeader = headerAtPosition(active);
- }
- while (isHeader);
- var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
-
- if(options.scroll) {
- var offset = 0;
- listItems.slice(0, active).each(function() {
- offset += this.offsetHeight;
- });
- if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
- list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
- } else if(offset < list.scrollTop()) {
- list.scrollTop(offset);
+ $.Autocompleter.prototype.focusItem = function(item) {
+ var $item, $items = this.getItems();
+ if ($items.length) {
+ $items.removeClass(this.selectClass_).removeClass(this.options.selectClass);
+ if (typeof item === 'number') {
+ if (item < 0) {
+ item = 0;
+ } else if (item >= $items.length) {
+ item = $items.length - 1;
+ }
+ $item = $($items[item]);
+ } else {
+ $item = $(item);
+ }
+ if ($item) {
+ $item.addClass(this.selectClass_).addClass(this.options.selectClass);
}
}
};
-
- function movePosition(step) {
- active += step;
- if (active < 0) {
- active = listItems.size() - 1;
- } else if (active >= listItems.size()) {
- active = 0;
- }
- }
-
- function limitNumberOfItems(available) {
- return options.max && options.max < available
- ? options.max
- : available;
- }
-
- function fillList() {
- list.empty();
- var max = limitNumberOfItems(data.length);
- for (var i=0; i < max; i++) {
- if (!data[i])
- continue;
-
- var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
- if ( formatted === false )
- continue;
- // JG: Build list item by formatting the item and choosing a CSS class.
- if (headerAtPosition(i)) {
- // Found header element; only add header if there are subsequent elements.
- if (i != max-1)
- var li = $("<li/>").html(data[i].data[1]).addClass("ac_header").appendTo(list)[0];
- } else {
- // Found completion element.
- var li = $("<li/>").html(options.highlight(formatted, term)).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
- }
-
- $.data(li, "ac_data", data[i]);
- }
- listItems = list.find("li");
- if ( options.selectFirst ) {
- listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
- active = 0;
- }
- // apply bgiframe if available
- if ( $.fn.bgiframe )
- list.bgiframe();
- }
-
- return {
- display: function(d, q) {
- init();
- data = d;
- term = q;
- fillList();
- },
- next: function() {
- moveSelect(1);
- },
- prev: function() {
- moveSelect(-1);
- },
- pageUp: function() {
- if (active != 0 && active - 8 < 0) {
- moveSelect( -active );
- } else {
- moveSelect(-8);
- }
- },
- pageDown: function() {
- if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
- moveSelect( listItems.size() - 1 - active );
- } else {
- moveSelect(8);
- }
- },
- hide: function() {
- element && element.hide();
- listItems && listItems.removeClass(CLASSES.ACTIVE);
- active = -1;
- },
- visible : function() {
- return element && element.is(":visible");
- },
- current: function() {
- return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
- },
- show: function() {
- var offset = $(input).offset();
- element.css({
- width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
- top: offset.top + input.offsetHeight,
- left: offset.left
- }).show();
- if(options.scroll) {
- list.scrollTop(0);
- list.css({
- maxHeight: options.scrollHeight,
- overflow: 'auto'
- });
-
- if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
- var listHeight = 0;
- listItems.each(function() {
- listHeight += this.offsetHeight;
- });
- var scrollbarsVisible = listHeight > options.scrollHeight;
- list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
- if (!scrollbarsVisible) {
- // IE doesn't recalculate width when scrollbar disappears
- listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
- }
- }
-
- }
- },
- selected: function() {
- var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
- return selected && selected.length && $.data(selected[0], "ac_data");
- },
- emptyList: function (){
- list && list.empty();
- },
- unbind: function() {
- element && element.remove();
+ $.Autocompleter.prototype.selectCurrent = function() {
+ var $item = $('li.' + this.selectClass_, this.dom.$results);
+ if ($item.length === 1) {
+ this.selectItem($item);
+ } else {
+ this.deactivate(false);
}
};
-};
-$.Autocompleter.Selection = function(field, start, end) {
- if( field.createTextRange ){
- var selRange = field.createTextRange();
- selRange.collapse(true);
- selRange.moveStart("character", start);
- selRange.moveEnd("character", end);
- selRange.select();
- } else if( field.setSelectionRange ){
- field.setSelectionRange(start, end);
- } else {
- if( field.selectionStart ){
- field.selectionStart = start;
- field.selectionEnd = end;
+ $.Autocompleter.prototype.selectItem = function($li) {
+ var value = $li.data('value');
+ var data = $li.data('data');
+ var displayValue = this.displayValue(value, data);
+ var processedDisplayValue = this.beforeUseConverter(displayValue);
+ this.lastProcessedValue_ = processedDisplayValue;
+ this.lastSelectedValue_ = processedDisplayValue;
+ var d = this.getDelimiterOffsets();
+ var delimiter = this.options.delimiterChar;
+ var elem = this.dom.$elem;
+ var extraCaretPos = 0;
+ if ( this.options.useDelimiter ) {
+ // if there is a preceding delimiter, add a space after the delimiter
+ if ( elem.val().substring(d.start-1, d.start) == delimiter && delimiter != ' ' ) {
+ displayValue = ' ' + displayValue;
+ }
+ // if there is not already a delimiter trailing this value, add it
+ if ( elem.val().substring(d.end, d.end+1) != delimiter && this.lastKeyPressed_ != this.options.delimiterKeyCode ) {
+ displayValue = displayValue + delimiter;
+ } else {
+ // move the cursor after the existing trailing delimiter
+ extraCaretPos = 1;
+ }
}
- }
- field.focus();
-};
+ this.setValue(displayValue);
+ this.setCaret(d.start + displayValue.length + extraCaretPos);
+ this.callHook('onItemSelect', { value: value, data: data });
+ this.deactivate(true);
+ elem.focus();
+ };
-})(jQuery);
+ $.Autocompleter.prototype.displayValue = function(value, data) {
+ if ($.isFunction(this.options.displayValue)) {
+ return this.options.displayValue(value, data);
+ }
+ return value;
+ };
+
+ $.Autocompleter.prototype.hideResults = function() {
+ this.dom.$results.hide();
+ };
+
+ $.Autocompleter.prototype.deactivate = function(finish) {
+ if (this.finishTimeout_) {
+ clearTimeout(this.finishTimeout_);
+ }
+ if (this.keyTimeout_) {
+ clearTimeout(this.keyTimeout_);
+ }
+ if (finish) {
+ if (this.lastProcessedValue_ !== this.lastSelectedValue_) {
+ if (this.options.mustMatch) {
+ this.setValue('');
+ }
+ this.callHook('onNoMatch');
+ }
+ if (this.active_) {
+ this.callHook('onFinish');
+ }
+ this.lastKeyPressed_ = null;
+ this.lastProcessedValue_ = null;
+ this.lastSelectedValue_ = null;
+ this.active_ = false;
+ }
+ this.hideResults();
+ };
+
+ $.Autocompleter.prototype.selectRange = function(start, end) {
+ var input = this.dom.$elem.get(0);
+ if (input.setSelectionRange) {
+ input.focus();
+ input.setSelectionRange(start, end);
+ } else if (input.createTextRange) {
+ var range = input.createTextRange();
+ range.collapse(true);
+ range.moveEnd('character', end);
+ range.moveStart('character', start);
+ range.select();
+ }
+ };
+
+ /**
+ * Move caret to position
+ * @param {Number} pos
+ */
+ $.Autocompleter.prototype.setCaret = function(pos) {
+ this.selectRange(pos, pos);
+ };
+
+ /**
+ * Get caret position
+ */
+ $.Autocompleter.prototype.getCaret = function() {
+ var $elem = this.dom.$elem;
+ var elem = $elem[0];
+ var val, selection, range, start, end, stored_range;
+ if (elem.createTextRange) { // IE
+ selection = document.selection;
+ if (elem.tagName.toLowerCase() != 'textarea') {
+ val = $elem.val();
+ range = selection.createRange().duplicate();
+ range.moveEnd('character', val.length);
+ if (range.text === '') {
+ start = val.length;
+ } else {
+ start = val.lastIndexOf(range.text);
+ }
+ range = selection.createRange().duplicate();
+ range.moveStart('character', -val.length);
+ end = range.text.length;
+ } else {
+ range = selection.createRange();
+ stored_range = range.duplicate();
+ stored_range.moveToElementText(elem);
+ stored_range.setEndPoint('EndToEnd', range);
+ start = stored_range.text.length - range.text.length;
+ end = start + range.text.length;
+ }
+ } else {
+ start = $elem[0].selectionStart;
+ end = $elem[0].selectionEnd;
+ }
+ return {
+ start: start,
+ end: end
+ };
+ };
+
+ /**
+ * Set the value that is currently being autocompleted
+ * @param {String} value
+ */
+ $.Autocompleter.prototype.setValue = function(value) {
+ if ( this.options.useDelimiter ) {
+ // set the substring between the current delimiters
+ var val = this.dom.$elem.val();
+ var d = this.getDelimiterOffsets();
+ var preVal = val.substring(0, d.start);
+ var postVal = val.substring(d.end);
+ value = preVal + value + postVal;
+ }
+ this.dom.$elem.val(value);
+ };
+
+ /**
+ * Get the value currently being autocompleted
+ * @param {String} value
+ */
+ $.Autocompleter.prototype.getValue = function(value) {
+ if ( this.options.useDelimiter ) {
+ var d = this.getDelimiterOffsets();
+ return value.substring(d.start, d.end).trim();
+ } else {
+ return value;
+ }
+ };
+
+ /**
+ * Get the offsets of the value currently being autocompleted
+ */
+ $.Autocompleter.prototype.getDelimiterOffsets = function() {
+ var val = this.dom.$elem.val();
+ if ( this.options.useDelimiter ) {
+ var preCaretVal = val.substring(0, this.getCaret().start);
+ var start = preCaretVal.lastIndexOf(this.options.delimiterChar) + 1;
+ var postCaretVal = val.substring(this.getCaret().start);
+ var end = postCaretVal.indexOf(this.options.delimiterChar);
+ if ( end == -1 ) end = val.length;
+ end += this.getCaret().start;
+ } else {
+ start = 0;
+ end = val.length;
+ }
+ return {
+ start: start,
+ end: end
+ };
+ };
+
+})(jQuery);
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: DataProviders: add hierarchy module and xml provider, add provider to datatype
by commits-noreply@bitbucket.org 08 Aug '13
by commits-noreply@bitbucket.org 08 Aug '13
08 Aug '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/bf6bf3b5edfd/
Changeset: bf6bf3b5edfd
User: carlfeberhard
Date: 2013-08-08 18:59:34
Summary: DataProviders: add hierarchy module and xml provider, add provider to datatype
Affected #: 5 files
diff -r f46505803e8f0efd26be16f9e18353fbdc969205 -r bf6bf3b5edfdc409f235cb91edcc9d91246df816 lib/galaxy/datatypes/dataproviders/__init__.py
--- a/lib/galaxy/datatypes/dataproviders/__init__.py
+++ b/lib/galaxy/datatypes/dataproviders/__init__.py
@@ -23,6 +23,7 @@
import base
import chunk
import line
+import hierarchy
import column
import external
import dataset
diff -r f46505803e8f0efd26be16f9e18353fbdc969205 -r bf6bf3b5edfdc409f235cb91edcc9d91246df816 lib/galaxy/datatypes/dataproviders/decorators.py
--- a/lib/galaxy/datatypes/dataproviders/decorators.py
+++ b/lib/galaxy/datatypes/dataproviders/decorators.py
@@ -134,6 +134,7 @@
Parse the values in `query_kwargs` from strings to the proper types
listed in the same key in `settings`.
"""
+ #TODO: this was a relatively late addition: review and re-think
def list_from_query_string( s ):
# assume csv
return s.split( ',' )
@@ -155,10 +156,11 @@
#TODO: this would be the place to sanitize any strings
query_value = query_kwargs[ key ]
needed_type = settings[ key ]
- try:
- query_kwargs[ key ] = parsers[ needed_type ]( query_value )
- except ( KeyError, ValueError ):
- del query_kwargs[ key ]
+ if needed_type != 'str':
+ try:
+ query_kwargs[ key ] = parsers[ needed_type ]( query_value )
+ except ( KeyError, ValueError ):
+ del query_kwargs[ key ]
#TODO:?? do we want to remove query_kwarg entries NOT in settings?
return query_kwargs
diff -r f46505803e8f0efd26be16f9e18353fbdc969205 -r bf6bf3b5edfdc409f235cb91edcc9d91246df816 lib/galaxy/datatypes/dataproviders/hierarchy.py
--- /dev/null
+++ b/lib/galaxy/datatypes/dataproviders/hierarchy.py
@@ -0,0 +1,142 @@
+"""
+Dataproviders that iterate over lines from their sources.
+"""
+
+import line
+import xml.etree.ElementTree as elementtree
+
+_TODO = """
+"""
+
+import logging
+log = logging.getLogger( __name__ )
+
+
+# ----------------------------------------------------------------------------- hierarchal/tree data providers
+class HierarchalDataProvider( line.BlockDataProvider ):
+ """
+ Class that uses formats where a datum may have a parent or children
+ data.
+
+ e.g. XML, HTML, GFF3, Phylogenetic
+ """
+ def __init__( self, source, **kwargs ):
+ #TODO: (and defer to better (than I can write) parsers for each subtype)
+ super( HierarchalDataProvider, self ).__init__( source, **kwargs )
+
+
+# ----------------------------------------------------------------------------- xml
+class XMLDataProvider( HierarchalDataProvider ):
+ """
+ Data provider that converts selected XML elements to dictionaries.
+ """
+ # using elementtree's iterparse method to keep mem down
+ #TODO: this, however (AFAIK), prevents the use of xpath
+ settings = {
+ 'selector' : 'str', #urlencoded
+ 'max_depth' : 'int',
+ }
+ ITERPARSE_ALL_EVENTS = ( 'start', 'end', 'start-ns', 'end-ns' )
+ #TODO: move appropo into super
+
+ def __init__( self, source, selector=None, max_depth=None, **kwargs ):
+ """
+ :param selector: some partial string in the desired tags to return
+ :param max_depth: the number of generations of descendents to return
+ """
+ self.selector = selector
+ self.max_depth = max_depth
+ self.namespaces = {}
+
+ super( XMLDataProvider, self ).__init__( source, **kwargs )
+
+ def matches_selector( self, element, selector=None ):
+ """
+ Returns true if the ``element`` matches the ``selector``.
+
+ :param element: an XML ``ElementTree.Element``
+ :param selector: some partial string in the desired tags to return
+
+ Change point for more sophisticated selectors.
+ """
+ # search for partial match of selector to the element tag
+ #TODO: add more flexibility here w/o re-implementing xpath
+ #TODO: fails with '#' - browser thinks it's anchor - use urlencode
+ #TODO: need removal/replacement of etree namespacing here - then move to string match
+ return bool( ( selector == None )
+ or ( isinstance( element, elementtree.Element ) and selector in element.tag ) )
+
+ def element_as_dict( self, element ):
+ """
+ Converts an XML element (its text, tag, and attributes) to dictionary form.
+
+ :param element: an XML ``ElementTree.Element``
+ """
+ #TODO: Key collision is unlikely here, but still should be better handled
+ return {
+ 'tag' : element.tag,
+ 'text' : element.text.strip() if element.text else None,
+ # needs shallow copy to protect v. element.clear()
+ 'attrib' : dict( element.attrib )
+ }
+
+ def get_children( self, element, max_depth=None ):
+ """
+ Yield all children of element (and their children - recursively)
+ in dictionary form.
+ :param element: an XML ``ElementTree.Element``
+ :param max_depth: the number of generations of descendents to return
+ """
+ if not isinstance( max_depth, int ) or max_depth >= 1:
+ for child in element.getchildren():
+ child_data = self.element_as_dict( child )
+
+ next_depth = max_depth - 1 if isinstance( max_depth, int ) else None
+ grand_children = list( self.get_children( child, next_depth ) )
+ if grand_children:
+ child_data[ 'children' ] = grand_children
+
+ yield child_data
+
+ def __iter__( self ):
+ context = elementtree.iterparse( self.source, events=self.ITERPARSE_ALL_EVENTS )
+ context = iter( context )
+
+ selected_element = None
+ for event, element in context:
+ #print 'iterparse, event:', event
+ #print 'iterparse, element:', element, ( element.tag if hasattr( element, 'tag' ) else '' )
+
+ if event == 'start-ns':
+ ns, uri = element
+ self.namespaces[ ns ] = uri
+
+ elif event == 'start':
+ if( ( selected_element == None )
+ and ( self.matches_selector( element, self.selector ) ) ):
+ # start tag of selected element - wait for 'end' to emit/yield
+ selected_element = element
+
+ elif event == 'end':
+ if( ( selected_element != None )
+ and ( element == selected_element ) ):
+ self.num_valid_data_read += 1
+
+ # offset
+ if self.num_valid_data_read > self.offset:
+ # convert to dict and yield
+ selected_element_dict = self.element_as_dict( selected_element )
+ children = list( self.get_children( selected_element, self.max_depth ) )
+ if children:
+ selected_element_dict[ 'children' ] = children
+ yield selected_element_dict
+
+ # limit
+ self.num_data_returned += 1
+ if self.limit is not None and self.num_data_returned >= self.limit:
+ break
+
+ selected_element.clear()
+ selected_element = None
+
+ self.num_data_read += 1
diff -r f46505803e8f0efd26be16f9e18353fbdc969205 -r bf6bf3b5edfdc409f235cb91edcc9d91246df816 lib/galaxy/datatypes/dataproviders/line.py
--- a/lib/galaxy/datatypes/dataproviders/line.py
+++ b/lib/galaxy/datatypes/dataproviders/line.py
@@ -132,7 +132,7 @@
e.g. Fasta, GenBank, MAF, hg log
Note: mem intensive (gathers list of lines before output)
"""
- def __init__( self, source, new_block_delim_fn, block_filter_fn=None, **kwargs ):
+ def __init__( self, source, new_block_delim_fn=None, block_filter_fn=None, **kwargs ):
"""
:param new_block_delim_fn: T/F function to determine whether a given line
is the start of a new block.
@@ -214,7 +214,7 @@
"""
if self.new_block_delim_fn:
return self.new_block_delim_fn( line )
- return False
+ return True
# NOTE:
# some formats have one block attr per line
@@ -251,17 +251,3 @@
if self.block_filter_fn:
return self.block_filter_fn( block )
return block
-
-
-# ----------------------------------------------------------------------------- hierarchal/tree data providers
-class HierarchalDataProvider( BlockDataProvider ):
- """
- Class that uses formats where a datum may have a parent or children
- data.
-
- e.g. XML, HTML, GFF3, Phylogenetic
- """
- def __init__( self, source, **kwargs ):
- #TODO: (and defer to better (than I can write) parsers for each subtype)
- raise NotImplementedError( 'Abstract class' )
- super( HierarchalDataProvider, self ).__init__( source, **kwargs )
diff -r f46505803e8f0efd26be16f9e18353fbdc969205 -r bf6bf3b5edfdc409f235cb91edcc9d91246df816 lib/galaxy/datatypes/xml.py
--- a/lib/galaxy/datatypes/xml.py
+++ b/lib/galaxy/datatypes/xml.py
@@ -4,9 +4,11 @@
import data
import logging
from galaxy.datatypes.sniff import *
+import dataproviders
log = logging.getLogger(__name__)
+(a)dataproviders.decorators.has_dataproviders
class GenericXml( data.Text ):
"""Base format class for any XML file."""
file_ext = "xml"
@@ -47,6 +49,12 @@
data.Text.merge(split_files, output_file)
merge = staticmethod(merge)
+ @dataproviders.decorators.dataprovider_factory( 'xml', dataproviders.hierarchy.XMLDataProvider.settings )
+ def xml_dataprovider( self, dataset, **settings ):
+ dataset_source = dataproviders.dataset.DatasetDataProvider( dataset )
+ return dataproviders.hierarchy.XMLDataProvider( dataset_source, **settings )
+
+
class MEMEXml( GenericXml ):
"""MEME XML Output data"""
file_ext = "memexml"
@@ -62,6 +70,7 @@
def sniff( self, filename ):
return False
+
class CisML( GenericXml ):
"""CisML XML data""" #see: http://www.ncbi.nlm.nih.gov/pubmed/15001475
file_ext = "cisml"
@@ -77,6 +86,7 @@
def sniff( self, filename ):
return False
+
class Phyloxml( GenericXml ):
"""Format for defining phyloxml data http://www.phyloxml.org/"""
file_ext = "phyloxml"
@@ -105,4 +115,4 @@
Returns a list of visualizations for datatype.
"""
- return [ 'phyloviz' ]
\ No newline at end of file
+ return [ 'phyloviz' ]
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/8d8429484ac3/
Changeset: 8d8429484ac3
Branch: next-stable
User: carlfeberhard
Date: 2013-08-08 16:40:27
Summary: History view: fix broken scrollbar
Affected #: 1 file
diff -r 8ee4176d44bad2c1029356e869d601752ae54cd0 -r 8d8429484ac3ffef21fedd1077acf050c8aed459 templates/webapps/galaxy/history/view.mako
--- a/templates/webapps/galaxy/history/view.mako
+++ b/templates/webapps/galaxy/history/view.mako
@@ -27,6 +27,7 @@
<style type="text/css">
+ /* these don't appear to be used? */
.page-body
{
padding: 10px;
@@ -44,20 +45,38 @@
border-top: 4px solid #DDDDDD;
}
+
+ body {
+ padding: 0px;
+ margin: 0px;
+ }
+
div.unified-panel-body {
- position: relative;
+ position: absolute;
top: 0px;
- width: auto;
+ width: 100%;
+ }
+
+ #history-name-area {
+ margin: 12px 0px 0px 16px;
+ font-size: 120%;
+ }
+ #top-links {
+ margin: 4px 0px 8px 16px;
}
.historyItemContainer {
- padding-right: 3px;
- border-right-style: solid;
- border-right-color: #66AA66;
+ /*padding-right: 3px;*/
}
.historyItemBody {
display: none;
}
+ div.historyItemWrapper {
+ margin: 0px 4px 0px 4px ;
+ border-left: 1px solid #999999;
+ border-right: 1px solid #999999;
+ }
+ /* TODO: unify with other history css and into .less */
</style><noscript>
@@ -92,14 +111,13 @@
href_to_user_histories = h.url_for( controller='/history', action='list_published' )##should this instead be be None or empty string?
%>
- %if context.get( 'use_panels' ):
- <div class="unified-panel-header" unselectable="on">
- </div>
- %endif
-
<div class="unified-panel-body">
- <div class="wrapping" style="overflow: auto; height: 100%;">
+ <div style="overflow: auto; height: 100%;">
## Render view of history.
+ <div id="history-name-area" class="historyLinks" style="color: gray; font-weight: bold; padding: 0px 0px 5px 0px">
+ <div id="history-name">${history.get_display_name()}</div>
+ </div>
+
<div id="top-links" class="historyLinks" style="padding: 0px 0px 5px 0px">
%if not history.purged:
<a href="${h.url_for(controller='history', action='imp', id=trans.security.encode_id(history.id) )}">import and start using history</a> |
@@ -113,10 +131,6 @@
<a href="#" class="toggle">collapse all</a></div>
- <div id="history-name-area" class="historyLinks" style="color: gray; font-weight: bold; padding: 0px 0px 5px 0px">
- <div id="history-name">${history.get_display_name()}</div>
- </div>
-
%if history.deleted:
<div class="warningmessagesmall">
${_('You are currently viewing a deleted history!')}
https://bitbucket.org/galaxy/galaxy-central/commits/f46505803e8f/
Changeset: f46505803e8f
User: carlfeberhard
Date: 2013-08-08 16:41:18
Summary: merge next-stable
Affected #: 2 files
diff -r 3f8f52fa01f807b624ce05ef2186284b947c2e34 -r f46505803e8f0efd26be16f9e18353fbdc969205 templates/webapps/galaxy/history/view.mako
--- a/templates/webapps/galaxy/history/view.mako
+++ b/templates/webapps/galaxy/history/view.mako
@@ -27,6 +27,7 @@
<style type="text/css">
+ /* these don't appear to be used? */
.page-body
{
padding: 10px;
@@ -44,20 +45,38 @@
border-top: 4px solid #DDDDDD;
}
+
+ body {
+ padding: 0px;
+ margin: 0px;
+ }
+
div.unified-panel-body {
- position: relative;
+ position: absolute;
top: 0px;
- width: auto;
+ width: 100%;
+ }
+
+ #history-name-area {
+ margin: 12px 0px 0px 16px;
+ font-size: 120%;
+ }
+ #top-links {
+ margin: 4px 0px 8px 16px;
}
.historyItemContainer {
- padding-right: 3px;
- border-right-style: solid;
- border-right-color: #66AA66;
+ /*padding-right: 3px;*/
}
.historyItemBody {
display: none;
}
+ div.historyItemWrapper {
+ margin: 0px 4px 0px 4px ;
+ border-left: 1px solid #999999;
+ border-right: 1px solid #999999;
+ }
+ /* TODO: unify with other history css and into .less */
</style><noscript>
@@ -92,14 +111,13 @@
href_to_user_histories = h.url_for( controller='/history', action='list_published' )##should this instead be be None or empty string?
%>
- %if context.get( 'use_panels' ):
- <div class="unified-panel-header" unselectable="on">
- </div>
- %endif
-
<div class="unified-panel-body">
- <div class="wrapping" style="overflow: auto; height: 100%;">
+ <div style="overflow: auto; height: 100%;">
## Render view of history.
+ <div id="history-name-area" class="historyLinks" style="color: gray; font-weight: bold; padding: 0px 0px 5px 0px">
+ <div id="history-name">${history.get_display_name()}</div>
+ </div>
+
<div id="top-links" class="historyLinks" style="padding: 0px 0px 5px 0px">
%if not history.purged:
<a href="${h.url_for(controller='history', action='imp', id=trans.security.encode_id(history.id) )}">import and start using history</a> |
@@ -113,10 +131,6 @@
<a href="#" class="toggle">collapse all</a></div>
- <div id="history-name-area" class="historyLinks" style="color: gray; font-weight: bold; padding: 0px 0px 5px 0px">
- <div id="history-name">${history.get_display_name()}</div>
- </div>
-
%if history.deleted:
<div class="warningmessagesmall">
${_('You are currently viewing a deleted history!')}
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: guerler: Fix precision formatting in Circster
by commits-noreply@bitbucket.org 08 Aug '13
by commits-noreply@bitbucket.org 08 Aug '13
08 Aug '13
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3f8f52fa01f8/
Changeset: 3f8f52fa01f8
User: guerler
Date: 2013-08-08 10:10:36
Summary: Fix precision formatting in Circster
Affected #: 1 file
diff -r ba8330a9fdefe0431ee1869e3a60c55caebc937c -r 3f8f52fa01f807b624ce05ef2186284b947c2e34 static/scripts/viz/circster.js
--- a/static/scripts/viz/circster.js
+++ b/static/scripts/viz/circster.js
@@ -85,10 +85,14 @@
*/
formatNum: function(num, sigDigits) {
// Use default of 2 sig. digits.
- if (sigDigits === undefined) {
+ if (sigDigits === undefined)
sigDigits = 2;
- }
-
+
+ // Verify input number
+ if (num === null)
+ return null;
+
+ // Calculate return value
var rval = null;
if (num < 1) {
rval = num.toPrecision(sigDigits);
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0