diff --git a/.hgignore b/.hgignore
index b1cb3ca9553..beab290cf6b 100644
--- a/.hgignore
+++ b/.hgignore
@@ -30,9 +30,9 @@ database/openid_consumer_cache
*.pyc
# Galaxy Runtime Files
-paster.lock
-paster.log
-paster.pid
+*.lock
+*.log
+*.pid
# Tool Shed Runtime Files
tool_shed_webapp.lock
@@ -108,12 +108,9 @@ tool-data/shared/jars/
# CSS build artifacts.
sprite-*.less
-# Local node_modules directories
+# Local node_modules and bower_components directories
node_modules
-
-# Testing
-selenium-server.jar
-selenium_results.html
+bower_components
# Documentation build files.
doc/build
diff --git a/.hgtags b/.hgtags
index 22ac92bb8d0..68d98974a64 100644
--- a/.hgtags
+++ b/.hgtags
@@ -8,16 +8,18 @@ a4113cc1cb5eaa68091c9a73375f00555b66dd11 release_2013.01.13
5e605ed6069fe4c5ca9875e95e91b2713499e8ca release_2014.02.10
9e53251b0b7e93b9563008a2b112f2e815a04bbc release_2014.04.14
7e257c7b10badb65772b1528cb61d58175a42e47 release_2014.06.02
-7a4d321c0e38fa263ea83d29a35a608c3181fcba latest_2014.06.02
-9661b9d5d5b330483ae3ad2236410e0efaa7c500 latest_2014.04.14
-6b0bd93038a843b1585155f0d63f0eea2459c70b latest_2013.01.13
-3e62060b14b9afc46f8e0ec02e1a4500d77db9e1 latest_2013.02.08
-425009b3ff4d8b67d2812253b221f3c4f4a8d1e3 latest_2013.04.01
-9713d86392ef985ffcdc39ff0c8ddf51a1f9ce47 latest_2013.06.03
-9ed84cd208e07e8985ec917cb025fcbbb09edcfb latest_2013.08.12
-81fbe25bd02edcd53065e8e4476dd1dfb5a72cf2 latest_2013.11.04
-2a756ca2cb1826db7796018e77d12e2dd7b67603 latest_2014.02.10
+9bce3f426863f8ba88062f67c7efc1836e82ee7c latest_2014.06.02
+8f9dcac033694e4cabcf5daae5cca1cfefbe967f latest_2014.04.14
+9c323aad4ffdd65a3deb06a4a36f6b2c5115a60f latest_2013.01.13
+b986c184be88947b5d1d90be7f36cfd2627dd938 latest_2013.02.08
+dec9431d66b837a208e2f060d90afd913c721227 latest_2013.04.01
+19e56e66b0b344c6e2afa4541f6988e4fdb9af29 latest_2013.06.03
+cee903b8b3eee9145627ee89742555dac581791e latest_2013.08.12
+7d5aa19a166cba9039e15f338a1e3fc924c43d3a latest_2013.11.04
+0c000cc2f9c05bf4c1c2bc3a10215014fd64e696 latest_2014.02.10
ca45b78adb4152fc6e7395514d46eba6b7d0b838 release_2014.08.11
-548ab24667d6206780237bd807f7d857a484c461 latest_2014.08.11
+f3fc4602e22b39468d780b955a1a05caf867a7e9 latest_2014.08.11
2092948937ac30ef82f71463a235c66d34987088 release_2014.10.06
-7086b87d83a9092cbece0fec6f3e3ed03266be0c latest_2014.10.06
+9bd6f8b5b8153db752f4e61ed62f2b6c01ae4a11 latest_2014.10.06
+2e8dd2949dd3eee0f56f9a3a5ebf1b2baca24aee release_2015.01.13
+4039bfd5584aac053f686197a76ac176253e6f3d latest_2015.01.13
diff --git a/client/GruntFile.js b/client/GruntFile.js
index 9d42d7dcb33..c8e08c11938 100644
--- a/client/GruntFile.js
+++ b/client/GruntFile.js
@@ -47,15 +47,77 @@ module.exports = function(grunt) {
options: {
spawn: false
}
+ },
+
+ // call bower to install libraries and other external resources
+ "bower-install-simple" : {
+ options: {
+ color: true
+ },
+ "prod": {
+ options: {
+ production: true
+ }
+ },
+ "dev": {
+ options: {
+ production: false
+ }
+ }
+ },
+ // where to move fetched bower components into the build structure (libName: [ bower-location, libs-location ])
+ libraryLocations : {
+ "jquery": [ "dist/jquery.js", "jquery/jquery.js" ],
+ "jquery-migrate": [ "jquery-migrate.js", "jquery/jquery.migrate.js" ],
+ "traceKit": [ "tracekit.js", "tracekit.js" ],
+ "ravenjs": [ "dist/raven.js", "raven.js" ],
+ "underscore": [ "underscore.js", "underscore.js" ],
+ "handlebars": [ "handlebars.runtime.js", "handlebars.runtime.js" ],
+ "lunr.js": [ "lunr.js", "lunr.js" ]
+ //"backbone": [ "backbone.js", "backbone/backbone.js" ],
+
+ // these need to be updated and tested
+ //"require": [ "build/require.js", "require.js" ],
+ //"d3": [ "d3.js", "d3.js" ],
+ //"farbtastic": [ "src/farbtastic.js", "farbtastic.js" ],
+ //"jQTouch": [ "src/reference/jqtouch.js", "jquery/jqtouch.js" ],
+ //"bib2json": [ "Parser.js", "bibtex.js" ],
+ //"jquery-form": [ "jquery.form.js", "jquery/jquery.form.js" ],
+ //"jquery-autocomplete": [ "src/jquery.autocomplete.js", "jquery/jquery.autocomplete.js" ],
+ //"select2": [ "select2.js", "jquery/select2.js" ],
+ //"jStorage": [ "jstorage.js", "jquery/jstorage.js" ],
+ //"jquery.cookie": [ "", "jquery/jquery.cookie.js" ],
+ //"dynatree": [ "dist/jquery.dynatree.js", "jquery/jquery.dynatree.js" ],
+ //"jquery-mousewheel": [ "jquery.mousewheel.js", "jquery/jquery.mousewheel.js" ],
+ //"jquery.event.drag-drop": [
+ // [ "event.drag/jquery.event.drag.js", "jquery/jquery.event.drag.js" ],
+ // [ "event.drag/jquery.event.drop.js", "jquery/jquery.event.drop.js" ]
+ //],
+
+ // these are complicated by additional css/less
+ //"toastr": [ "toastr.js", "toastr.js" ],
+ //"wymeditor": [ "dist/wymeditor/jquery.wymeditor.js", "jquery/jquery.wymeditor.js" ],
+ //"jstree": [ "jstree.js", "jquery/jstree.js" ],
+
+ // these have been customized by Galaxy
+ //"bootstrap": [ "dist/js/bootstrap.js", "bootstrap.js" ],
+ //"jquery-ui": [
+ // // multiple components now
+ // [ "", "jquery/jquery-ui.js" ]
+ //],
+
}
+
});
grunt.loadNpmTasks( 'grunt-contrib-watch' );
grunt.loadNpmTasks( 'grunt-contrib-copy');
- grunt.loadNpmTasks('grunt-exec');
+ grunt.loadNpmTasks( 'grunt-bower-install-simple');
+ grunt.loadNpmTasks( 'grunt-exec' );
grunt.registerTask( 'pack', [ 'exec' ] );
- grunt.registerTask( 'default', [ 'copy', 'pack' ] );
+ grunt.registerTask( 'default', [ 'copy:main', 'pack' ] );
+
// -------------------------------------------------------------------------- copy,pack only those changed
// adapted from grunt-contrib-watch jslint example
@@ -85,4 +147,25 @@ module.exports = function(grunt) {
onChange();
});
+ // -------------------------------------------------------------------------- fetch/update external libraries
+ /** copy external libraries from bower components to scripts/libs */
+ function copyLibs(){
+ var libraryLocations = grunt.config( 'libraryLocations' );
+ for( var libName in libraryLocations ){
+ if( libraryLocations.hasOwnProperty( libName ) ){
+
+ var BOWER_DIR = 'bower_components',
+ location = libraryLocations[ libName ],
+ source = [ BOWER_DIR, libName, location[0] ].join( '/' ),
+ destination = 'galaxy/scripts/libs/' + location[1];
+ grunt.log.writeln( source + ' -> ' + destination );
+ grunt.file.copy( source, destination );
+ }
+ }
+ }
+
+ grunt.registerTask( 'copy-libs', 'copy external libraries to src', copyLibs );
+ grunt.registerTask( 'install-libs', 'fetch external libraries and copy to src',
+ [ 'bower-install-simple:prod', 'copy-libs' ] );
+
};
diff --git a/client/bower.json b/client/bower.json
new file mode 100644
index 00000000000..173a855d922
--- /dev/null
+++ b/client/bower.json
@@ -0,0 +1,41 @@
+{
+ "name": "galaxy-client-libs",
+ "version": "0.0.0",
+ "description": "External client-side libraries used by Galaxy",
+ "keywords": [
+ "galaxy",
+ "galaxyproject"
+ ],
+ "homepage": "usegalaxy.org",
+ "dependencies": {
+ "jquery": "~1.11.1",
+ "traceKit": "*",
+ "ravenjs": "~1.1.16",
+ "require": "*",
+ "underscore": "~1.7.0",
+ "backbone": "~1.1.2",
+ "bootstrap": "~3.3.2",
+ "d3": "~3.5.3",
+ "farbtastic": "~2.0.0-alpha.1",
+ "toastr": "~2.1.0",
+ "jQTouch": "git://github.com/senchalabs/jQTouch#~1.0.0",
+ "bib2json": "git://github.com/mayanklahiri/bib2json",
+ "jquery-form": "~3.46.0",
+ "jquery-autocomplete": "git://github.com/dyve/jquery-autocomplete",
+ "select2": "~3.5.2",
+ "jStorage": "~0.4.12",
+ "jquery.cookie": "~1.4.1",
+ "dynatree": "~1.2.5",
+ "jquery-mousewheel": "~3.1.12",
+ "wymeditor": "~1.0.0-rc.1",
+ "jstree": "~3.0.9",
+ "jquery-ui": "git://github.com/jquery/jquery-ui.git#~1.11.2",
+ "jquery.event.drag-drop": "~2.2.1",
+ "handlebars": "~2.0.0",
+ "jquery-migrate": "~1.2.1",
+ "lunr.js": "git://github.com/olivernn/lunr.js.git#~0.5.7"
+ },
+ "resolutions": {
+ "jquery": "~1.11.1"
+ }
+}
diff --git a/client/galaxy/scripts/galaxy.masthead.js b/client/galaxy/scripts/galaxy.masthead.js
index 66be6dfabf5..bf488a0de99 100644
--- a/client/galaxy/scripts/galaxy.masthead.js
+++ b/client/galaxy/scripts/galaxy.masthead.js
@@ -364,7 +364,7 @@ var GalaxyMastheadTab = Backbone.View.extend({
var $popover_element = $(this.el).find('.head');
$popover_element.popover({
html: true,
- content: 'Please log in or register to use this feature.',
+ content: 'Please log in or register to use this feature.',
placement: 'bottom'
}).on('shown.bs.popover', function() { // hooking on bootstrap event to automatically hide popovers after delay
setTimeout(function() {
diff --git a/client/galaxy/scripts/galaxy.workflow_editor.canvas.js b/client/galaxy/scripts/galaxy.workflow_editor.canvas.js
index b22dc903c69..2ec9f9543e0 100644
--- a/client/galaxy/scripts/galaxy.workflow_editor.canvas.js
+++ b/client/galaxy/scripts/galaxy.workflow_editor.canvas.js
@@ -805,7 +805,7 @@ var Node = Backbone.Model.extend({
nodeView.addDataOutput( output );
} );
nodeView.render();
- workflow.node_changed( this );
+ workflow.node_changed( this, true);
},
update_field_data : function( data ) {
var node = this;
@@ -1089,17 +1089,25 @@ $.extend( Workflow.prototype, {
if ( this.active_node != node ) {
this.check_changes_in_active_form();
this.clear_active_node();
- parent.show_form_for_tool( node.form_html + node.tooltip, node );
+ if (parent.__NEWTOOLFORM__) {
+ parent.show_form_for_tool( node.form_html, node );
+ } else {
+ parent.show_form_for_tool( node.form_html + node.tooltip, node );
+ }
node.make_active();
this.active_node = node;
}
},
- node_changed : function ( node ) {
+ node_changed : function ( node, force ) {
this.has_changes = true;
- if ( this.active_node == node ) {
+ if ( this.active_node == node && (!parent.__NEWTOOLFORM__ || force)) {
// Reactive with new form_html
this.check_changes_in_active_form(); //Force changes to be saved even on new connection (previously dumped)
- parent.show_form_for_tool( node.form_html + node.tooltip, node );
+ if (parent.__NEWTOOLFORM__) {
+ parent.show_form_for_tool( node.form_html, node );
+ } else {
+ parent.show_form_for_tool( node.form_html + node.tooltip, node );
+ }
}
},
layout : function () {
@@ -1305,7 +1313,6 @@ function add_node( type, title_text, tool_id ) {
return node;
}
-
var ext_to_type = null;
var type_to_type = null;
@@ -1863,6 +1870,9 @@ $.extend( CanvasManager.prototype, {
left: x,
top: y
});
+ self.cv.css( { "background-position-x": x,
+ "background-position-y": y
+ });
self.update_viewport_overlay();
};
// Dragging within canvas background
diff --git a/client/galaxy/scripts/libs/handlebars.runtime.js b/client/galaxy/scripts/libs/handlebars.runtime.js
index b9f4e492055..932fb7aea01 100644
--- a/client/galaxy/scripts/libs/handlebars.runtime.js
+++ b/client/galaxy/scripts/libs/handlebars.runtime.js
@@ -1,6 +1,8 @@
-/*
+/*!
-Copyright (C) 2011 by Yehuda Katz
+ handlebars v2.0.0
+
+Copyright (C) 2011-2014 by Yehuda Katz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -20,343 +22,639 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+@license
*/
-
-// lib/handlebars/browser-prefix.js
-var Handlebars = {};
-
-(function(Handlebars, undefined) {
-;
-// lib/handlebars/base.js
-
-Handlebars.VERSION = "1.0.0";
-Handlebars.COMPILER_REVISION = 4;
-
-Handlebars.REVISION_CHANGES = {
- 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
- 2: '== 1.0.0-rc.3',
- 3: '== 1.0.0-rc.4',
- 4: '>= 1.0.0'
-};
-
-Handlebars.helpers = {};
-Handlebars.partials = {};
-
-var toString = Object.prototype.toString,
- functionType = '[object Function]',
- objectType = '[object Object]';
-
-Handlebars.registerHelper = function(name, fn, inverse) {
- if (toString.call(name) === objectType) {
- if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
- Handlebars.Utils.extend(this.helpers, name);
+/* exported Handlebars */
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define([], factory);
+ } else if (typeof exports === 'object') {
+ module.exports = factory();
} else {
- if (inverse) { fn.not = inverse; }
- this.helpers[name] = fn;
+ root.Handlebars = root.Handlebars || factory();
}
-};
-
-Handlebars.registerPartial = function(name, str) {
- if (toString.call(name) === objectType) {
- Handlebars.Utils.extend(this.partials, name);
- } else {
- this.partials[name] = str;
- }
-};
-
-Handlebars.registerHelper('helperMissing', function(arg) {
- if(arguments.length === 2) {
- return undefined;
- } else {
- throw new Error("Missing helper: '" + arg + "'");
- }
-});
-
-Handlebars.registerHelper('blockHelperMissing', function(context, options) {
- var inverse = options.inverse || function() {}, fn = options.fn;
-
- var type = toString.call(context);
-
- if(type === functionType) { context = context.call(this); }
-
- if(context === true) {
- return fn(this);
- } else if(context === false || context == null) {
- return inverse(this);
- } else if(type === "[object Array]") {
- if(context.length > 0) {
- return Handlebars.helpers.each(context, options);
- } else {
- return inverse(this);
- }
- } else {
- return fn(context);
- }
-});
-
-Handlebars.K = function() {};
-
-Handlebars.createFrame = Object.create || function(object) {
- Handlebars.K.prototype = object;
- var obj = new Handlebars.K();
- Handlebars.K.prototype = null;
- return obj;
-};
-
-Handlebars.logger = {
- DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
-
- methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
-
- // can be overridden in the host environment
- log: function(level, obj) {
- if (Handlebars.logger.level <= level) {
- var method = Handlebars.logger.methodMap[level];
- if (typeof console !== 'undefined' && console[method]) {
- console[method].call(console, obj);
- }
- }
- }
-};
-
-Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
-
-Handlebars.registerHelper('each', function(context, options) {
- var fn = options.fn, inverse = options.inverse;
- var i = 0, ret = "", data;
-
- var type = toString.call(context);
- if(type === functionType) { context = context.call(this); }
-
- if (options.data) {
- data = Handlebars.createFrame(options.data);
+}(this, function () {
+// handlebars/safe-string.js
+var __module3__ = (function() {
+ "use strict";
+ var __exports__;
+ // Build out our basic SafeString type
+ function SafeString(string) {
+ this.string = string;
}
- if(context && typeof context === 'object') {
- if(context instanceof Array){
- for(var j = context.length; i": ">",
+ '"': """,
+ "'": "'",
+ "`": "`"
+ };
+
+ var badChars = /[&<>"'`]/g;
+ var possible = /[&<>"'`]/;
+
+ function escapeChar(chr) {
+ return escape[chr];
+ }
+
+ function extend(obj /* , ...source */) {
+ for (var i = 1; i < arguments.length; i++) {
+ for (var key in arguments[i]) {
+ if (Object.prototype.hasOwnProperty.call(arguments[i], key)) {
+ obj[key] = arguments[i][key];
}
}
}
+
+ return obj;
}
- if(i === 0){
- ret = inverse(this);
+ __exports__.extend = extend;var toString = Object.prototype.toString;
+ __exports__.toString = toString;
+ // Sourced from lodash
+ // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt
+ var isFunction = function(value) {
+ return typeof value === 'function';
+ };
+ // fallback for older versions of Chrome and Safari
+ /* istanbul ignore next */
+ if (isFunction(/x/)) {
+ isFunction = function(value) {
+ return typeof value === 'function' && toString.call(value) === '[object Function]';
+ };
}
+ var isFunction;
+ __exports__.isFunction = isFunction;
+ /* istanbul ignore next */
+ var isArray = Array.isArray || function(value) {
+ return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false;
+ };
+ __exports__.isArray = isArray;
- return ret;
-});
-
-Handlebars.registerHelper('if', function(conditional, options) {
- var type = toString.call(conditional);
- if(type === functionType) { conditional = conditional.call(this); }
-
- if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
- return options.inverse(this);
- } else {
- return options.fn(this);
- }
-});
-
-Handlebars.registerHelper('unless', function(conditional, options) {
- return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
-});
-
-Handlebars.registerHelper('with', function(context, options) {
- var type = toString.call(context);
- if(type === functionType) { context = context.call(this); }
-
- if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
-});
-
-Handlebars.registerHelper('log', function(context, options) {
- var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
- Handlebars.log(level, context);
-});
-;
-// lib/handlebars/utils.js
-
-var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
-
-Handlebars.Exception = function(message) {
- var tmp = Error.prototype.constructor.apply(this, arguments);
-
- // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
- for (var idx = 0; idx < errorProps.length; idx++) {
- this[errorProps[idx]] = tmp[errorProps[idx]];
- }
-};
-Handlebars.Exception.prototype = new Error();
-
-// Build out our basic SafeString type
-Handlebars.SafeString = function(string) {
- this.string = string;
-};
-Handlebars.SafeString.prototype.toString = function() {
- return this.string.toString();
-};
-
-var escape = {
- "&": "&",
- "<": "<",
- ">": ">",
- '"': """,
- "'": "'",
- "`": "`"
-};
-
-var badChars = /[&<>"'`]/g;
-var possible = /[&<>"'`]/;
-
-var escapeChar = function(chr) {
- return escape[chr] || "&";
-};
-
-Handlebars.Utils = {
- extend: function(obj, value) {
- for(var key in value) {
- if(value.hasOwnProperty(key)) {
- obj[key] = value[key];
- }
- }
- },
-
- escapeExpression: function(string) {
+ function escapeExpression(string) {
// don't escape SafeStrings, since they're already safe
- if (string instanceof Handlebars.SafeString) {
+ if (string instanceof SafeString) {
return string.toString();
- } else if (string == null || string === false) {
+ } else if (string == null) {
return "";
+ } else if (!string) {
+ return string + '';
}
// Force a string conversion as this will be done by the append regardless and
// the regex test will do this transparently behind the scenes, causing issues if
// an object's to string has escaped characters in it.
- string = string.toString();
+ string = "" + string;
if(!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
- },
+ }
- isEmpty: function(value) {
+ __exports__.escapeExpression = escapeExpression;function isEmpty(value) {
if (!value && value !== 0) {
return true;
- } else if(toString.call(value) === "[object Array]" && value.length === 0) {
+ } else if (isArray(value) && value.length === 0) {
return true;
} else {
return false;
}
}
-};
-;
-// lib/handlebars/runtime.js
-Handlebars.VM = {
- template: function(templateSpec) {
+ __exports__.isEmpty = isEmpty;function appendContextPath(contextPath, id) {
+ return (contextPath ? contextPath + '.' : '') + id;
+ }
+
+ __exports__.appendContextPath = appendContextPath;
+ return __exports__;
+})(__module3__);
+
+// handlebars/exception.js
+var __module4__ = (function() {
+ "use strict";
+ var __exports__;
+
+ var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
+
+ function Exception(message, node) {
+ var line;
+ if (node && node.firstLine) {
+ line = node.firstLine;
+
+ message += ' - ' + line + ':' + node.firstColumn;
+ }
+
+ var tmp = Error.prototype.constructor.call(this, message);
+
+ // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
+ for (var idx = 0; idx < errorProps.length; idx++) {
+ this[errorProps[idx]] = tmp[errorProps[idx]];
+ }
+
+ if (line) {
+ this.lineNumber = line;
+ this.column = node.firstColumn;
+ }
+ }
+
+ Exception.prototype = new Error();
+
+ __exports__ = Exception;
+ return __exports__;
+})();
+
+// handlebars/base.js
+var __module1__ = (function(__dependency1__, __dependency2__) {
+ "use strict";
+ var __exports__ = {};
+ var Utils = __dependency1__;
+ var Exception = __dependency2__;
+
+ var VERSION = "2.0.0";
+ __exports__.VERSION = VERSION;var COMPILER_REVISION = 6;
+ __exports__.COMPILER_REVISION = COMPILER_REVISION;
+ var REVISION_CHANGES = {
+ 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
+ 2: '== 1.0.0-rc.3',
+ 3: '== 1.0.0-rc.4',
+ 4: '== 1.x.x',
+ 5: '== 2.0.0-alpha.x',
+ 6: '>= 2.0.0-beta.1'
+ };
+ __exports__.REVISION_CHANGES = REVISION_CHANGES;
+ var isArray = Utils.isArray,
+ isFunction = Utils.isFunction,
+ toString = Utils.toString,
+ objectType = '[object Object]';
+
+ function HandlebarsEnvironment(helpers, partials) {
+ this.helpers = helpers || {};
+ this.partials = partials || {};
+
+ registerDefaultHelpers(this);
+ }
+
+ __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = {
+ constructor: HandlebarsEnvironment,
+
+ logger: logger,
+ log: log,
+
+ registerHelper: function(name, fn) {
+ if (toString.call(name) === objectType) {
+ if (fn) { throw new Exception('Arg not supported with multiple helpers'); }
+ Utils.extend(this.helpers, name);
+ } else {
+ this.helpers[name] = fn;
+ }
+ },
+ unregisterHelper: function(name) {
+ delete this.helpers[name];
+ },
+
+ registerPartial: function(name, partial) {
+ if (toString.call(name) === objectType) {
+ Utils.extend(this.partials, name);
+ } else {
+ this.partials[name] = partial;
+ }
+ },
+ unregisterPartial: function(name) {
+ delete this.partials[name];
+ }
+ };
+
+ function registerDefaultHelpers(instance) {
+ instance.registerHelper('helperMissing', function(/* [args, ]options */) {
+ if(arguments.length === 1) {
+ // A missing field in a {{foo}} constuct.
+ return undefined;
+ } else {
+ // Someone is actually trying to call something, blow up.
+ throw new Exception("Missing helper: '" + arguments[arguments.length-1].name + "'");
+ }
+ });
+
+ instance.registerHelper('blockHelperMissing', function(context, options) {
+ var inverse = options.inverse,
+ fn = options.fn;
+
+ if(context === true) {
+ return fn(this);
+ } else if(context === false || context == null) {
+ return inverse(this);
+ } else if (isArray(context)) {
+ if(context.length > 0) {
+ if (options.ids) {
+ options.ids = [options.name];
+ }
+
+ return instance.helpers.each(context, options);
+ } else {
+ return inverse(this);
+ }
+ } else {
+ if (options.data && options.ids) {
+ var data = createFrame(options.data);
+ data.contextPath = Utils.appendContextPath(options.data.contextPath, options.name);
+ options = {data: data};
+ }
+
+ return fn(context, options);
+ }
+ });
+
+ instance.registerHelper('each', function(context, options) {
+ if (!options) {
+ throw new Exception('Must pass iterator to #each');
+ }
+
+ var fn = options.fn, inverse = options.inverse;
+ var i = 0, ret = "", data;
+
+ var contextPath;
+ if (options.data && options.ids) {
+ contextPath = Utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.';
+ }
+
+ if (isFunction(context)) { context = context.call(this); }
+
+ if (options.data) {
+ data = createFrame(options.data);
+ }
+
+ if(context && typeof context === 'object') {
+ if (isArray(context)) {
+ for(var j = context.length; i= 0;
+ // adding 1 corrects loss of precision from parseFloat (#15100)
+ return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0;
},
isEmptyObject: function( obj ) {
@@ -584,14 +585,14 @@ function isArraylike( obj ) {
}
var Sizzle =
/*!
- * Sizzle CSS Selector Engine v1.10.19
+ * Sizzle CSS Selector Engine v2.2.0-pre
* http://sizzlejs.com/
*
- * Copyright 2013 jQuery Foundation, Inc. and other contributors
+ * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors
* Released under the MIT license
* http://jquery.org/license
*
- * Date: 2014-04-18
+ * Date: 2014-12-16
*/
(function( window ) {
@@ -618,7 +619,7 @@ var i,
contains,
// Instance-specific data
- expando = "sizzle" + -(new Date()),
+ expando = "sizzle" + 1 * new Date(),
preferredDoc = window.document,
dirruns = 0,
done = 0,
@@ -633,7 +634,6 @@ var i,
},
// General-purpose constants
- strundefined = typeof undefined,
MAX_NEGATIVE = 1 << 31,
// Instance methods
@@ -643,12 +643,13 @@ var i,
push_native = arr.push,
push = arr.push,
slice = arr.slice,
- // Use a stripped-down indexOf if we can't use a native one
- indexOf = arr.indexOf || function( elem ) {
+ // Use a stripped-down indexOf as it's faster than native
+ // http://jsperf.com/thor-indexof-vs-for/5
+ indexOf = function( list, elem ) {
var i = 0,
- len = this.length;
+ len = list.length;
for ( ; i < len; i++ ) {
- if ( this[i] === elem ) {
+ if ( list[i] === elem ) {
return i;
}
}
@@ -688,6 +689,7 @@ var i,
")\\)|)",
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+ rwhitespace = new RegExp( whitespace + "+", "g" ),
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
@@ -739,6 +741,14 @@ var i,
String.fromCharCode( high + 0x10000 ) :
// Supplemental Plane codepoint (surrogate pair)
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+ },
+
+ // Used for iframes
+ // See setDocument()
+ // Removing the function wrapper causes a "Permission Denied"
+ // error in IE
+ unloadHandler = function() {
+ setDocument();
};
// Optimize for push.apply( _, NodeList )
@@ -781,19 +791,18 @@ function Sizzle( selector, context, results, seed ) {
context = context || document;
results = results || [];
+ nodeType = context.nodeType;
+
+ if ( typeof selector !== "string" || !selector ||
+ nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
- if ( !selector || typeof selector !== "string" ) {
return results;
}
- if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
- return [];
- }
+ if ( !seed && documentIsHTML ) {
- if ( documentIsHTML && !seed ) {
-
- // Shortcuts
- if ( (match = rquickExpr.exec( selector )) ) {
+ // Try to shortcut find operations when possible (e.g., not under DocumentFragment)
+ if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
// Speed-up: Sizzle("#ID")
if ( (m = match[1]) ) {
if ( nodeType === 9 ) {
@@ -825,7 +834,7 @@ function Sizzle( selector, context, results, seed ) {
return results;
// Speed-up: Sizzle(".CLASS")
- } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
+ } else if ( (m = match[3]) && support.getElementsByClassName ) {
push.apply( results, context.getElementsByClassName( m ) );
return results;
}
@@ -835,7 +844,7 @@ function Sizzle( selector, context, results, seed ) {
if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
nid = old = expando;
newContext = context;
- newSelector = nodeType === 9 && selector;
+ newSelector = nodeType !== 1 && selector;
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
@@ -1022,7 +1031,7 @@ function createPositionalPseudo( fn ) {
* @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
*/
function testContext( context ) {
- return context && typeof context.getElementsByTagName !== strundefined && context;
+ return context && typeof context.getElementsByTagName !== "undefined" && context;
}
// Expose support vars for convenience
@@ -1046,9 +1055,8 @@ isXML = Sizzle.isXML = function( elem ) {
* @returns {Object} Returns the current document
*/
setDocument = Sizzle.setDocument = function( node ) {
- var hasCompare,
- doc = node ? node.ownerDocument || node : preferredDoc,
- parent = doc.defaultView;
+ var hasCompare, parent,
+ doc = node ? node.ownerDocument || node : preferredDoc;
// If no document and documentElement is available, return
if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
@@ -1058,9 +1066,7 @@ setDocument = Sizzle.setDocument = function( node ) {
// Set our document
document = doc;
docElem = doc.documentElement;
-
- // Support tests
- documentIsHTML = !isXML( doc );
+ parent = doc.defaultView;
// Support: IE>8
// If iframe document is assigned to "document" variable and if iframe has been reloaded,
@@ -1069,21 +1075,22 @@ setDocument = Sizzle.setDocument = function( node ) {
if ( parent && parent !== parent.top ) {
// IE11 does not have attachEvent, so all must suffer
if ( parent.addEventListener ) {
- parent.addEventListener( "unload", function() {
- setDocument();
- }, false );
+ parent.addEventListener( "unload", unloadHandler, false );
} else if ( parent.attachEvent ) {
- parent.attachEvent( "onunload", function() {
- setDocument();
- });
+ parent.attachEvent( "onunload", unloadHandler );
}
}
+ /* Support tests
+ ---------------------------------------------------------------------- */
+ documentIsHTML = !isXML( doc );
+
/* Attributes
---------------------------------------------------------------------- */
// Support: IE<8
- // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
+ // Verify that getAttribute really returns attributes and not properties
+ // (excepting IE8 booleans)
support.attributes = assert(function( div ) {
div.className = "i";
return !div.getAttribute("className");
@@ -1098,17 +1105,8 @@ setDocument = Sizzle.setDocument = function( node ) {
return !div.getElementsByTagName("*").length;
});
- // Check if getElementsByClassName can be trusted
- support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
- div.innerHTML = "";
-
- // Support: Safari<4
- // Catch class over-caching
- div.firstChild.className = "i";
- // Support: Opera<10
- // Catch gEBCN failure to find non-leading classes
- return div.getElementsByClassName("i").length === 2;
- });
+ // Support: IE<9
+ support.getElementsByClassName = rnative.test( doc.getElementsByClassName );
// Support: IE<10
// Check if getElementById returns elements by name
@@ -1122,7 +1120,7 @@ setDocument = Sizzle.setDocument = function( node ) {
// ID find and filter
if ( support.getById ) {
Expr.find["ID"] = function( id, context ) {
- if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
+ if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
var m = context.getElementById( id );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
@@ -1143,7 +1141,7 @@ setDocument = Sizzle.setDocument = function( node ) {
Expr.filter["ID"] = function( id ) {
var attrId = id.replace( runescape, funescape );
return function( elem ) {
- var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
return node && node.value === attrId;
};
};
@@ -1152,14 +1150,20 @@ setDocument = Sizzle.setDocument = function( node ) {
// Tag
Expr.find["TAG"] = support.getElementsByTagName ?
function( tag, context ) {
- if ( typeof context.getElementsByTagName !== strundefined ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
return context.getElementsByTagName( tag );
+
+ // DocumentFragment nodes don't have gEBTN
+ } else if ( support.qsa ) {
+ return context.querySelectorAll( tag );
}
} :
+
function( tag, context ) {
var elem,
tmp = [],
i = 0,
+ // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
results = context.getElementsByTagName( tag );
// Filter out possible comments
@@ -1177,7 +1181,7 @@ setDocument = Sizzle.setDocument = function( node ) {
// Class
Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
- if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
+ if ( documentIsHTML ) {
return context.getElementsByClassName( className );
}
};
@@ -1206,13 +1210,15 @@ setDocument = Sizzle.setDocument = function( node ) {
// setting a boolean content attribute,
// since its presence should be enough
// http://bugs.jquery.com/ticket/12359
- div.innerHTML = "";
+ docElem.appendChild( div ).innerHTML = "" +
+ "";
// Support: IE8, Opera 11-12.16
// Nothing should be selected when empty strings follow ^= or $= or *=
// The test attribute must be unknown in Opera but "safe" for WinRT
// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
- if ( div.querySelectorAll("[msallowclip^='']").length ) {
+ if ( div.querySelectorAll("[msallowcapture^='']").length ) {
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
}
@@ -1222,12 +1228,24 @@ setDocument = Sizzle.setDocument = function( node ) {
rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
}
+ // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+
+ if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
+ rbuggyQSA.push("~=");
+ }
+
// Webkit/Opera - :checked should return selected option elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
// IE8 throws error here and will not see later tests
if ( !div.querySelectorAll(":checked").length ) {
rbuggyQSA.push(":checked");
}
+
+ // Support: Safari 8+, iOS 8+
+ // https://bugs.webkit.org/show_bug.cgi?id=136851
+ // In-page `selector#id sibing-combinator selector` fails
+ if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) {
+ rbuggyQSA.push(".#.+[+~]");
+ }
});
assert(function( div ) {
@@ -1344,7 +1362,7 @@ setDocument = Sizzle.setDocument = function( node ) {
// Maintain original order
return sortInput ?
- ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+ ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
0;
}
@@ -1371,7 +1389,7 @@ setDocument = Sizzle.setDocument = function( node ) {
aup ? -1 :
bup ? 1 :
sortInput ?
- ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+ ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
0;
// If the nodes are siblings, we can do a quick check
@@ -1434,7 +1452,7 @@ Sizzle.matchesSelector = function( elem, expr ) {
elem.document && elem.document.nodeType !== 11 ) {
return ret;
}
- } catch(e) {}
+ } catch (e) {}
}
return Sizzle( expr, document, null, [ elem ] ).length > 0;
@@ -1653,7 +1671,7 @@ Expr = Sizzle.selectors = {
return pattern ||
(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
classCache( className, function( elem ) {
- return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
+ return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
});
},
@@ -1675,7 +1693,7 @@ Expr = Sizzle.selectors = {
operator === "^=" ? check && result.indexOf( check ) === 0 :
operator === "*=" ? check && result.indexOf( check ) > -1 :
operator === "$=" ? check && result.slice( -check.length ) === check :
- operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+ operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
false;
};
@@ -1795,7 +1813,7 @@ Expr = Sizzle.selectors = {
matched = fn( seed, argument ),
i = matched.length;
while ( i-- ) {
- idx = indexOf.call( seed, matched[i] );
+ idx = indexOf( seed, matched[i] );
seed[ idx ] = !( matches[ idx ] = matched[i] );
}
}) :
@@ -1834,6 +1852,8 @@ Expr = Sizzle.selectors = {
function( elem, context, xml ) {
input[0] = elem;
matcher( input, null, xml, results );
+ // Don't keep the element (issue #299)
+ input[0] = null;
return !results.pop();
};
}),
@@ -1845,6 +1865,7 @@ Expr = Sizzle.selectors = {
}),
"contains": markFunction(function( text ) {
+ text = text.replace( runescape, funescape );
return function( elem ) {
return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
};
@@ -2266,7 +2287,7 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS
i = matcherOut.length;
while ( i-- ) {
if ( (elem = matcherOut[i]) &&
- (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+ (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
seed[temp] = !(results[temp] = elem);
}
@@ -2301,13 +2322,16 @@ function matcherFromTokens( tokens ) {
return elem === checkContext;
}, implicitRelative, true ),
matchAnyContext = addCombinator( function( elem ) {
- return indexOf.call( checkContext, elem ) > -1;
+ return indexOf( checkContext, elem ) > -1;
}, implicitRelative, true ),
matchers = [ function( elem, context, xml ) {
- return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
(checkContext = context).nodeType ?
matchContext( elem, context, xml ) :
matchAnyContext( elem, context, xml ) );
+ // Avoid hanging onto element (issue #299)
+ checkContext = null;
+ return ret;
} ];
for ( ; i < len; i++ ) {
@@ -2557,7 +2581,7 @@ select = Sizzle.select = function( selector, context, results, seed ) {
// Sort stability
support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
-// Support: Chrome<14
+// Support: Chrome 14-35+
// Always assume duplicates if they aren't passed to the comparison function
support.detectDuplicates = !!hasDuplicate;
@@ -6115,7 +6139,14 @@ var getStyles, curCSS,
if ( window.getComputedStyle ) {
getStyles = function( elem ) {
- return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
+ // Support: IE<=11+, Firefox<=30+ (#15098, #14150)
+ // IE throws on elements created in popups
+ // FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
+ if ( elem.ownerDocument.defaultView.opener ) {
+ return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
+ }
+
+ return window.getComputedStyle( elem, null );
};
curCSS = function( elem, name, computed ) {
@@ -6363,6 +6394,8 @@ function addGetHookIf( conditionFn, hookFn ) {
reliableMarginRightVal =
!parseFloat( ( window.getComputedStyle( contents, null ) || {} ).marginRight );
+
+ div.removeChild( contents );
}
// Support: IE8
@@ -9070,7 +9103,8 @@ jQuery.extend({
}
// We can fire global events as of now if asked to
- fireGlobals = s.global;
+ // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
+ fireGlobals = jQuery.event && s.global;
// Watch for a new set of requests
if ( fireGlobals && jQuery.active++ === 0 ) {
@@ -9329,13 +9363,6 @@ jQuery.each( [ "get", "post" ], function( i, method ) {
};
});
-// Attach a bunch of functions for handling common AJAX events
-jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
- jQuery.fn[ type ] = function( fn ) {
- return this.on( type, fn );
- };
-});
-
jQuery._evalUrl = function( url ) {
return jQuery.ajax({
@@ -9561,8 +9588,9 @@ var xhrId = 0,
// Support: IE<10
// Open requests must be manually aborted on unload (#5280)
-if ( window.ActiveXObject ) {
- jQuery( window ).on( "unload", function() {
+// See https://support.microsoft.com/kb/2856746 for more info
+if ( window.attachEvent ) {
+ window.attachEvent( "onunload", function() {
for ( var key in xhrCallbacks ) {
xhrCallbacks[ key ]( undefined, true );
}
@@ -9996,6 +10024,16 @@ jQuery.fn.load = function( url, params, callback ) {
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
+ jQuery.fn[ type ] = function( fn ) {
+ return this.on( type, fn );
+ };
+});
+
+
+
+
jQuery.expr.filters.animated = function( elem ) {
return jQuery.grep(jQuery.timers, function( fn ) {
return elem === fn.elem;
diff --git a/client/galaxy/scripts/libs/lunr.js b/client/galaxy/scripts/libs/lunr.js
new file mode 100644
index 00000000000..b5bacd97960
--- /dev/null
+++ b/client/galaxy/scripts/libs/lunr.js
@@ -0,0 +1,1910 @@
+/**
+ * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.5.7
+ * Copyright (C) 2014 Oliver Nightingale
+ * MIT Licensed
+ * @license
+ */
+
+(function(){
+
+/**
+ * Convenience function for instantiating a new lunr index and configuring it
+ * with the default pipeline functions and the passed config function.
+ *
+ * When using this convenience function a new index will be created with the
+ * following functions already in the pipeline:
+ *
+ * lunr.StopWordFilter - filters out any stop words before they enter the
+ * index
+ *
+ * lunr.stemmer - stems the tokens before entering the index.
+ *
+ * Example:
+ *
+ * var idx = lunr(function () {
+ * this.field('title', 10)
+ * this.field('tags', 100)
+ * this.field('body')
+ *
+ * this.ref('cid')
+ *
+ * this.pipeline.add(function () {
+ * // some custom pipeline function
+ * })
+ *
+ * })
+ *
+ * @param {Function} config A function that will be called with the new instance
+ * of the lunr.Index as both its context and first parameter. It can be used to
+ * customize the instance of new lunr.Index.
+ * @namespace
+ * @module
+ * @returns {lunr.Index}
+ *
+ */
+var lunr = function (config) {
+ var idx = new lunr.Index
+
+ idx.pipeline.add(
+ lunr.trimmer,
+ lunr.stopWordFilter,
+ lunr.stemmer
+ )
+
+ if (config) config.call(idx, idx)
+
+ return idx
+}
+
+lunr.version = "0.5.7"
+/*!
+ * lunr.utils
+ * Copyright (C) 2014 Oliver Nightingale
+ */
+
+/**
+ * A namespace containing utils for the rest of the lunr library
+ */
+lunr.utils = {}
+
+/**
+ * Print a warning message to the console.
+ *
+ * @param {String} message The message to be printed.
+ * @memberOf Utils
+ */
+lunr.utils.warn = (function (global) {
+ return function (message) {
+ if (global.console && console.warn) {
+ console.warn(message)
+ }
+ }
+})(this)
+
+/*!
+ * lunr.EventEmitter
+ * Copyright (C) 2014 Oliver Nightingale
+ */
+
+/**
+ * lunr.EventEmitter is an event emitter for lunr. It manages adding and removing event handlers and triggering events and their handlers.
+ *
+ * @constructor
+ */
+lunr.EventEmitter = function () {
+ this.events = {}
+}
+
+/**
+ * Binds a handler function to a specific event(s).
+ *
+ * Can bind a single function to many different events in one call.
+ *
+ * @param {String} [eventName] The name(s) of events to bind this function to.
+ * @param {Function} handler The function to call when an event is fired.
+ * @memberOf EventEmitter
+ */
+lunr.EventEmitter.prototype.addListener = function () {
+ var args = Array.prototype.slice.call(arguments),
+ fn = args.pop(),
+ names = args
+
+ if (typeof fn !== "function") throw new TypeError ("last argument must be a function")
+
+ names.forEach(function (name) {
+ if (!this.hasHandler(name)) this.events[name] = []
+ this.events[name].push(fn)
+ }, this)
+}
+
+/**
+ * Removes a handler function from a specific event.
+ *
+ * @param {String} eventName The name of the event to remove this function from.
+ * @param {Function} handler The function to remove from an event.
+ * @memberOf EventEmitter
+ */
+lunr.EventEmitter.prototype.removeListener = function (name, fn) {
+ if (!this.hasHandler(name)) return
+
+ var fnIndex = this.events[name].indexOf(fn)
+ this.events[name].splice(fnIndex, 1)
+
+ if (!this.events[name].length) delete this.events[name]
+}
+
+/**
+ * Calls all functions bound to the given event.
+ *
+ * Additional data can be passed to the event handler as arguments to `emit`
+ * after the event name.
+ *
+ * @param {String} eventName The name of the event to emit.
+ * @memberOf EventEmitter
+ */
+lunr.EventEmitter.prototype.emit = function (name) {
+ if (!this.hasHandler(name)) return
+
+ var args = Array.prototype.slice.call(arguments, 1)
+
+ this.events[name].forEach(function (fn) {
+ fn.apply(undefined, args)
+ })
+}
+
+/**
+ * Checks whether a handler has ever been stored against an event.
+ *
+ * @param {String} eventName The name of the event to check.
+ * @private
+ * @memberOf EventEmitter
+ */
+lunr.EventEmitter.prototype.hasHandler = function (name) {
+ return name in this.events
+}
+
+/*!
+ * lunr.tokenizer
+ * Copyright (C) 2014 Oliver Nightingale
+ */
+
+/**
+ * A function for splitting a string into tokens ready to be inserted into
+ * the search index.
+ *
+ * @module
+ * @param {String} obj The string to convert into tokens
+ * @returns {Array}
+ */
+lunr.tokenizer = function (obj) {
+ if (!arguments.length || obj == null || obj == undefined) return []
+ if (Array.isArray(obj)) return obj.map(function (t) { return t.toLowerCase() })
+
+ var str = obj.toString().replace(/^\s+/, '')
+
+ for (var i = str.length - 1; i >= 0; i--) {
+ if (/\S/.test(str.charAt(i))) {
+ str = str.substring(0, i + 1)
+ break
+ }
+ }
+
+ return str
+ .split(/(?:\s+|\-)/)
+ .filter(function (token) {
+ return !!token
+ })
+ .map(function (token) {
+ return token.toLowerCase()
+ })
+}
+/*!
+ * lunr.Pipeline
+ * Copyright (C) 2014 Oliver Nightingale
+ */
+
+/**
+ * lunr.Pipelines maintain an ordered list of functions to be applied to all
+ * tokens in documents entering the search index and queries being ran against
+ * the index.
+ *
+ * An instance of lunr.Index created with the lunr shortcut will contain a
+ * pipeline with a stop word filter and an English language stemmer. Extra
+ * functions can be added before or after either of these functions or these
+ * default functions can be removed.
+ *
+ * When run the pipeline will call each function in turn, passing a token, the
+ * index of that token in the original list of all tokens and finally a list of
+ * all the original tokens.
+ *
+ * The output of functions in the pipeline will be passed to the next function
+ * in the pipeline. To exclude a token from entering the index the function
+ * should return undefined, the rest of the pipeline will not be called with
+ * this token.
+ *
+ * For serialisation of pipelines to work, all functions used in an instance of
+ * a pipeline should be registered with lunr.Pipeline. Registered functions can
+ * then be loaded. If trying to load a serialised pipeline that uses functions
+ * that are not registered an error will be thrown.
+ *
+ * If not planning on serialising the pipeline then registering pipeline functions
+ * is not necessary.
+ *
+ * @constructor
+ */
+lunr.Pipeline = function () {
+ this._stack = []
+}
+
+lunr.Pipeline.registeredFunctions = {}
+
+/**
+ * Register a function with the pipeline.
+ *
+ * Functions that are used in the pipeline should be registered if the pipeline
+ * needs to be serialised, or a serialised pipeline needs to be loaded.
+ *
+ * Registering a function does not add it to a pipeline, functions must still be
+ * added to instances of the pipeline for them to be used when running a pipeline.
+ *
+ * @param {Function} fn The function to check for.
+ * @param {String} label The label to register this function with
+ * @memberOf Pipeline
+ */
+lunr.Pipeline.registerFunction = function (fn, label) {
+ if (label in this.registeredFunctions) {
+ lunr.utils.warn('Overwriting existing registered function: ' + label)
+ }
+
+ fn.label = label
+ lunr.Pipeline.registeredFunctions[fn.label] = fn
+}
+
+/**
+ * Warns if the function is not registered as a Pipeline function.
+ *
+ * @param {Function} fn The function to check for.
+ * @private
+ * @memberOf Pipeline
+ */
+lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {
+ var isRegistered = fn.label && (fn.label in this.registeredFunctions)
+
+ if (!isRegistered) {
+ lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn)
+ }
+}
+
+/**
+ * Loads a previously serialised pipeline.
+ *
+ * All functions to be loaded must already be registered with lunr.Pipeline.
+ * If any function from the serialised data has not been registered then an
+ * error will be thrown.
+ *
+ * @param {Object} serialised The serialised pipeline to load.
+ * @returns {lunr.Pipeline}
+ * @memberOf Pipeline
+ */
+lunr.Pipeline.load = function (serialised) {
+ var pipeline = new lunr.Pipeline
+
+ serialised.forEach(function (fnName) {
+ var fn = lunr.Pipeline.registeredFunctions[fnName]
+
+ if (fn) {
+ pipeline.add(fn)
+ } else {
+ throw new Error ('Cannot load un-registered function: ' + fnName)
+ }
+ })
+
+ return pipeline
+}
+
+/**
+ * Adds new functions to the end of the pipeline.
+ *
+ * Logs a warning if the function has not been registered.
+ *
+ * @param {Function} functions Any number of functions to add to the pipeline.
+ * @memberOf Pipeline
+ */
+lunr.Pipeline.prototype.add = function () {
+ var fns = Array.prototype.slice.call(arguments)
+
+ fns.forEach(function (fn) {
+ lunr.Pipeline.warnIfFunctionNotRegistered(fn)
+ this._stack.push(fn)
+ }, this)
+}
+
+/**
+ * Adds a single function after a function that already exists in the
+ * pipeline.
+ *
+ * Logs a warning if the function has not been registered.
+ *
+ * @param {Function} existingFn A function that already exists in the pipeline.
+ * @param {Function} newFn The new function to add to the pipeline.
+ * @memberOf Pipeline
+ */
+lunr.Pipeline.prototype.after = function (existingFn, newFn) {
+ lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
+
+ var pos = this._stack.indexOf(existingFn) + 1
+ this._stack.splice(pos, 0, newFn)
+}
+
+/**
+ * Adds a single function before a function that already exists in the
+ * pipeline.
+ *
+ * Logs a warning if the function has not been registered.
+ *
+ * @param {Function} existingFn A function that already exists in the pipeline.
+ * @param {Function} newFn The new function to add to the pipeline.
+ * @memberOf Pipeline
+ */
+lunr.Pipeline.prototype.before = function (existingFn, newFn) {
+ lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
+
+ var pos = this._stack.indexOf(existingFn)
+ this._stack.splice(pos, 0, newFn)
+}
+
+/**
+ * Removes a function from the pipeline.
+ *
+ * @param {Function} fn The function to remove from the pipeline.
+ * @memberOf Pipeline
+ */
+lunr.Pipeline.prototype.remove = function (fn) {
+ var pos = this._stack.indexOf(fn)
+ this._stack.splice(pos, 1)
+}
+
+/**
+ * Runs the current list of functions that make up the pipeline against the
+ * passed tokens.
+ *
+ * @param {Array} tokens The tokens to run through the pipeline.
+ * @returns {Array}
+ * @memberOf Pipeline
+ */
+lunr.Pipeline.prototype.run = function (tokens) {
+ var out = [],
+ tokenLength = tokens.length,
+ stackLength = this._stack.length
+
+ for (var i = 0; i < tokenLength; i++) {
+ var token = tokens[i]
+
+ for (var j = 0; j < stackLength; j++) {
+ token = this._stack[j](token, i, tokens)
+ if (token === void 0) break
+ };
+
+ if (token !== void 0) out.push(token)
+ };
+
+ return out
+}
+
+/**
+ * Resets the pipeline by removing any existing processors.
+ *
+ * @memberOf Pipeline
+ */
+lunr.Pipeline.prototype.reset = function () {
+ this._stack = []
+}
+
+/**
+ * Returns a representation of the pipeline ready for serialisation.
+ *
+ * Logs a warning if the function has not been registered.
+ *
+ * @returns {Array}
+ * @memberOf Pipeline
+ */
+lunr.Pipeline.prototype.toJSON = function () {
+ return this._stack.map(function (fn) {
+ lunr.Pipeline.warnIfFunctionNotRegistered(fn)
+
+ return fn.label
+ })
+}
+/*!
+ * lunr.Vector
+ * Copyright (C) 2014 Oliver Nightingale
+ */
+
+/**
+ * lunr.Vectors implement vector related operations for
+ * a series of elements.
+ *
+ * @constructor
+ */
+lunr.Vector = function () {
+ this._magnitude = null
+ this.list = undefined
+ this.length = 0
+}
+
+/**
+ * lunr.Vector.Node is a simple struct for each node
+ * in a lunr.Vector.
+ *
+ * @private
+ * @param {Number} The index of the node in the vector.
+ * @param {Object} The data at this node in the vector.
+ * @param {lunr.Vector.Node} The node directly after this node in the vector.
+ * @constructor
+ * @memberOf Vector
+ */
+lunr.Vector.Node = function (idx, val, next) {
+ this.idx = idx
+ this.val = val
+ this.next = next
+}
+
+/**
+ * Inserts a new value at a position in a vector.
+ *
+ * @param {Number} The index at which to insert a value.
+ * @param {Object} The object to insert in the vector.
+ * @memberOf Vector.
+ */
+lunr.Vector.prototype.insert = function (idx, val) {
+ var list = this.list
+
+ if (!list) {
+ this.list = new lunr.Vector.Node (idx, val, list)
+ return this.length++
+ }
+
+ var prev = list,
+ next = list.next
+
+ while (next != undefined) {
+ if (idx < next.idx) {
+ prev.next = new lunr.Vector.Node (idx, val, next)
+ return this.length++
+ }
+
+ prev = next, next = next.next
+ }
+
+ prev.next = new lunr.Vector.Node (idx, val, next)
+ return this.length++
+}
+
+/**
+ * Calculates the magnitude of this vector.
+ *
+ * @returns {Number}
+ * @memberOf Vector
+ */
+lunr.Vector.prototype.magnitude = function () {
+ if (this._magniture) return this._magnitude
+ var node = this.list,
+ sumOfSquares = 0,
+ val
+
+ while (node) {
+ val = node.val
+ sumOfSquares += val * val
+ node = node.next
+ }
+
+ return this._magnitude = Math.sqrt(sumOfSquares)
+}
+
+/**
+ * Calculates the dot product of this vector and another vector.
+ *
+ * @param {lunr.Vector} otherVector The vector to compute the dot product with.
+ * @returns {Number}
+ * @memberOf Vector
+ */
+lunr.Vector.prototype.dot = function (otherVector) {
+ var node = this.list,
+ otherNode = otherVector.list,
+ dotProduct = 0
+
+ while (node && otherNode) {
+ if (node.idx < otherNode.idx) {
+ node = node.next
+ } else if (node.idx > otherNode.idx) {
+ otherNode = otherNode.next
+ } else {
+ dotProduct += node.val * otherNode.val
+ node = node.next
+ otherNode = otherNode.next
+ }
+ }
+
+ return dotProduct
+}
+
+/**
+ * Calculates the cosine similarity between this vector and another
+ * vector.
+ *
+ * @param {lunr.Vector} otherVector The other vector to calculate the
+ * similarity with.
+ * @returns {Number}
+ * @memberOf Vector
+ */
+lunr.Vector.prototype.similarity = function (otherVector) {
+ return this.dot(otherVector) / (this.magnitude() * otherVector.magnitude())
+}
+/*!
+ * lunr.SortedSet
+ * Copyright (C) 2014 Oliver Nightingale
+ */
+
+/**
+ * lunr.SortedSets are used to maintain an array of uniq values in a sorted
+ * order.
+ *
+ * @constructor
+ */
+lunr.SortedSet = function () {
+ this.length = 0
+ this.elements = []
+}
+
+/**
+ * Loads a previously serialised sorted set.
+ *
+ * @param {Array} serialisedData The serialised set to load.
+ * @returns {lunr.SortedSet}
+ * @memberOf SortedSet
+ */
+lunr.SortedSet.load = function (serialisedData) {
+ var set = new this
+
+ set.elements = serialisedData
+ set.length = serialisedData.length
+
+ return set
+}
+
+/**
+ * Inserts new items into the set in the correct position to maintain the
+ * order.
+ *
+ * @param {Object} The objects to add to this set.
+ * @memberOf SortedSet
+ */
+lunr.SortedSet.prototype.add = function () {
+ Array.prototype.slice.call(arguments).forEach(function (element) {
+ if (~this.indexOf(element)) return
+ this.elements.splice(this.locationFor(element), 0, element)
+ }, this)
+
+ this.length = this.elements.length
+}
+
+/**
+ * Converts this sorted set into an array.
+ *
+ * @returns {Array}
+ * @memberOf SortedSet
+ */
+lunr.SortedSet.prototype.toArray = function () {
+ return this.elements.slice()
+}
+
+/**
+ * Creates a new array with the results of calling a provided function on every
+ * element in this sorted set.
+ *
+ * Delegates to Array.prototype.map and has the same signature.
+ *
+ * @param {Function} fn The function that is called on each element of the
+ * set.
+ * @param {Object} ctx An optional object that can be used as the context
+ * for the function fn.
+ * @returns {Array}
+ * @memberOf SortedSet
+ */
+lunr.SortedSet.prototype.map = function (fn, ctx) {
+ return this.elements.map(fn, ctx)
+}
+
+/**
+ * Executes a provided function once per sorted set element.
+ *
+ * Delegates to Array.prototype.forEach and has the same signature.
+ *
+ * @param {Function} fn The function that is called on each element of the
+ * set.
+ * @param {Object} ctx An optional object that can be used as the context
+ * @memberOf SortedSet
+ * for the function fn.
+ */
+lunr.SortedSet.prototype.forEach = function (fn, ctx) {
+ return this.elements.forEach(fn, ctx)
+}
+
+/**
+ * Returns the index at which a given element can be found in the
+ * sorted set, or -1 if it is not present.
+ *
+ * @param {Object} elem The object to locate in the sorted set.
+ * @param {Number} start An optional index at which to start searching from
+ * within the set.
+ * @param {Number} end An optional index at which to stop search from within
+ * the set.
+ * @returns {Number}
+ * @memberOf SortedSet
+ */
+lunr.SortedSet.prototype.indexOf = function (elem, start, end) {
+ var start = start || 0,
+ end = end || this.elements.length,
+ sectionLength = end - start,
+ pivot = start + Math.floor(sectionLength / 2),
+ pivotElem = this.elements[pivot]
+
+ if (sectionLength <= 1) {
+ if (pivotElem === elem) {
+ return pivot
+ } else {
+ return -1
+ }
+ }
+
+ if (pivotElem < elem) return this.indexOf(elem, pivot, end)
+ if (pivotElem > elem) return this.indexOf(elem, start, pivot)
+ if (pivotElem === elem) return pivot
+}
+
+/**
+ * Returns the position within the sorted set that an element should be
+ * inserted at to maintain the current order of the set.
+ *
+ * This function assumes that the element to search for does not already exist
+ * in the sorted set.
+ *
+ * @param {Object} elem The elem to find the position for in the set
+ * @param {Number} start An optional index at which to start searching from
+ * within the set.
+ * @param {Number} end An optional index at which to stop search from within
+ * the set.
+ * @returns {Number}
+ * @memberOf SortedSet
+ */
+lunr.SortedSet.prototype.locationFor = function (elem, start, end) {
+ var start = start || 0,
+ end = end || this.elements.length,
+ sectionLength = end - start,
+ pivot = start + Math.floor(sectionLength / 2),
+ pivotElem = this.elements[pivot]
+
+ if (sectionLength <= 1) {
+ if (pivotElem > elem) return pivot
+ if (pivotElem < elem) return pivot + 1
+ }
+
+ if (pivotElem < elem) return this.locationFor(elem, pivot, end)
+ if (pivotElem > elem) return this.locationFor(elem, start, pivot)
+}
+
+/**
+ * Creates a new lunr.SortedSet that contains the elements in the intersection
+ * of this set and the passed set.
+ *
+ * @param {lunr.SortedSet} otherSet The set to intersect with this set.
+ * @returns {lunr.SortedSet}
+ * @memberOf SortedSet
+ */
+lunr.SortedSet.prototype.intersect = function (otherSet) {
+ var intersectSet = new lunr.SortedSet,
+ i = 0, j = 0,
+ a_len = this.length, b_len = otherSet.length,
+ a = this.elements, b = otherSet.elements
+
+ while (true) {
+ if (i > a_len - 1 || j > b_len - 1) break
+
+ if (a[i] === b[j]) {
+ intersectSet.add(a[i])
+ i++, j++
+ continue
+ }
+
+ if (a[i] < b[j]) {
+ i++
+ continue
+ }
+
+ if (a[i] > b[j]) {
+ j++
+ continue
+ }
+ };
+
+ return intersectSet
+}
+
+/**
+ * Makes a copy of this set
+ *
+ * @returns {lunr.SortedSet}
+ * @memberOf SortedSet
+ */
+lunr.SortedSet.prototype.clone = function () {
+ var clone = new lunr.SortedSet
+
+ clone.elements = this.toArray()
+ clone.length = clone.elements.length
+
+ return clone
+}
+
+/**
+ * Creates a new lunr.SortedSet that contains the elements in the union
+ * of this set and the passed set.
+ *
+ * @param {lunr.SortedSet} otherSet The set to union with this set.
+ * @returns {lunr.SortedSet}
+ * @memberOf SortedSet
+ */
+lunr.SortedSet.prototype.union = function (otherSet) {
+ var longSet, shortSet, unionSet
+
+ if (this.length >= otherSet.length) {
+ longSet = this, shortSet = otherSet
+ } else {
+ longSet = otherSet, shortSet = this
+ }
+
+ unionSet = longSet.clone()
+
+ unionSet.add.apply(unionSet, shortSet.toArray())
+
+ return unionSet
+}
+
+/**
+ * Returns a representation of the sorted set ready for serialisation.
+ *
+ * @returns {Array}
+ * @memberOf SortedSet
+ */
+lunr.SortedSet.prototype.toJSON = function () {
+ return this.toArray()
+}
+/*!
+ * lunr.Index
+ * Copyright (C) 2014 Oliver Nightingale
+ */
+
+/**
+ * lunr.Index is object that manages a search index. It contains the indexes
+ * and stores all the tokens and document lookups. It also provides the main
+ * user facing API for the library.
+ *
+ * @constructor
+ */
+lunr.Index = function () {
+ this._fields = []
+ this._ref = 'id'
+ this.pipeline = new lunr.Pipeline
+ this.documentStore = new lunr.Store
+ this.tokenStore = new lunr.TokenStore
+ this.corpusTokens = new lunr.SortedSet
+ this.eventEmitter = new lunr.EventEmitter
+
+ this._idfCache = {}
+
+ this.on('add', 'remove', 'update', (function () {
+ this._idfCache = {}
+ }).bind(this))
+}
+
+/**
+ * Bind a handler to events being emitted by the index.
+ *
+ * The handler can be bound to many events at the same time.
+ *
+ * @param {String} [eventName] The name(s) of events to bind the function to.
+ * @param {Function} handler The serialised set to load.
+ * @memberOf Index
+ */
+lunr.Index.prototype.on = function () {
+ var args = Array.prototype.slice.call(arguments)
+ return this.eventEmitter.addListener.apply(this.eventEmitter, args)
+}
+
+/**
+ * Removes a handler from an event being emitted by the index.
+ *
+ * @param {String} eventName The name of events to remove the function from.
+ * @param {Function} handler The serialised set to load.
+ * @memberOf Index
+ */
+lunr.Index.prototype.off = function (name, fn) {
+ return this.eventEmitter.removeListener(name, fn)
+}
+
+/**
+ * Loads a previously serialised index.
+ *
+ * Issues a warning if the index being imported was serialised
+ * by a different version of lunr.
+ *
+ * @param {Object} serialisedData The serialised set to load.
+ * @returns {lunr.Index}
+ * @memberOf Index
+ */
+lunr.Index.load = function (serialisedData) {
+ if (serialisedData.version !== lunr.version) {
+ lunr.utils.warn('version mismatch: current ' + lunr.version + ' importing ' + serialisedData.version)
+ }
+
+ var idx = new this
+
+ idx._fields = serialisedData.fields
+ idx._ref = serialisedData.ref
+
+ idx.documentStore = lunr.Store.load(serialisedData.documentStore)
+ idx.tokenStore = lunr.TokenStore.load(serialisedData.tokenStore)
+ idx.corpusTokens = lunr.SortedSet.load(serialisedData.corpusTokens)
+ idx.pipeline = lunr.Pipeline.load(serialisedData.pipeline)
+
+ return idx
+}
+
+/**
+ * Adds a field to the list of fields that will be searchable within documents
+ * in the index.
+ *
+ * An optional boost param can be passed to affect how much tokens in this field
+ * rank in search results, by default the boost value is 1.
+ *
+ * Fields should be added before any documents are added to the index, fields
+ * that are added after documents are added to the index will only apply to new
+ * documents added to the index.
+ *
+ * @param {String} fieldName The name of the field within the document that
+ * should be indexed
+ * @param {Number} boost An optional boost that can be applied to terms in this
+ * field.
+ * @returns {lunr.Index}
+ * @memberOf Index
+ */
+lunr.Index.prototype.field = function (fieldName, opts) {
+ var opts = opts || {},
+ field = { name: fieldName, boost: opts.boost || 1 }
+
+ this._fields.push(field)
+ return this
+}
+
+/**
+ * Sets the property used to uniquely identify documents added to the index,
+ * by default this property is 'id'.
+ *
+ * This should only be changed before adding documents to the index, changing
+ * the ref property without resetting the index can lead to unexpected results.
+ *
+ * @param {String} refName The property to use to uniquely identify the
+ * documents in the index.
+ * @param {Boolean} emitEvent Whether to emit add events, defaults to true
+ * @returns {lunr.Index}
+ * @memberOf Index
+ */
+lunr.Index.prototype.ref = function (refName) {
+ this._ref = refName
+ return this
+}
+
+/**
+ * Add a document to the index.
+ *
+ * This is the way new documents enter the index, this function will run the
+ * fields from the document through the index's pipeline and then add it to
+ * the index, it will then show up in search results.
+ *
+ * An 'add' event is emitted with the document that has been added and the index
+ * the document has been added to. This event can be silenced by passing false
+ * as the second argument to add.
+ *
+ * @param {Object} doc The document to add to the index.
+ * @param {Boolean} emitEvent Whether or not to emit events, default true.
+ * @memberOf Index
+ */
+lunr.Index.prototype.add = function (doc, emitEvent) {
+ var docTokens = {},
+ allDocumentTokens = new lunr.SortedSet,
+ docRef = doc[this._ref],
+ emitEvent = emitEvent === undefined ? true : emitEvent
+
+ this._fields.forEach(function (field) {
+ var fieldTokens = this.pipeline.run(lunr.tokenizer(doc[field.name]))
+
+ docTokens[field.name] = fieldTokens
+ lunr.SortedSet.prototype.add.apply(allDocumentTokens, fieldTokens)
+ }, this)
+
+ this.documentStore.set(docRef, allDocumentTokens)
+ lunr.SortedSet.prototype.add.apply(this.corpusTokens, allDocumentTokens.toArray())
+
+ for (var i = 0; i < allDocumentTokens.length; i++) {
+ var token = allDocumentTokens.elements[i]
+ var tf = this._fields.reduce(function (memo, field) {
+ var fieldLength = docTokens[field.name].length
+
+ if (!fieldLength) return memo
+
+ var tokenCount = docTokens[field.name].filter(function (t) { return t === token }).length
+
+ return memo + (tokenCount / fieldLength * field.boost)
+ }, 0)
+
+ this.tokenStore.add(token, { ref: docRef, tf: tf })
+ };
+
+ if (emitEvent) this.eventEmitter.emit('add', doc, this)
+}
+
+/**
+ * Removes a document from the index.
+ *
+ * To make sure documents no longer show up in search results they can be
+ * removed from the index using this method.
+ *
+ * The document passed only needs to have the same ref property value as the
+ * document that was added to the index, they could be completely different
+ * objects.
+ *
+ * A 'remove' event is emitted with the document that has been removed and the index
+ * the document has been removed from. This event can be silenced by passing false
+ * as the second argument to remove.
+ *
+ * @param {Object} doc The document to remove from the index.
+ * @param {Boolean} emitEvent Whether to emit remove events, defaults to true
+ * @memberOf Index
+ */
+lunr.Index.prototype.remove = function (doc, emitEvent) {
+ var docRef = doc[this._ref],
+ emitEvent = emitEvent === undefined ? true : emitEvent
+
+ if (!this.documentStore.has(docRef)) return
+
+ var docTokens = this.documentStore.get(docRef)
+
+ this.documentStore.remove(docRef)
+
+ docTokens.forEach(function (token) {
+ this.tokenStore.remove(token, docRef)
+ }, this)
+
+ if (emitEvent) this.eventEmitter.emit('remove', doc, this)
+}
+
+/**
+ * Updates a document in the index.
+ *
+ * When a document contained within the index gets updated, fields changed,
+ * added or removed, to make sure it correctly matched against search queries,
+ * it should be updated in the index.
+ *
+ * This method is just a wrapper around `remove` and `add`
+ *
+ * An 'update' event is emitted with the document that has been updated and the index.
+ * This event can be silenced by passing false as the second argument to update. Only
+ * an update event will be fired, the 'add' and 'remove' events of the underlying calls
+ * are silenced.
+ *
+ * @param {Object} doc The document to update in the index.
+ * @param {Boolean} emitEvent Whether to emit update events, defaults to true
+ * @see Index.prototype.remove
+ * @see Index.prototype.add
+ * @memberOf Index
+ */
+lunr.Index.prototype.update = function (doc, emitEvent) {
+ var emitEvent = emitEvent === undefined ? true : emitEvent
+
+ this.remove(doc, false)
+ this.add(doc, false)
+
+ if (emitEvent) this.eventEmitter.emit('update', doc, this)
+}
+
+/**
+ * Calculates the inverse document frequency for a token within the index.
+ *
+ * @param {String} token The token to calculate the idf of.
+ * @see Index.prototype.idf
+ * @private
+ * @memberOf Index
+ */
+lunr.Index.prototype.idf = function (term) {
+ var cacheKey = "@" + term
+ if (Object.prototype.hasOwnProperty.call(this._idfCache, cacheKey)) return this._idfCache[cacheKey]
+
+ var documentFrequency = this.tokenStore.count(term),
+ idf = 1
+
+ if (documentFrequency > 0) {
+ idf = 1 + Math.log(this.tokenStore.length / documentFrequency)
+ }
+
+ return this._idfCache[cacheKey] = idf
+}
+
+/**
+ * Searches the index using the passed query.
+ *
+ * Queries should be a string, multiple words are allowed and will lead to an
+ * AND based query, e.g. `idx.search('foo bar')` will run a search for
+ * documents containing both 'foo' and 'bar'.
+ *
+ * All query tokens are passed through the same pipeline that document tokens
+ * are passed through, so any language processing involved will be run on every
+ * query term.
+ *
+ * Each query term is expanded, so that the term 'he' might be expanded to
+ * 'hello' and 'help' if those terms were already included in the index.
+ *
+ * Matching documents are returned as an array of objects, each object contains
+ * the matching document ref, as set for this index, and the similarity score
+ * for this document against the query.
+ *
+ * @param {String} query The query to search the index with.
+ * @returns {Object}
+ * @see Index.prototype.idf
+ * @see Index.prototype.documentVector
+ * @memberOf Index
+ */
+lunr.Index.prototype.search = function (query) {
+ var queryTokens = this.pipeline.run(lunr.tokenizer(query)),
+ queryVector = new lunr.Vector,
+ documentSets = [],
+ fieldBoosts = this._fields.reduce(function (memo, f) { return memo + f.boost }, 0)
+
+ var hasSomeToken = queryTokens.some(function (token) {
+ return this.tokenStore.has(token)
+ }, this)
+
+ if (!hasSomeToken) return []
+
+ queryTokens
+ .forEach(function (token, i, tokens) {
+ var tf = 1 / tokens.length * this._fields.length * fieldBoosts,
+ self = this
+
+ var set = this.tokenStore.expand(token).reduce(function (memo, key) {
+ var pos = self.corpusTokens.indexOf(key),
+ idf = self.idf(key),
+ similarityBoost = 1,
+ set = new lunr.SortedSet
+
+ // if the expanded key is not an exact match to the token then
+ // penalise the score for this key by how different the key is
+ // to the token.
+ if (key !== token) {
+ var diff = Math.max(3, key.length - token.length)
+ similarityBoost = 1 / Math.log(diff)
+ }
+
+ // calculate the query tf-idf score for this token
+ // applying an similarityBoost to ensure exact matches
+ // these rank higher than expanded terms
+ if (pos > -1) queryVector.insert(pos, tf * idf * similarityBoost)
+
+ // add all the documents that have this key into a set
+ Object.keys(self.tokenStore.get(key)).forEach(function (ref) { set.add(ref) })
+
+ return memo.union(set)
+ }, new lunr.SortedSet)
+
+ documentSets.push(set)
+ }, this)
+
+ var documentSet = documentSets.reduce(function (memo, set) {
+ return memo.intersect(set)
+ })
+
+ return documentSet
+ .map(function (ref) {
+ return { ref: ref, score: queryVector.similarity(this.documentVector(ref)) }
+ }, this)
+ .sort(function (a, b) {
+ return b.score - a.score
+ })
+}
+
+/**
+ * Generates a vector containing all the tokens in the document matching the
+ * passed documentRef.
+ *
+ * The vector contains the tf-idf score for each token contained in the
+ * document with the passed documentRef. The vector will contain an element
+ * for every token in the indexes corpus, if the document does not contain that
+ * token the element will be 0.
+ *
+ * @param {Object} documentRef The ref to find the document with.
+ * @returns {lunr.Vector}
+ * @private
+ * @memberOf Index
+ */
+lunr.Index.prototype.documentVector = function (documentRef) {
+ var documentTokens = this.documentStore.get(documentRef),
+ documentTokensLength = documentTokens.length,
+ documentVector = new lunr.Vector
+
+ for (var i = 0; i < documentTokensLength; i++) {
+ var token = documentTokens.elements[i],
+ tf = this.tokenStore.get(token)[documentRef].tf,
+ idf = this.idf(token)
+
+ documentVector.insert(this.corpusTokens.indexOf(token), tf * idf)
+ };
+
+ return documentVector
+}
+
+/**
+ * Returns a representation of the index ready for serialisation.
+ *
+ * @returns {Object}
+ * @memberOf Index
+ */
+lunr.Index.prototype.toJSON = function () {
+ return {
+ version: lunr.version,
+ fields: this._fields,
+ ref: this._ref,
+ documentStore: this.documentStore.toJSON(),
+ tokenStore: this.tokenStore.toJSON(),
+ corpusTokens: this.corpusTokens.toJSON(),
+ pipeline: this.pipeline.toJSON()
+ }
+}
+
+/**
+ * Applies a plugin to the current index.
+ *
+ * A plugin is a function that is called with the index as its context.
+ * Plugins can be used to customise or extend the behaviour the index
+ * in some way. A plugin is just a function, that encapsulated the custom
+ * behaviour that should be applied to the index.
+ *
+ * The plugin function will be called with the index as its argument, additional
+ * arguments can also be passed when calling use. The function will be called
+ * with the index as its context.
+ *
+ * Example:
+ *
+ * var myPlugin = function (idx, arg1, arg2) {
+ * // `this` is the index to be extended
+ * // apply any extensions etc here.
+ * }
+ *
+ * var idx = lunr(function () {
+ * this.use(myPlugin, 'arg1', 'arg2')
+ * })
+ *
+ * @param {Function} plugin The plugin to apply.
+ * @memberOf Index
+ */
+lunr.Index.prototype.use = function (plugin) {
+ var args = Array.prototype.slice.call(arguments, 1)
+ args.unshift(this)
+ plugin.apply(this, args)
+}
+/*!
+ * lunr.Store
+ * Copyright (C) 2014 Oliver Nightingale
+ */
+
+/**
+ * lunr.Store is a simple key-value store used for storing sets of tokens for
+ * documents stored in index.
+ *
+ * @constructor
+ * @module
+ */
+lunr.Store = function () {
+ this.store = {}
+ this.length = 0
+}
+
+/**
+ * Loads a previously serialised store
+ *
+ * @param {Object} serialisedData The serialised store to load.
+ * @returns {lunr.Store}
+ * @memberOf Store
+ */
+lunr.Store.load = function (serialisedData) {
+ var store = new this
+
+ store.length = serialisedData.length
+ store.store = Object.keys(serialisedData.store).reduce(function (memo, key) {
+ memo[key] = lunr.SortedSet.load(serialisedData.store[key])
+ return memo
+ }, {})
+
+ return store
+}
+
+/**
+ * Stores the given tokens in the store against the given id.
+ *
+ * @param {Object} id The key used to store the tokens against.
+ * @param {Object} tokens The tokens to store against the key.
+ * @memberOf Store
+ */
+lunr.Store.prototype.set = function (id, tokens) {
+ if (!this.has(id)) this.length++
+ this.store[id] = tokens
+}
+
+/**
+ * Retrieves the tokens from the store for a given key.
+ *
+ * @param {Object} id The key to lookup and retrieve from the store.
+ * @returns {Object}
+ * @memberOf Store
+ */
+lunr.Store.prototype.get = function (id) {
+ return this.store[id]
+}
+
+/**
+ * Checks whether the store contains a key.
+ *
+ * @param {Object} id The id to look up in the store.
+ * @returns {Boolean}
+ * @memberOf Store
+ */
+lunr.Store.prototype.has = function (id) {
+ return id in this.store
+}
+
+/**
+ * Removes the value for a key in the store.
+ *
+ * @param {Object} id The id to remove from the store.
+ * @memberOf Store
+ */
+lunr.Store.prototype.remove = function (id) {
+ if (!this.has(id)) return
+
+ delete this.store[id]
+ this.length--
+}
+
+/**
+ * Returns a representation of the store ready for serialisation.
+ *
+ * @returns {Object}
+ * @memberOf Store
+ */
+lunr.Store.prototype.toJSON = function () {
+ return {
+ store: this.store,
+ length: this.length
+ }
+}
+
+/*!
+ * lunr.stemmer
+ * Copyright (C) 2014 Oliver Nightingale
+ * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
+ */
+
+/**
+ * lunr.stemmer is an english language stemmer, this is a JavaScript
+ * implementation of the PorterStemmer taken from http://tartaurs.org/~martin
+ *
+ * @module
+ * @param {String} str The string to stem
+ * @returns {String}
+ * @see lunr.Pipeline
+ */
+lunr.stemmer = (function(){
+ var step2list = {
+ "ational" : "ate",
+ "tional" : "tion",
+ "enci" : "ence",
+ "anci" : "ance",
+ "izer" : "ize",
+ "bli" : "ble",
+ "alli" : "al",
+ "entli" : "ent",
+ "eli" : "e",
+ "ousli" : "ous",
+ "ization" : "ize",
+ "ation" : "ate",
+ "ator" : "ate",
+ "alism" : "al",
+ "iveness" : "ive",
+ "fulness" : "ful",
+ "ousness" : "ous",
+ "aliti" : "al",
+ "iviti" : "ive",
+ "biliti" : "ble",
+ "logi" : "log"
+ },
+
+ step3list = {
+ "icate" : "ic",
+ "ative" : "",
+ "alize" : "al",
+ "iciti" : "ic",
+ "ical" : "ic",
+ "ful" : "",
+ "ness" : ""
+ },
+
+ c = "[^aeiou]", // consonant
+ v = "[aeiouy]", // vowel
+ C = c + "[^aeiouy]*", // consonant sequence
+ V = v + "[aeiou]*", // vowel sequence
+
+ mgr0 = "^(" + C + ")?" + V + C, // [C]VC... is m>0
+ meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$", // [C]VC[V] is m=1
+ mgr1 = "^(" + C + ")?" + V + C + V + C, // [C]VCVC... is m>1
+ s_v = "^(" + C + ")?" + v; // vowel in stem
+
+ var re_mgr0 = new RegExp(mgr0);
+ var re_mgr1 = new RegExp(mgr1);
+ var re_meq1 = new RegExp(meq1);
+ var re_s_v = new RegExp(s_v);
+
+ var re_1a = /^(.+?)(ss|i)es$/;
+ var re2_1a = /^(.+?)([^s])s$/;
+ var re_1b = /^(.+?)eed$/;
+ var re2_1b = /^(.+?)(ed|ing)$/;
+ var re_1b_2 = /.$/;
+ var re2_1b_2 = /(at|bl|iz)$/;
+ var re3_1b_2 = new RegExp("([^aeiouylsz])\\1$");
+ var re4_1b_2 = new RegExp("^" + C + v + "[^aeiouwxy]$");
+
+ var re_1c = /^(.+?[^aeiou])y$/;
+ var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
+
+ var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
+
+ var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
+ var re2_4 = /^(.+?)(s|t)(ion)$/;
+
+ var re_5 = /^(.+?)e$/;
+ var re_5_1 = /ll$/;
+ var re3_5 = new RegExp("^" + C + v + "[^aeiouwxy]$");
+
+ var porterStemmer = function porterStemmer(w) {
+ var stem,
+ suffix,
+ firstch,
+ re,
+ re2,
+ re3,
+ re4;
+
+ if (w.length < 3) { return w; }
+
+ firstch = w.substr(0,1);
+ if (firstch == "y") {
+ w = firstch.toUpperCase() + w.substr(1);
+ }
+
+ // Step 1a
+ re = re_1a
+ re2 = re2_1a;
+
+ if (re.test(w)) { w = w.replace(re,"$1$2"); }
+ else if (re2.test(w)) { w = w.replace(re2,"$1$2"); }
+
+ // Step 1b
+ re = re_1b;
+ re2 = re2_1b;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ re = re_mgr0;
+ if (re.test(fp[1])) {
+ re = re_1b_2;
+ w = w.replace(re,"");
+ }
+ } else if (re2.test(w)) {
+ var fp = re2.exec(w);
+ stem = fp[1];
+ re2 = re_s_v;
+ if (re2.test(stem)) {
+ w = stem;
+ re2 = re2_1b_2;
+ re3 = re3_1b_2;
+ re4 = re4_1b_2;
+ if (re2.test(w)) { w = w + "e"; }
+ else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,""); }
+ else if (re4.test(w)) { w = w + "e"; }
+ }
+ }
+
+ // Step 1c - replace suffix y or Y by i if preceded by a non-vowel which is not the first letter of the word (so cry -> cri, by -> by, say -> say)
+ re = re_1c;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ w = stem + "i";
+ }
+
+ // Step 2
+ re = re_2;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ suffix = fp[2];
+ re = re_mgr0;
+ if (re.test(stem)) {
+ w = stem + step2list[suffix];
+ }
+ }
+
+ // Step 3
+ re = re_3;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ suffix = fp[2];
+ re = re_mgr0;
+ if (re.test(stem)) {
+ w = stem + step3list[suffix];
+ }
+ }
+
+ // Step 4
+ re = re_4;
+ re2 = re2_4;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ re = re_mgr1;
+ if (re.test(stem)) {
+ w = stem;
+ }
+ } else if (re2.test(w)) {
+ var fp = re2.exec(w);
+ stem = fp[1] + fp[2];
+ re2 = re_mgr1;
+ if (re2.test(stem)) {
+ w = stem;
+ }
+ }
+
+ // Step 5
+ re = re_5;
+ if (re.test(w)) {
+ var fp = re.exec(w);
+ stem = fp[1];
+ re = re_mgr1;
+ re2 = re_meq1;
+ re3 = re3_5;
+ if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {
+ w = stem;
+ }
+ }
+
+ re = re_5_1;
+ re2 = re_mgr1;
+ if (re.test(w) && re2.test(w)) {
+ re = re_1b_2;
+ w = w.replace(re,"");
+ }
+
+ // and turn initial Y back to y
+
+ if (firstch == "y") {
+ w = firstch.toLowerCase() + w.substr(1);
+ }
+
+ return w;
+ };
+
+ return porterStemmer;
+})();
+
+lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer')
+/*!
+ * lunr.stopWordFilter
+ * Copyright (C) 2014 Oliver Nightingale
+ */
+
+/**
+ * lunr.stopWordFilter is an English language stop word list filter, any words
+ * contained in the list will not be passed through the filter.
+ *
+ * This is intended to be used in the Pipeline. If the token does not pass the
+ * filter then undefined will be returned.
+ *
+ * @module
+ * @param {String} token The token to pass through the filter
+ * @returns {String}
+ * @see lunr.Pipeline
+ */
+lunr.stopWordFilter = function (token) {
+ if (lunr.stopWordFilter.stopWords.indexOf(token) === -1) return token
+}
+
+lunr.stopWordFilter.stopWords = new lunr.SortedSet
+lunr.stopWordFilter.stopWords.length = 119
+lunr.stopWordFilter.stopWords.elements = [
+ "",
+ "a",
+ "able",
+ "about",
+ "across",
+ "after",
+ "all",
+ "almost",
+ "also",
+ "am",
+ "among",
+ "an",
+ "and",
+ "any",
+ "are",
+ "as",
+ "at",
+ "be",
+ "because",
+ "been",
+ "but",
+ "by",
+ "can",
+ "cannot",
+ "could",
+ "dear",
+ "did",
+ "do",
+ "does",
+ "either",
+ "else",
+ "ever",
+ "every",
+ "for",
+ "from",
+ "get",
+ "got",
+ "had",
+ "has",
+ "have",
+ "he",
+ "her",
+ "hers",
+ "him",
+ "his",
+ "how",
+ "however",
+ "i",
+ "if",
+ "in",
+ "into",
+ "is",
+ "it",
+ "its",
+ "just",
+ "least",
+ "let",
+ "like",
+ "likely",
+ "may",
+ "me",
+ "might",
+ "most",
+ "must",
+ "my",
+ "neither",
+ "no",
+ "nor",
+ "not",
+ "of",
+ "off",
+ "often",
+ "on",
+ "only",
+ "or",
+ "other",
+ "our",
+ "own",
+ "rather",
+ "said",
+ "say",
+ "says",
+ "she",
+ "should",
+ "since",
+ "so",
+ "some",
+ "than",
+ "that",
+ "the",
+ "their",
+ "them",
+ "then",
+ "there",
+ "these",
+ "they",
+ "this",
+ "tis",
+ "to",
+ "too",
+ "twas",
+ "us",
+ "wants",
+ "was",
+ "we",
+ "were",
+ "what",
+ "when",
+ "where",
+ "which",
+ "while",
+ "who",
+ "whom",
+ "why",
+ "will",
+ "with",
+ "would",
+ "yet",
+ "you",
+ "your"
+]
+
+lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter')
+/*!
+ * lunr.trimmer
+ * Copyright (C) 2014 Oliver Nightingale
+ */
+
+/**
+ * lunr.trimmer is a pipeline function for trimming non word
+ * characters from the begining and end of tokens before they
+ * enter the index.
+ *
+ * This implementation may not work correctly for non latin
+ * characters and should either be removed or adapted for use
+ * with languages with non-latin characters.
+ *
+ * @module
+ * @param {String} token The token to pass through the filter
+ * @returns {String}
+ * @see lunr.Pipeline
+ */
+lunr.trimmer = function (token) {
+ return token
+ .replace(/^\W+/, '')
+ .replace(/\W+$/, '')
+}
+
+lunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer')
+/*!
+ * lunr.stemmer
+ * Copyright (C) 2014 Oliver Nightingale
+ * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
+ */
+
+/**
+ * lunr.TokenStore is used for efficient storing and lookup of the reverse
+ * index of token to document ref.
+ *
+ * @constructor
+ */
+lunr.TokenStore = function () {
+ this.root = { docs: {} }
+ this.length = 0
+}
+
+/**
+ * Loads a previously serialised token store
+ *
+ * @param {Object} serialisedData The serialised token store to load.
+ * @returns {lunr.TokenStore}
+ * @memberOf TokenStore
+ */
+lunr.TokenStore.load = function (serialisedData) {
+ var store = new this
+
+ store.root = serialisedData.root
+ store.length = serialisedData.length
+
+ return store
+}
+
+/**
+ * Adds a new token doc pair to the store.
+ *
+ * By default this function starts at the root of the current store, however
+ * it can start at any node of any token store if required.
+ *
+ * @param {String} token The token to store the doc under
+ * @param {Object} doc The doc to store against the token
+ * @param {Object} root An optional node at which to start looking for the
+ * correct place to enter the doc, by default the root of this lunr.TokenStore
+ * is used.
+ * @memberOf TokenStore
+ */
+lunr.TokenStore.prototype.add = function (token, doc, root) {
+ var root = root || this.root,
+ key = token[0],
+ rest = token.slice(1)
+
+ if (!(key in root)) root[key] = {docs: {}}
+
+ if (rest.length === 0) {
+ root[key].docs[doc.ref] = doc
+ this.length += 1
+ return
+ } else {
+ return this.add(rest, doc, root[key])
+ }
+}
+
+/**
+ * Checks whether this key is contained within this lunr.TokenStore.
+ *
+ * By default this function starts at the root of the current store, however
+ * it can start at any node of any token store if required.
+ *
+ * @param {String} token The token to check for
+ * @param {Object} root An optional node at which to start
+ * @memberOf TokenStore
+ */
+lunr.TokenStore.prototype.has = function (token) {
+ if (!token) return false
+
+ var node = this.root
+
+ for (var i = 0; i < token.length; i++) {
+ if (!node[token[i]]) return false
+
+ node = node[token[i]]
+ }
+
+ return true
+}
+
+/**
+ * Retrieve a node from the token store for a given token.
+ *
+ * By default this function starts at the root of the current store, however
+ * it can start at any node of any token store if required.
+ *
+ * @param {String} token The token to get the node for.
+ * @param {Object} root An optional node at which to start.
+ * @returns {Object}
+ * @see TokenStore.prototype.get
+ * @memberOf TokenStore
+ */
+lunr.TokenStore.prototype.getNode = function (token) {
+ if (!token) return {}
+
+ var node = this.root
+
+ for (var i = 0; i < token.length; i++) {
+ if (!node[token[i]]) return {}
+
+ node = node[token[i]]
+ }
+
+ return node
+}
+
+/**
+ * Retrieve the documents for a node for the given token.
+ *
+ * By default this function starts at the root of the current store, however
+ * it can start at any node of any token store if required.
+ *
+ * @param {String} token The token to get the documents for.
+ * @param {Object} root An optional node at which to start.
+ * @returns {Object}
+ * @memberOf TokenStore
+ */
+lunr.TokenStore.prototype.get = function (token, root) {
+ return this.getNode(token, root).docs || {}
+}
+
+lunr.TokenStore.prototype.count = function (token, root) {
+ return Object.keys(this.get(token, root)).length
+}
+
+/**
+ * Remove the document identified by ref from the token in the store.
+ *
+ * By default this function starts at the root of the current store, however
+ * it can start at any node of any token store if required.
+ *
+ * @param {String} token The token to get the documents for.
+ * @param {String} ref The ref of the document to remove from this token.
+ * @param {Object} root An optional node at which to start.
+ * @returns {Object}
+ * @memberOf TokenStore
+ */
+lunr.TokenStore.prototype.remove = function (token, ref) {
+ if (!token) return
+ var node = this.root
+
+ for (var i = 0; i < token.length; i++) {
+ if (!(token[i] in node)) return
+ node = node[token[i]]
+ }
+
+ delete node.docs[ref]
+}
+
+/**
+ * Find all the possible suffixes of the passed token using tokens
+ * currently in the store.
+ *
+ * @param {String} token The token to expand.
+ * @returns {Array}
+ * @memberOf TokenStore
+ */
+lunr.TokenStore.prototype.expand = function (token, memo) {
+ var root = this.getNode(token),
+ docs = root.docs || {},
+ memo = memo || []
+
+ if (Object.keys(docs).length) memo.push(token)
+
+ Object.keys(root)
+ .forEach(function (key) {
+ if (key === 'docs') return
+
+ memo.concat(this.expand(token + key, memo))
+ }, this)
+
+ return memo
+}
+
+/**
+ * Returns a representation of the token store ready for serialisation.
+ *
+ * @returns {Object}
+ * @memberOf TokenStore
+ */
+lunr.TokenStore.prototype.toJSON = function () {
+ return {
+ root: this.root,
+ length: this.length
+ }
+}
+
+
+ /**
+ * export the module via AMD, CommonJS or as a browser global
+ * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js
+ */
+ ;(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(factory)
+ } else if (typeof exports === 'object') {
+ /**
+ * Node. Does not work with strict CommonJS, but
+ * only CommonJS-like enviroments that support module.exports,
+ * like Node.
+ */
+ module.exports = factory()
+ } else {
+ // Browser globals (root is window)
+ root.lunr = factory()
+ }
+ }(this, function () {
+ /**
+ * Just return a value to define the module export.
+ * This example returns an object, but the module
+ * can return a function as the exported value.
+ */
+ return lunr
+ }))
+})()
diff --git a/client/galaxy/scripts/libs/raven.js b/client/galaxy/scripts/libs/raven.js
index f8e45d7e50e..37896c4018a 100644
--- a/client/galaxy/scripts/libs/raven.js
+++ b/client/galaxy/scripts/libs/raven.js
@@ -1,11 +1,1089 @@
-(function(){
+/*! Raven.js 1.1.16 (463f68f) | github.com/getsentry/raven-js */
-// Raven.js
-//
-// Originally based on the Arecibo JavaScript client.
-//
-// Requires:
-// * TraceKit (included in the full and minified distribution files)
+/*
+ * Includes TraceKit
+ * https://github.com/getsentry/TraceKit
+ *
+ * Copyright 2014 Matt Robenolt and other contributors
+ * Released under the BSD license
+ * https://github.com/getsentry/raven-js/blob/master/LICENSE
+ *
+ */
+;(function(window, undefined){
+'use strict';
+
+/*
+ TraceKit - Cross brower stack traces - github.com/occ/TraceKit
+ MIT license
+*/
+
+var TraceKit = {
+ remoteFetching: false,
+ collectWindowErrors: true,
+ // 3 lines before, the offending line, 3 lines after
+ linesOfContext: 7
+};
+
+// global reference to slice
+var _slice = [].slice;
+var UNKNOWN_FUNCTION = '?';
+
+
+/**
+ * TraceKit.wrap: Wrap any function in a TraceKit reporter
+ * Example: func = TraceKit.wrap(func);
+ *
+ * @param {Function} func Function to be wrapped
+ * @return {Function} The wrapped func
+ */
+TraceKit.wrap = function traceKitWrapper(func) {
+ function wrapped() {
+ try {
+ return func.apply(this, arguments);
+ } catch (e) {
+ TraceKit.report(e);
+ throw e;
+ }
+ }
+ return wrapped;
+};
+
+/**
+ * TraceKit.report: cross-browser processing of unhandled exceptions
+ *
+ * Syntax:
+ * TraceKit.report.subscribe(function(stackInfo) { ... })
+ * TraceKit.report.unsubscribe(function(stackInfo) { ... })
+ * TraceKit.report(exception)
+ * try { ...code... } catch(ex) { TraceKit.report(ex); }
+ *
+ * Supports:
+ * - Firefox: full stack trace with line numbers, plus column number
+ * on top frame; column number is not guaranteed
+ * - Opera: full stack trace with line and column numbers
+ * - Chrome: full stack trace with line and column numbers
+ * - Safari: line and column number for the top frame only; some frames
+ * may be missing, and column number is not guaranteed
+ * - IE: line and column number for the top frame only; some frames
+ * may be missing, and column number is not guaranteed
+ *
+ * In theory, TraceKit should work on all of the following versions:
+ * - IE5.5+ (only 8.0 tested)
+ * - Firefox 0.9+ (only 3.5+ tested)
+ * - Opera 7+ (only 10.50 tested; versions 9 and earlier may require
+ * Exceptions Have Stacktrace to be enabled in opera:config)
+ * - Safari 3+ (only 4+ tested)
+ * - Chrome 1+ (only 5+ tested)
+ * - Konqueror 3.5+ (untested)
+ *
+ * Requires TraceKit.computeStackTrace.
+ *
+ * Tries to catch all unhandled exceptions and report them to the
+ * subscribed handlers. Please note that TraceKit.report will rethrow the
+ * exception. This is REQUIRED in order to get a useful stack trace in IE.
+ * If the exception does not reach the top of the browser, you will only
+ * get a stack trace from the point where TraceKit.report was called.
+ *
+ * Handlers receive a stackInfo object as described in the
+ * TraceKit.computeStackTrace docs.
+ */
+TraceKit.report = (function reportModuleWrapper() {
+ var handlers = [],
+ lastArgs = null,
+ lastException = null,
+ lastExceptionStack = null;
+
+ /**
+ * Add a crash handler.
+ * @param {Function} handler
+ */
+ function subscribe(handler) {
+ installGlobalHandler();
+ handlers.push(handler);
+ }
+
+ /**
+ * Remove a crash handler.
+ * @param {Function} handler
+ */
+ function unsubscribe(handler) {
+ for (var i = handlers.length - 1; i >= 0; --i) {
+ if (handlers[i] === handler) {
+ handlers.splice(i, 1);
+ }
+ }
+ }
+
+ /**
+ * Remove all crash handlers.
+ */
+ function unsubscribeAll() {
+ uninstallGlobalHandler();
+ handlers = [];
+ }
+
+ /**
+ * Dispatch stack information to all handlers.
+ * @param {Object.} stack
+ */
+ function notifyHandlers(stack, isWindowError) {
+ var exception = null;
+ if (isWindowError && !TraceKit.collectWindowErrors) {
+ return;
+ }
+ for (var i in handlers) {
+ if (hasKey(handlers, i)) {
+ try {
+ handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2)));
+ } catch (inner) {
+ exception = inner;
+ }
+ }
+ }
+
+ if (exception) {
+ throw exception;
+ }
+ }
+
+ var _oldOnerrorHandler, _onErrorHandlerInstalled;
+
+ /**
+ * Ensures all global unhandled exceptions are recorded.
+ * Supported by Gecko and IE.
+ * @param {string} message Error message.
+ * @param {string} url URL of script that generated the exception.
+ * @param {(number|string)} lineNo The line number at which the error
+ * occurred.
+ * @param {?(number|string)} colNo The column number at which the error
+ * occurred.
+ * @param {?Error} ex The actual Error object.
+ */
+ function traceKitWindowOnError(message, url, lineNo, colNo, ex) {
+ var stack = null;
+
+ if (lastExceptionStack) {
+ TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message);
+ processLastException();
+ } else if (ex) {
+ // New chrome and blink send along a real error object
+ // Let's just report that like a normal error.
+ // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
+ stack = TraceKit.computeStackTrace(ex);
+ notifyHandlers(stack, true);
+ } else {
+ var location = {
+ 'url': url,
+ 'line': lineNo,
+ 'column': colNo
+ };
+ location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line);
+ location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);
+ stack = {
+ 'message': message,
+ 'url': document.location.href,
+ 'stack': [location]
+ };
+ notifyHandlers(stack, true);
+ }
+
+ if (_oldOnerrorHandler) {
+ return _oldOnerrorHandler.apply(this, arguments);
+ }
+
+ return false;
+ }
+
+ function installGlobalHandler ()
+ {
+ if (_onErrorHandlerInstalled) {
+ return;
+ }
+ _oldOnerrorHandler = window.onerror;
+ window.onerror = traceKitWindowOnError;
+ _onErrorHandlerInstalled = true;
+ }
+
+ function uninstallGlobalHandler ()
+ {
+ if (!_onErrorHandlerInstalled) {
+ return;
+ }
+ window.onerror = _oldOnerrorHandler;
+ _onErrorHandlerInstalled = false;
+ _oldOnerrorHandler = undefined;
+ }
+
+ function processLastException() {
+ var _lastExceptionStack = lastExceptionStack,
+ _lastArgs = lastArgs;
+ lastArgs = null;
+ lastExceptionStack = null;
+ lastException = null;
+ notifyHandlers.apply(null, [_lastExceptionStack, false].concat(_lastArgs));
+ }
+
+ /**
+ * Reports an unhandled Error to TraceKit.
+ * @param {Error} ex
+ * @param {?boolean} rethrow If false, do not re-throw the exception.
+ * Only used for window.onerror to not cause an infinite loop of
+ * rethrowing.
+ */
+ function report(ex, rethrow) {
+ var args = _slice.call(arguments, 1);
+ if (lastExceptionStack) {
+ if (lastException === ex) {
+ return; // already caught by an inner catch block, ignore
+ } else {
+ processLastException();
+ }
+ }
+
+ var stack = TraceKit.computeStackTrace(ex);
+ lastExceptionStack = stack;
+ lastException = ex;
+ lastArgs = args;
+
+ // If the stack trace is incomplete, wait for 2 seconds for
+ // slow slow IE to see if onerror occurs or not before reporting
+ // this exception; otherwise, we will end up with an incomplete
+ // stack trace
+ window.setTimeout(function () {
+ if (lastException === ex) {
+ processLastException();
+ }
+ }, (stack.incomplete ? 2000 : 0));
+
+ if (rethrow !== false) {
+ throw ex; // re-throw to propagate to the top level (and cause window.onerror)
+ }
+ }
+
+ report.subscribe = subscribe;
+ report.unsubscribe = unsubscribe;
+ report.uninstall = unsubscribeAll;
+ return report;
+}());
+
+/**
+ * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript
+ *
+ * Syntax:
+ * s = TraceKit.computeStackTrace.ofCaller([depth])
+ * s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below)
+ * Returns:
+ * s.name - exception name
+ * s.message - exception message
+ * s.stack[i].url - JavaScript or HTML file URL
+ * s.stack[i].func - function name, or empty for anonymous functions (if guessing did not work)
+ * s.stack[i].args - arguments passed to the function, if known
+ * s.stack[i].line - line number, if known
+ * s.stack[i].column - column number, if known
+ * s.stack[i].context - an array of source code lines; the middle element corresponds to the correct line#
+ *
+ * Supports:
+ * - Firefox: full stack trace with line numbers and unreliable column
+ * number on top frame
+ * - Opera 10: full stack trace with line and column numbers
+ * - Opera 9-: full stack trace with line numbers
+ * - Chrome: full stack trace with line and column numbers
+ * - Safari: line and column number for the topmost stacktrace element
+ * only
+ * - IE: no line numbers whatsoever
+ *
+ * Tries to guess names of anonymous functions by looking for assignments
+ * in the source code. In IE and Safari, we have to guess source file names
+ * by searching for function bodies inside all page scripts. This will not
+ * work for scripts that are loaded cross-domain.
+ * Here be dragons: some function names may be guessed incorrectly, and
+ * duplicate functions may be mismatched.
+ *
+ * TraceKit.computeStackTrace should only be used for tracing purposes.
+ * Logging of unhandled exceptions should be done with TraceKit.report,
+ * which builds on top of TraceKit.computeStackTrace and provides better
+ * IE support by utilizing the window.onerror event to retrieve information
+ * about the top of the stack.
+ *
+ * Note: In IE and Safari, no stack trace is recorded on the Error object,
+ * so computeStackTrace instead walks its *own* chain of callers.
+ * This means that:
+ * * in Safari, some methods may be missing from the stack trace;
+ * * in IE, the topmost function in the stack trace will always be the
+ * caller of computeStackTrace.
+ *
+ * This is okay for tracing (because you are likely to be calling
+ * computeStackTrace from the function you want to be the topmost element
+ * of the stack trace anyway), but not okay for logging unhandled
+ * exceptions (because your catch block will likely be far away from the
+ * inner function that actually caused the exception).
+ *
+ * Tracing example:
+ * function trace(message) {
+ * var stackInfo = TraceKit.computeStackTrace.ofCaller();
+ * var data = message + "\n";
+ * for(var i in stackInfo.stack) {
+ * var item = stackInfo.stack[i];
+ * data += (item.func || '[anonymous]') + "() in " + item.url + ":" + (item.line || '0') + "\n";
+ * }
+ * if (window.console)
+ * console.info(data);
+ * else
+ * alert(data);
+ * }
+ */
+TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
+ var debug = false,
+ sourceCache = {};
+
+ /**
+ * Attempts to retrieve source code via XMLHttpRequest, which is used
+ * to look up anonymous function names.
+ * @param {string} url URL of source code.
+ * @return {string} Source contents.
+ */
+ function loadSource(url) {
+ if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on.
+ return '';
+ }
+ try {
+ var getXHR = function() {
+ try {
+ return new window.XMLHttpRequest();
+ } catch (e) {
+ // explicitly bubble up the exception if not found
+ return new window.ActiveXObject('Microsoft.XMLHTTP');
+ }
+ };
+
+ var request = getXHR();
+ request.open('GET', url, false);
+ request.send('');
+ return request.responseText;
+ } catch (e) {
+ return '';
+ }
+ }
+
+ /**
+ * Retrieves source code from the source code cache.
+ * @param {string} url URL of source code.
+ * @return {Array.} Source contents.
+ */
+ function getSource(url) {
+ if (!isString(url)) return [];
+ if (!hasKey(sourceCache, url)) {
+ // URL needs to be able to fetched within the acceptable domain. Otherwise,
+ // cross-domain errors will be triggered.
+ var source = '';
+ if (url.indexOf(document.domain) !== -1) {
+ source = loadSource(url);
+ }
+ sourceCache[url] = source ? source.split('\n') : [];
+ }
+
+ return sourceCache[url];
+ }
+
+ /**
+ * Tries to use an externally loaded copy of source code to determine
+ * the name of a function by looking at the name of the variable it was
+ * assigned to, if any.
+ * @param {string} url URL of source code.
+ * @param {(string|number)} lineNo Line number in source code.
+ * @return {string} The function name, if discoverable.
+ */
+ function guessFunctionName(url, lineNo) {
+ var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/,
+ reGuessFunction = /['"]?([0-9A-Za-z$_]+)['"]?\s*[:=]\s*(function|eval|new Function)/,
+ line = '',
+ maxLines = 10,
+ source = getSource(url),
+ m;
+
+ if (!source.length) {
+ return UNKNOWN_FUNCTION;
+ }
+
+ // Walk backwards from the first line in the function until we find the line which
+ // matches the pattern above, which is the function definition
+ for (var i = 0; i < maxLines; ++i) {
+ line = source[lineNo - i] + line;
+
+ if (!isUndefined(line)) {
+ if ((m = reGuessFunction.exec(line))) {
+ return m[1];
+ } else if ((m = reFunctionArgNames.exec(line))) {
+ return m[1];
+ }
+ }
+ }
+
+ return UNKNOWN_FUNCTION;
+ }
+
+ /**
+ * Retrieves the surrounding lines from where an exception occurred.
+ * @param {string} url URL of source code.
+ * @param {(string|number)} line Line number in source code to centre
+ * around for context.
+ * @return {?Array.} Lines of source code.
+ */
+ function gatherContext(url, line) {
+ var source = getSource(url);
+
+ if (!source.length) {
+ return null;
+ }
+
+ var context = [],
+ // linesBefore & linesAfter are inclusive with the offending line.
+ // if linesOfContext is even, there will be one extra line
+ // *before* the offending line.
+ linesBefore = Math.floor(TraceKit.linesOfContext / 2),
+ // Add one extra line if linesOfContext is odd
+ linesAfter = linesBefore + (TraceKit.linesOfContext % 2),
+ start = Math.max(0, line - linesBefore - 1),
+ end = Math.min(source.length, line + linesAfter - 1);
+
+ line -= 1; // convert to 0-based index
+
+ for (var i = start; i < end; ++i) {
+ if (!isUndefined(source[i])) {
+ context.push(source[i]);
+ }
+ }
+
+ return context.length > 0 ? context : null;
+ }
+
+ /**
+ * Escapes special characters, except for whitespace, in a string to be
+ * used inside a regular expression as a string literal.
+ * @param {string} text The string.
+ * @return {string} The escaped string literal.
+ */
+ function escapeRegExp(text) {
+ return text.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, '\\$&');
+ }
+
+ /**
+ * Escapes special characters in a string to be used inside a regular
+ * expression as a string literal. Also ensures that HTML entities will
+ * be matched the same as their literal friends.
+ * @param {string} body The string.
+ * @return {string} The escaped string.
+ */
+ function escapeCodeAsRegExpForMatchingInsideHTML(body) {
+ return escapeRegExp(body).replace('<', '(?:<|<)').replace('>', '(?:>|>)').replace('&', '(?:&|&)').replace('"', '(?:"|")').replace(/\s+/g, '\\s+');
+ }
+
+ /**
+ * Determines where a code fragment occurs in the source code.
+ * @param {RegExp} re The function definition.
+ * @param {Array.} urls A list of URLs to search.
+ * @return {?Object.} An object containing
+ * the url, line, and column number of the defined function.
+ */
+ function findSourceInUrls(re, urls) {
+ var source, m;
+ for (var i = 0, j = urls.length; i < j; ++i) {
+ // console.log('searching', urls[i]);
+ if ((source = getSource(urls[i])).length) {
+ source = source.join('\n');
+ if ((m = re.exec(source))) {
+ // console.log('Found function in ' + urls[i]);
+
+ return {
+ 'url': urls[i],
+ 'line': source.substring(0, m.index).split('\n').length,
+ 'column': m.index - source.lastIndexOf('\n', m.index) - 1
+ };
+ }
+ }
+ }
+
+ // console.log('no match');
+
+ return null;
+ }
+
+ /**
+ * Determines at which column a code fragment occurs on a line of the
+ * source code.
+ * @param {string} fragment The code fragment.
+ * @param {string} url The URL to search.
+ * @param {(string|number)} line The line number to examine.
+ * @return {?number} The column number.
+ */
+ function findSourceInLine(fragment, url, line) {
+ var source = getSource(url),
+ re = new RegExp('\\b' + escapeRegExp(fragment) + '\\b'),
+ m;
+
+ line -= 1;
+
+ if (source && source.length > line && (m = re.exec(source[line]))) {
+ return m.index;
+ }
+
+ return null;
+ }
+
+ /**
+ * Determines where a function was defined within the source code.
+ * @param {(Function|string)} func A function reference or serialized
+ * function definition.
+ * @return {?Object.} An object containing
+ * the url, line, and column number of the defined function.
+ */
+ function findSourceByFunctionBody(func) {
+ var urls = [window.location.href],
+ scripts = document.getElementsByTagName('script'),
+ body,
+ code = '' + func,
+ codeRE = /^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,
+ eventRE = /^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/,
+ re,
+ parts,
+ result;
+
+ for (var i = 0; i < scripts.length; ++i) {
+ var script = scripts[i];
+ if (script.src) {
+ urls.push(script.src);
+ }
+ }
+
+ if (!(parts = codeRE.exec(code))) {
+ re = new RegExp(escapeRegExp(code).replace(/\s+/g, '\\s+'));
+ }
+
+ // not sure if this is really necessary, but I don’t have a test
+ // corpus large enough to confirm that and it was in the original.
+ else {
+ var name = parts[1] ? '\\s+' + parts[1] : '',
+ args = parts[2].split(',').join('\\s*,\\s*');
+
+ body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\s+/g, '\\s+');
+ re = new RegExp('function' + name + '\\s*\\(\\s*' + args + '\\s*\\)\\s*{\\s*' + body + '\\s*}');
+ }
+
+ // look for a normal function definition
+ if ((result = findSourceInUrls(re, urls))) {
+ return result;
+ }
+
+ // look for an old-school event handler function
+ if ((parts = eventRE.exec(code))) {
+ var event = parts[1];
+ body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]);
+
+ // look for a function defined in HTML as an onXXX handler
+ re = new RegExp('on' + event + '=[\\\'"]\\s*' + body + '\\s*[\\\'"]', 'i');
+
+ if ((result = findSourceInUrls(re, urls[0]))) {
+ return result;
+ }
+
+ // look for ???
+ re = new RegExp(body);
+
+ if ((result = findSourceInUrls(re, urls))) {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ // Contents of Exception in various browsers.
+ //
+ // SAFARI:
+ // ex.message = Can't find variable: qq
+ // ex.line = 59
+ // ex.sourceId = 580238192
+ // ex.sourceURL = http://...
+ // ex.expressionBeginOffset = 96
+ // ex.expressionCaretOffset = 98
+ // ex.expressionEndOffset = 98
+ // ex.name = ReferenceError
+ //
+ // FIREFOX:
+ // ex.message = qq is not defined
+ // ex.fileName = http://...
+ // ex.lineNumber = 59
+ // ex.columnNumber = 69
+ // ex.stack = ...stack trace... (see the example below)
+ // ex.name = ReferenceError
+ //
+ // CHROME:
+ // ex.message = qq is not defined
+ // ex.name = ReferenceError
+ // ex.type = not_defined
+ // ex.arguments = ['aa']
+ // ex.stack = ...stack trace...
+ //
+ // INTERNET EXPLORER:
+ // ex.message = ...
+ // ex.name = ReferenceError
+ //
+ // OPERA:
+ // ex.message = ...message... (see the example below)
+ // ex.name = ReferenceError
+ // ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message)
+ // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace'
+
+ /**
+ * Computes stack trace information from the stack property.
+ * Chrome and Gecko use this property.
+ * @param {Error} ex
+ * @return {?Object.} Stack trace information.
+ */
+ function computeStackTraceFromStackProp(ex) {
+ if (!ex.stack) {
+ return null;
+ }
+
+ var chrome = /^\s*at (?:((?:\[object object\])?\S+(?: \[as \S+\])?) )?\(?((?:file|https?|chrome-extension):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
+ gecko = /^\s*(\S*)(?:\((.*?)\))?@((?:file|https?|chrome).*?):(\d+)(?::(\d+))?\s*$/i,
+ lines = ex.stack.split('\n'),
+ stack = [],
+ parts,
+ element,
+ reference = /^(.*) is undefined$/.exec(ex.message);
+
+ for (var i = 0, j = lines.length; i < j; ++i) {
+ if ((parts = gecko.exec(lines[i]))) {
+ element = {
+ 'url': parts[3],
+ 'func': parts[1] || UNKNOWN_FUNCTION,
+ 'args': parts[2] ? parts[2].split(',') : '',
+ 'line': +parts[4],
+ 'column': parts[5] ? +parts[5] : null
+ };
+ } else if ((parts = chrome.exec(lines[i]))) {
+ element = {
+ 'url': parts[2],
+ 'func': parts[1] || UNKNOWN_FUNCTION,
+ 'line': +parts[3],
+ 'column': parts[4] ? +parts[4] : null
+ };
+ } else {
+ continue;
+ }
+
+ if (!element.func && element.line) {
+ element.func = guessFunctionName(element.url, element.line);
+ }
+
+ if (element.line) {
+ element.context = gatherContext(element.url, element.line);
+ }
+
+ stack.push(element);
+ }
+
+ if (!stack.length) {
+ return null;
+ }
+
+ if (stack[0].line && !stack[0].column && reference) {
+ stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line);
+ } else if (!stack[0].column && !isUndefined(ex.columnNumber)) {
+ // FireFox uses this awesome columnNumber property for its top frame
+ // Also note, Firefox's column number is 0-based and everything else expects 1-based,
+ // so adding 1
+ stack[0].column = ex.columnNumber + 1;
+ }
+
+ return {
+ 'name': ex.name,
+ 'message': ex.message,
+ 'url': document.location.href,
+ 'stack': stack
+ };
+ }
+
+ /**
+ * Computes stack trace information from the stacktrace property.
+ * Opera 10 uses this property.
+ * @param {Error} ex
+ * @return {?Object.} Stack trace information.
+ */
+ function computeStackTraceFromStacktraceProp(ex) {
+ // Access and store the stacktrace property before doing ANYTHING
+ // else to it because Opera is not very good at providing it
+ // reliably in other circumstances.
+ var stacktrace = ex.stacktrace;
+
+ var testRE = / line (\d+), column (\d+) in (?:]+)>|([^\)]+))\((.*)\) in (.*):\s*$/i,
+ lines = stacktrace.split('\n'),
+ stack = [],
+ parts;
+
+ for (var i = 0, j = lines.length; i < j; i += 2) {
+ if ((parts = testRE.exec(lines[i]))) {
+ var element = {
+ 'line': +parts[1],
+ 'column': +parts[2],
+ 'func': parts[3] || parts[4],
+ 'args': parts[5] ? parts[5].split(',') : [],
+ 'url': parts[6]
+ };
+
+ if (!element.func && element.line) {
+ element.func = guessFunctionName(element.url, element.line);
+ }
+ if (element.line) {
+ try {
+ element.context = gatherContext(element.url, element.line);
+ } catch (exc) {}
+ }
+
+ if (!element.context) {
+ element.context = [lines[i + 1]];
+ }
+
+ stack.push(element);
+ }
+ }
+
+ if (!stack.length) {
+ return null;
+ }
+
+ return {
+ 'name': ex.name,
+ 'message': ex.message,
+ 'url': document.location.href,
+ 'stack': stack
+ };
+ }
+
+ /**
+ * NOT TESTED.
+ * Computes stack trace information from an error message that includes
+ * the stack trace.
+ * Opera 9 and earlier use this method if the option to show stack
+ * traces is turned on in opera:config.
+ * @param {Error} ex
+ * @return {?Object.} Stack information.
+ */
+ function computeStackTraceFromOperaMultiLineMessage(ex) {
+ // Opera includes a stack trace into the exception message. An example is:
+ //
+ // Statement on line 3: Undefined variable: undefinedFunc
+ // Backtrace:
+ // Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz
+ // undefinedFunc(a);
+ // Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy
+ // zzz(x, y, z);
+ // Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx
+ // yyy(a, a, a);
+ // Line 1 of function script
+ // try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); }
+ // ...
+
+ var lines = ex.message.split('\n');
+ if (lines.length < 4) {
+ return null;
+ }
+
+ var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,
+ lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i,
+ lineRE3 = /^\s*Line (\d+) of function script\s*$/i,
+ stack = [],
+ scripts = document.getElementsByTagName('script'),
+ inlineScriptBlocks = [],
+ parts,
+ i,
+ len,
+ source;
+
+ for (i in scripts) {
+ if (hasKey(scripts, i) && !scripts[i].src) {
+ inlineScriptBlocks.push(scripts[i]);
+ }
+ }
+
+ for (i = 2, len = lines.length; i < len; i += 2) {
+ var item = null;
+ if ((parts = lineRE1.exec(lines[i]))) {
+ item = {
+ 'url': parts[2],
+ 'func': parts[3],
+ 'line': +parts[1]
+ };
+ } else if ((parts = lineRE2.exec(lines[i]))) {
+ item = {
+ 'url': parts[3],
+ 'func': parts[4]
+ };
+ var relativeLine = (+parts[1]); // relative to the start of the
@@ -317,9 +314,6 @@
});
tool_shed_repository_status_updater( repository_status_list );
},
- error: function() {
- alert( "tool_shed_repository_status_updater_callback failed..." );
- }
});
};
diff --git a/templates/admin/tool_shed_repository/initiate_repository_installation.mako b/templates/admin/tool_shed_repository/initiate_repository_installation.mako
index 35743d5c00d..87957f99e92 100644
--- a/templates/admin/tool_shed_repository/initiate_repository_installation.mako
+++ b/templates/admin/tool_shed_repository/initiate_repository_installation.mako
@@ -23,9 +23,6 @@
success : function ( data ) {
//alert( "Initializing repository installation succeeded" );
},
- error: function() {
- alert( "Initializing repository installation failed" );
- },
});
};
diff --git a/templates/galaxy_client_app.mako b/templates/galaxy_client_app.mako
index c259b78be6f..0b5d19bda1a 100644
--- a/templates/galaxy_client_app.mako
+++ b/templates/galaxy_client_app.mako
@@ -83,7 +83,7 @@ ${ h.dumps( get_config_dict() )}
users_api_controller = trans.webapp.api_controllers[ 'users' ]
tags_used = []
for tag in users_api_controller.get_user_tags_used( trans, user=trans.user ):
- tag = tag | h
+ tag = escape( tag )
if tag:
tags_used.append( tag )
user_dict[ 'tags_used' ] = tags_used
@@ -106,6 +106,8 @@ ${ h.dumps( get_config_dict() )}
except Exception, exc:
pass
+ #TODO: no logging available?
+ #log.exception( exc )
return user_dict
%>
diff --git a/templates/tagging_common.mako b/templates/tagging_common.mako
index 35ccfb4550a..b85388b793f 100644
--- a/templates/tagging_common.mako
+++ b/templates/tagging_common.mako
@@ -89,7 +89,7 @@
<%def name="render_tool_tagging_elements()">
<%
elt_id = int ( floor ( random()*maxint ) )
- tags = trans.app.tag_handler.get_tool_tags( trans )
+ tags = trans.app.tag_handler.get_tool_tags()
%>
${self.render_tagging_element_html(elt_id=elt_id, \
tags=tags, \
@@ -105,7 +105,7 @@
## Build HTML.
<%
elt_id = int ( floor ( random()*maxint ) )
- community_tags = trans.app.tag_handler.get_community_tags( trans, item=tagged_item, limit=5 )
+ community_tags = trans.app.tag_handler.get_community_tags( item=tagged_item, limit=5 )
%>
${self.render_tagging_element_html(elt_id=elt_id, \
tags=community_tags, \
diff --git a/templates/user/change_password.mako b/templates/user/change_password.mako
new file mode 100644
index 00000000000..14030c3d8b9
--- /dev/null
+++ b/templates/user/change_password.mako
@@ -0,0 +1,31 @@
+<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+