diff --git a/dist/cjs/tom-select.complete.js b/dist/cjs/tom-select.complete.js index d6e415d8..a0a15416 100644 --- a/dist/cjs/tom-select.complete.js +++ b/dist/cjs/tom-select.complete.js @@ -1,5 +1,5 @@ /** -* Tom Select v1.6.2 +* Tom Select v1.6.3 * Licensed under the Apache License, Version 2.0 (the "License"); */ @@ -472,6 +472,12 @@ class Sifter { weights = search.weights, field_count = fields.length, getAttrFn = search.getAttrFn; + + if (!field_count) { + return function () { + return 1; + }; + } /** * Calculates the score of an object * against the search query. @@ -481,13 +487,8 @@ class Sifter { * @return {number} */ - const scoreObject = function () { - if (!field_count) { - return function () { - return 0; - }; - } + const scoreObject = function () { if (field_count === 1) { return function (token, data) { const field = fields[0].field; @@ -1419,7 +1420,6 @@ class TomSelect extends MicroPlugin(MicroEvent) { this.activeItems = []; this.optgroups = {}; this.options = {}; - this.options_i = 0; this.userOptions = {}; this.items = []; this.renderCache = { @@ -2829,7 +2829,7 @@ class TomSelect extends MicroPlugin(MicroEvent) { var key = hash_key(data[this.settings.valueField]); if (key === null || this.options.hasOwnProperty(key)) return false; data.$order = data.$order || ++this.order; - data.$id = this.inputId + '-opt-' + this.options_i++; + data.$id = this.inputId + '-opt-' + data.$order; this.options[key] = data; return key; } diff --git a/dist/cjs/tom-select.complete.js.map b/dist/cjs/tom-select.complete.js.map index bdcb84ba..936fc1b7 100644 --- a/dist/cjs/tom-select.complete.js.map +++ b/dist/cjs/tom-select.complete.js.map @@ -1 +1 @@ -{"version":3,"file":"tom-select.complete.js","sources":["../../src/contrib/microevent.js","../../src/contrib/microplugin.js","../../node_modules/@orchidjs/sifter/dist/esm/diacritics.js","../../node_modules/@orchidjs/sifter/dist/esm/utils.js","../../node_modules/@orchidjs/sifter/dist/esm/sifter.js","../../src/contrib/highlight.js","../../src/constants.ts","../../src/defaults.ts","../../src/utils.ts","../../src/getSettings.ts","../../src/vanilla.ts","../../src/tom-select.ts","../../src/plugins/change_listener/plugin.ts","../../src/plugins/checkbox_options/plugin.ts","../../src/plugins/clear_button/plugin.ts","../../src/plugins/drag_drop/plugin.ts","../../src/plugins/dropdown_header/plugin.ts","../../src/plugins/dropdown_input/plugin.ts","../../src/plugins/input_autogrow/plugin.ts","../../src/plugins/no_backspace_delete/plugin.ts","../../src/plugins/no_active_items/plugin.ts","../../src/plugins/optgroup_columns/plugin.ts","../../src/plugins/remove_button/plugin.ts","../../src/plugins/restore_on_backspace/plugin.ts"],"sourcesContent":["/**\n * MicroEvent - to make any js object an event emitter\n *\n * - pure javascript - server compatible, browser compatible\n * - dont rely on the browser doms\n * - super simple - you get it immediatly, no mistery, no magic involved\n *\n * @author Jerome Etienne (https://github.com/jeromeetienne)\n */\n\n\n/**\n * Execute callback for each event in space separated list of event names\n *\n */\nfunction forEvents(events,callback){\n\tevents.split(/\\s+/).forEach((event) =>{\n\t\tcallback(event);\n\t});\n}\n\nexport default class MicroEvent{\n\tconstructor(){\n\t\tthis._events = {};\n\t}\n\n\ton(events, fct){\n\t\tforEvents(events,(event) => {\n\t\t\tthis._events[event] = this._events[event] || [];\n\t\t\tthis._events[event].push(fct);\n\t\t});\n\t}\n\n\toff(events, fct){\n\t\tvar n = arguments.length;\n\t\tif( n === 0 ){\n\t\t\tthis._events = {};\n\t\t\treturn;\n\t\t}\n\n\t\tforEvents(events,(event) => {\n\n\t\t\tif (n === 1) return delete this._events[event];\n\n\t\t\tif (event in this._events === false) return;\n\t\t\tthis._events[event].splice(this._events[event].indexOf(fct), 1);\n\t\t});\n\t}\n\n\ttrigger(events, ...args){\n\t\tvar self = this;\n\n\t\tforEvents(events,(event) => {\n\t\t\tif(event in self._events === false) return;\n\t\t\tfor( let fct of self._events[event] ){\n\t\t\t\tfct.apply(self,args );\n\t\t\t}\n\t\t});\n\t}\n};\n","/**\n * microplugin.js\n * Copyright (c) 2013 Brian Reavis & contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this\n * file except in compliance with the License. You may obtain a copy of the License at:\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n * ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n *\n * @author Brian Reavis \n */\n\nexport default function MicroPlugin(Interface){\n\n\tInterface.plugins = {};\n\n\n\treturn class mixin extends Interface{\n\n\t\t/**\n\t\t * Registers a plugin.\n\t\t *\n\t\t * @param {string} name\n\t\t * @param {function} fn\n\t\t */\n\t\tstatic define(name, fn){\n\t\t\tInterface.plugins[name] = {\n\t\t\t\t'name' : name,\n\t\t\t\t'fn' : fn\n\t\t\t};\n\t\t}\n\n\t\t/**\n\t\t * Initializes the listed plugins (with options).\n\t\t * Acceptable formats:\n\t\t *\n\t\t * List (without options):\n\t\t * ['a', 'b', 'c']\n\t\t *\n\t\t * List (with options):\n\t\t * [{'name': 'a', options: {}}, {'name': 'b', options: {}}]\n\t\t *\n\t\t * Hash (with options):\n\t\t * {'a': { ... }, 'b': { ... }, 'c': { ... }}\n\t\t *\n\t\t * @param {array|object} plugins\n\t\t */\n\t\tinitializePlugins(plugins) {\n\t\t\tvar i, n, key;\n\t\t\tvar self = this;\n\t\t\tvar queue = [];\n\n\t\t\tself.plugins = {\n\t\t\t\tnames : [],\n\t\t\t\tsettings : {},\n\t\t\t\trequested : {},\n\t\t\t\tloaded : {}\n\t\t\t};\n\n\t\t\tif (Array.isArray(plugins)) {\n\t\t\t\tfor (i = 0, n = plugins.length; i < n; i++) {\n\t\t\t\t\tif (typeof plugins[i] === 'string') {\n\t\t\t\t\t\tqueue.push(plugins[i]);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tself.plugins.settings[plugins[i].name] = plugins[i].options;\n\t\t\t\t\t\tqueue.push(plugins[i].name);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (plugins) {\n\t\t\t\tfor (key in plugins) {\n\t\t\t\t\tif (plugins.hasOwnProperty(key)) {\n\t\t\t\t\t\tself.plugins.settings[key] = plugins[key];\n\t\t\t\t\t\tqueue.push(key);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\twhile (queue.length) {\n\t\t\t\tself.require(queue.shift());\n\t\t\t}\n\t\t}\n\n\t\tloadPlugin(name) {\n\t\t\tvar self = this;\n\t\t\tvar plugins = self.plugins;\n\t\t\tvar plugin = Interface.plugins[name];\n\n\t\t\tif (!Interface.plugins.hasOwnProperty(name)) {\n\t\t\t\tthrow new Error('Unable to find \"' + name + '\" plugin');\n\t\t\t}\n\n\t\t\tplugins.requested[name] = true;\n\t\t\tplugins.loaded[name] = plugin.fn.apply(self, [self.plugins.settings[name] || {}]);\n\t\t\tplugins.names.push(name);\n\t\t}\n\n\t\t/**\n\t\t * Initializes a plugin.\n\t\t *\n\t\t * @param {string} name\n\t\t */\n\t\trequire(name) {\n\t\t\tvar self = this;\n\t\t\tvar plugins = self.plugins;\n\n\t\t\tif (!self.plugins.loaded.hasOwnProperty(name)) {\n\t\t\t\tif (plugins.requested[name]) {\n\t\t\t\t\tthrow new Error('Plugin has circular dependency (\"' + name + '\")');\n\t\t\t\t}\n\t\t\t\tself.loadPlugin(name);\n\t\t\t}\n\n\t\t\treturn plugins.loaded[name];\n\t\t}\n\n\t};\n\n}\n","/*! sifter.js | https://github.com/orchidjs/sifter.js | Apache License (v2) */\n// https://github.com/andrewrk/node-diacritics/blob/master/index.js\n/**\n * code points generated from toCodePoints();\n * removed 65339 to 65345\n */\n\nvar code_points = [[67, 67], [160, 160], [192, 438], [452, 652], [961, 961], [1019, 1019], [1083, 1083], [1281, 1289], [1984, 1984], [5095, 5095], [7429, 7441], [7545, 7549], [7680, 7935], [8580, 8580], [9398, 9449], [11360, 11391], [42792, 42793], [42802, 42851], [42873, 42897], [42912, 42922], [64256, 64260], [65313, 65338], [65345, 65370]];\n/**\n * Remove accents\n * via https://github.com/krisk/Fuse/issues/133#issuecomment-318692703\n *\n */\n\nfunction asciifold(str) {\n return str.normalize('NFD').replace(/[\\u0300-\\u036F]/g, '').normalize('NFKD').toLowerCase();\n}\n/**\n * Generate a list of diacritics from the list of code points\n *\n */\n\n\nfunction generateDiacritics() {\n var latin_convert = {\n 'l·': 'l',\n 'ʼn': 'n',\n 'æ': 'ae',\n 'ø': 'o',\n 'aʾ': 'a',\n 'dž': 'dz'\n };\n var diacritics = {}; //var no_latin\t= [];\n\n code_points.forEach(code_range => {\n for (let i = code_range[0]; i <= code_range[1]; i++) {\n let diacritic = String.fromCharCode(i);\n let latin = diacritic.normalize('NFD').replace(/[\\u0300-\\u036F]/g, '').normalize('NFKD');\n\n if (latin == diacritic) {\n //no_latin.push(diacritic);\n continue;\n }\n\n latin = latin.toLowerCase();\n\n if (latin in latin_convert) {\n latin = latin_convert[latin];\n }\n\n if (!(latin in diacritics)) {\n diacritics[latin] = latin + latin.toUpperCase();\n }\n\n diacritics[latin] += diacritic;\n }\n }); //console.log('no_latin',JSON.stringify(no_latin));\n\n return diacritics;\n}\n/**\n * Expand a regular expression pattern to include diacritics\n * \teg /a/ becomes /aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐɑAⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ/\n *\n */\n\nvar diacritics = null;\nfunction diacriticRegexPoints(regex) {\n if (diacritics === null) {\n diacritics = generateDiacritics();\n }\n\n for (let latin in diacritics) {\n if (diacritics.hasOwnProperty(latin)) {\n regex = regex.replace(new RegExp(latin, 'g'), '[' + diacritics[latin] + ']');\n }\n }\n\n return regex;\n}\n/**\n * Expand a regular expression pattern to include diacritics\n * \teg /a/ becomes /aⓐaẚàáâầấẫẩãāăằắẵẳȧǡäǟảåǻǎȁȃạậặḁąⱥɐɑAⒶAÀÁÂẦẤẪẨÃĀĂẰẮẴẲȦǠÄǞẢÅǺǍȀȂẠẬẶḀĄȺⱯ/\n *\n * rollup will bundle this function (and the DIACRITICS constant) unless commented out\n *\nvar diacriticRegex = (function() {\n\n\tvar list = [];\n\tfor( let letter in DIACRITICS ){\n\n\t\tif( letter.toLowerCase() != letter && letter.toLowerCase() in DIACRITICS ){\n\t\t\tcontinue;\n\t\t}\n\n\t\tif( DIACRITICS.hasOwnProperty(letter) ){\n\n\t\t\tvar replace = letter + DIACRITICS[letter];\n\t\t\tif( letter.toUpperCase() in DIACRITICS ){\n\t\t\t\treplace += letter.toUpperCase() + DIACRITICS[letter.toUpperCase()];\n\t\t\t}\n\n\t\t\tlist.push({let:letter,pat:'['+replace+']'});\n\t\t}\n\t}\n\n\treturn function(regex:string):string{\n\t\tlist.forEach((item)=>{\n\t\t\tregex = regex.replace( new RegExp(item.let,'g'),item.pat);\n\t\t});\n\t\treturn regex;\n\t}\n})();\n*/\n\nexport { asciifold, diacriticRegexPoints, generateDiacritics };\n//# sourceMappingURL=diacritics.js.map\n","/*! sifter.js | https://github.com/orchidjs/sifter.js | Apache License (v2) */\nimport { asciifold } from './diacritics.js';\n\n// @ts-ignore\n/**\n * A property getter resolving dot-notation\n * @param {Object} obj The root object to fetch property on\n * @param {String} name The optionally dotted property name to fetch\n * @return {Object} The resolved property value\n */\n\nfunction getAttr(obj, name) {\n if (!obj) return;\n return obj[name];\n}\n/**\n * A property getter resolving dot-notation\n * @param {Object} obj The root object to fetch property on\n * @param {String} name The optionally dotted property name to fetch\n * @return {Object} The resolved property value\n */\n\nfunction getAttrNesting(obj, name) {\n if (!obj) return;\n var names = name.split(\".\");\n\n while (names.length && (obj = obj[names.shift()]));\n\n return obj;\n}\n/**\n * Calculates how close of a match the\n * given value is against a search token.\n *\n * @param {object} token\n * @return {number}\n */\n\nfunction scoreValue(value, token, weight) {\n var score, pos;\n if (!value) return 0;\n value = value + '';\n pos = value.search(token.regex);\n if (pos === -1) return 0;\n score = token.string.length / value.length;\n if (pos === 0) score += 0.5;\n return score * weight;\n}\nfunction escape_regex(str) {\n return (str + '').replace(/([.?*+^$[\\]\\\\(){}|-])/g, '\\\\$1');\n}\n/**\n * Cast object property to an array if it exists and has a value\n *\n */\n\nfunction propToArray(obj, key) {\n var value = obj[key];\n\n if (value && !Array.isArray(value)) {\n obj[key] = [value];\n }\n}\n/**\n * Iterates over arrays and hashes.\n *\n * ```\n * iterate(this.items, function(item, id) {\n * // invoked for each item\n * });\n * ```\n *\n * @param {array|object} object\n */\n\nfunction iterate(object, callback) {\n if (Array.isArray(object)) {\n object.forEach(callback);\n } else {\n for (var key in object) {\n if (object.hasOwnProperty(key)) {\n callback(object[key], key);\n }\n }\n }\n}\nfunction cmp(a, b) {\n if (typeof a === 'number' && typeof b === 'number') {\n return a > b ? 1 : a < b ? -1 : 0;\n }\n\n a = asciifold(a + '').toLowerCase();\n b = asciifold(b + '').toLowerCase();\n if (a > b) return 1;\n if (b > a) return -1;\n return 0;\n}\n\nexport { cmp, escape_regex, getAttr, getAttrNesting, iterate, propToArray, scoreValue };\n//# sourceMappingURL=utils.js.map\n","/*! sifter.js | https://github.com/orchidjs/sifter.js | Apache License (v2) */\nimport { escape_regex, iterate, cmp, propToArray, getAttrNesting, getAttr, scoreValue } from './utils.js';\nimport { diacriticRegexPoints, asciifold } from './diacritics.js';\n\n/**\n * sifter.js\n * Copyright (c) 2013–2020 Brian Reavis & contributors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this\n * file except in compliance with the License. You may obtain a copy of the License at:\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF\n * ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n *\n * @author Brian Reavis \n */\nclass Sifter {\n /**\n * Textually searches arrays and hashes of objects\n * by property (or multiple properties). Designed\n * specifically for autocomplete.\n *\n * @constructor\n * @param {array|object} items\n * @param {object} items\n */\n constructor(items, settings) {\n this.items = void 0;\n this.settings = void 0;\n this.items = items;\n this.settings = settings || {\n diacritics: true\n };\n }\n\n /**\n * Splits a search string into an array of individual\n * regexps to be used to match results.\n *\n */\n tokenize(query, respect_word_boundaries, weights) {\n if (!query || !query.length) return [];\n const tokens = [];\n const words = query.split(/\\s+/);\n var field_regex;\n\n if (weights) {\n field_regex = new RegExp('^(' + Object.keys(weights).map(escape_regex).join('|') + ')\\:(.*)$');\n }\n\n words.forEach(word => {\n let field_match;\n let field = null;\n let regex = null; // look for \"field:query\" tokens\n\n if (field_regex && (field_match = word.match(field_regex))) {\n field = field_match[1];\n word = field_match[2];\n }\n\n if (word.length > 0) {\n regex = escape_regex(word);\n\n if (this.settings.diacritics) {\n regex = diacriticRegexPoints(regex);\n }\n\n if (respect_word_boundaries) regex = \"\\\\b\" + regex;\n regex = new RegExp(regex, 'i');\n }\n\n tokens.push({\n string: word,\n regex: regex,\n field: field\n });\n });\n return tokens;\n }\n\n /**\n * Returns a function to be used to score individual results.\n *\n * Good matches will have a higher score than poor matches.\n * If an item is not a match, 0 will be returned by the function.\n *\n * @returns {function}\n */\n getScoreFunction(query, options) {\n var search = this.prepareSearch(query, options);\n return this._getScoreFunction(search);\n }\n\n _getScoreFunction(search) {\n const tokens = search.tokens,\n token_count = tokens.length;\n\n if (!token_count) {\n return function () {\n return 0;\n };\n }\n\n const fields = search.options.fields,\n weights = search.weights,\n field_count = fields.length,\n getAttrFn = search.getAttrFn;\n /**\n * Calculates the score of an object\n * against the search query.\n *\n * @param {TToken} token\n * @param {object} data\n * @return {number}\n */\n\n const scoreObject = function () {\n if (!field_count) {\n return function () {\n return 0;\n };\n }\n\n if (field_count === 1) {\n return function (token, data) {\n const field = fields[0].field;\n return scoreValue(getAttrFn(data, field), token, weights[field]);\n };\n }\n\n return function (token, data) {\n var sum = 0; // is the token specific to a field?\n\n if (token.field) {\n const value = getAttrFn(data, token.field);\n\n if (!token.regex && value) {\n sum += 1 / field_count;\n } else {\n sum += scoreValue(value, token, 1);\n }\n } else {\n iterate(weights, (weight, field) => {\n sum += scoreValue(getAttrFn(data, field), token, weight);\n });\n }\n\n return sum / field_count;\n };\n }();\n\n if (token_count === 1) {\n return function (data) {\n return scoreObject(tokens[0], data);\n };\n }\n\n if (search.options.conjunction === 'and') {\n return function (data) {\n var i = 0,\n score,\n sum = 0;\n\n for (; i < token_count; i++) {\n score = scoreObject(tokens[i], data);\n if (score <= 0) return 0;\n sum += score;\n }\n\n return sum / token_count;\n };\n } else {\n return function (data) {\n var sum = 0;\n iterate(tokens, token => {\n sum += scoreObject(token, data);\n });\n return sum / token_count;\n };\n }\n }\n\n /**\n * Returns a function that can be used to compare two\n * results, for sorting purposes. If no sorting should\n * be performed, `null` will be returned.\n *\n * @return function(a,b)\n */\n getSortFunction(query, options) {\n var search = this.prepareSearch(query, options);\n return this._getSortFunction(search);\n }\n\n _getSortFunction(search) {\n var i, n, sort_fld, sort_flds_count, multiplier, implicit_score;\n const self = this,\n options = search.options,\n sort = !search.query && options.sort_empty || options.sort,\n sort_flds = [],\n multipliers = [];\n /**\n * Fetches the specified sort field value\n * from a search result item.\n *\n * @param {string} name\n * @param {object} result\n * @return {string}\n */\n\n const get_field = function get_field(name, result) {\n if (name === '$score') return result.score;\n return search.getAttrFn(self.items[result.id], name);\n }; // parse options\n\n\n if (sort) {\n for (i = 0, n = sort.length; i < n; i++) {\n if (search.query || sort[i].field !== '$score') {\n sort_flds.push(sort[i]);\n }\n }\n } // the \"$score\" field is implied to be the primary\n // sort field, unless it's manually specified\n\n\n if (search.query) {\n implicit_score = true;\n\n for (i = 0, n = sort_flds.length; i < n; i++) {\n if (sort_flds[i].field === '$score') {\n implicit_score = false;\n break;\n }\n }\n\n if (implicit_score) {\n sort_flds.unshift({\n field: '$score',\n direction: 'desc'\n });\n }\n } else {\n for (i = 0, n = sort_flds.length; i < n; i++) {\n if (sort_flds[i].field === '$score') {\n sort_flds.splice(i, 1);\n break;\n }\n }\n }\n\n for (i = 0, n = sort_flds.length; i < n; i++) {\n multipliers.push(sort_flds[i].direction === 'desc' ? -1 : 1);\n } // build function\n\n\n sort_flds_count = sort_flds.length;\n\n if (!sort_flds_count) {\n return null;\n } else if (sort_flds_count === 1) {\n sort_fld = sort_flds[0].field;\n multiplier = multipliers[0];\n return function (a, b) {\n return multiplier * cmp(get_field(sort_fld, a), get_field(sort_fld, b));\n };\n } else {\n return function (a, b) {\n var i, result, field;\n\n for (i = 0; i < sort_flds_count; i++) {\n field = sort_flds[i].field;\n result = multipliers[i] * cmp(get_field(field, a), get_field(field, b));\n if (result) return result;\n }\n\n return 0;\n };\n }\n }\n\n /**\n * Parses a search query and returns an object\n * with tokens and fields ready to be populated\n * with results.\n *\n */\n prepareSearch(query, optsUser) {\n const weights = {};\n var options = Object.assign({}, optsUser);\n propToArray(options, 'sort');\n propToArray(options, 'sort_empty'); // convert fields to new format\n\n if (options.fields) {\n propToArray(options, 'fields');\n\n if (Array.isArray(options.fields) && typeof options.fields[0] !== 'object') {\n var fields = [];\n options.fields.forEach(fld_name => {\n fields.push({\n field: fld_name\n });\n });\n options.fields = fields;\n }\n\n options.fields.forEach(field_params => {\n weights[field_params.field] = 'weight' in field_params ? field_params.weight : 1;\n });\n }\n\n query = asciifold(query + '').toLowerCase().trim();\n return {\n options: options,\n query: query,\n tokens: this.tokenize(query, options.respect_word_boundaries, weights),\n total: 0,\n items: [],\n weights: weights,\n getAttrFn: options.nesting ? getAttrNesting : getAttr\n };\n }\n\n /**\n * Searches through all items and returns a sorted array of matches.\n *\n */\n search(query, options) {\n var self = this,\n score,\n search;\n var fn_sort;\n var fn_score;\n search = this.prepareSearch(query, options);\n options = search.options;\n query = search.query; // generate result scoring function\n\n fn_score = options.score || self._getScoreFunction(search); // perform search and sort\n\n if (query.length) {\n iterate(self.items, (item, id) => {\n score = fn_score(item);\n\n if (options.filter === false || score > 0) {\n search.items.push({\n 'score': score,\n 'id': id\n });\n }\n });\n } else {\n iterate(self.items, (item, id) => {\n search.items.push({\n 'score': 1,\n 'id': id\n });\n });\n }\n\n fn_sort = self._getSortFunction(search);\n if (fn_sort) search.items.sort(fn_sort); // apply limits\n\n search.total = search.items.length;\n\n if (typeof options.limit === 'number') {\n search.items = search.items.slice(0, options.limit);\n }\n\n return search;\n }\n\n}\n\nexport default Sifter;\n//# sourceMappingURL=sifter.js.map\n","/**\n * highlight v3 | MIT license | Johann Burkard \n * Highlights arbitrary terms in a node.\n *\n * - Modified by Marshal 2011-6-24 (added regex)\n * - Modified by Brian Reavis 2012-8-27 (cleanup)\n */\n\nexport function highlight(element, regex) {\n\n\tif( regex === null ) return;\n\n\t// convet string to regex\n\tif( typeof regex === 'string' ){\n\n\t\tif( !regex.length ) return;\n\t\tregex = new RegExp(regex, 'i');\n\t}\n\n\n\tvar highlight = function(node) {\n\t\tvar skip = 0;\n\t\t// Wrap matching part of text node with highlighting , e.g.\n\t\t// Soccer -> Soccer for regex = /soc/i\n\t\tif (node.nodeType === 3) {\n\t\t\tvar pos = node.data.search(regex);\n\t\t\tif (pos >= 0 && node.data.length > 0) {\n\t\t\t\tvar match = node.data.match(regex);\n\t\t\t\tvar spannode = document.createElement('span');\n\t\t\t\tspannode.className = 'highlight';\n\t\t\t\tvar middlebit = node.splitText(pos);\n\t\t\t\tmiddlebit.splitText(match[0].length);\n\t\t\t\tvar middleclone = middlebit.cloneNode(true);\n\t\t\t\tspannode.appendChild(middleclone);\n\t\t\t\tmiddlebit.parentNode.replaceChild(spannode, middlebit);\n\t\t\t\tskip = 1;\n\n\t\t\t}\n\t\t}\n\t\t// Recurse element node, looking for child text nodes to highlight, unless element\n\t\t// is childless,