github.com/shyftnetwork/go-empyrean@v1.8.3-0.20191127201940-fbfca9338f04/shyft_documentation/source/javascripts/lib/_lunr.js (about)

     1  /**
     2   * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.5.7
     3   * Copyright (C) 2014 Oliver Nightingale
     4   * MIT Licensed
     5   * @license
     6   */
     7  
     8  (function(){
     9  
    10    /**
    11     * Convenience function for instantiating a new lunr index and configuring it
    12     * with the default pipeline functions and the passed config function.
    13     *
    14     * When using this convenience function a new index will be created with the
    15     * following functions already in the pipeline:
    16     *
    17     * lunr.StopWordFilter - filters out any stop words before they enter the
    18     * index
    19     *
    20     * lunr.stemmer - stems the tokens before entering the index.
    21     *
    22     * Example:
    23     *
    24     *     var idx = lunr(function () {
    25   *       this.field('title', 10)
    26   *       this.field('tags', 100)
    27   *       this.field('body')
    28   *
    29   *       this.ref('cid')
    30   *
    31   *       this.pipeline.add(function () {
    32   *         // some custom pipeline function
    33   *       })
    34   *
    35   *     })
    36     *
    37     * @param {Function} config A function that will be called with the new instance
    38     * of the lunr.Index as both its context and first parameter. It can be used to
    39     * customize the instance of new lunr.Index.
    40     * @namespace
    41     * @module
    42     * @returns {lunr.Index}
    43     *
    44     */
    45    var lunr = function (config) {
    46      var idx = new lunr.Index
    47  
    48      idx.pipeline.add(
    49          lunr.trimmer,
    50          lunr.stopWordFilter,
    51          lunr.stemmer
    52      )
    53  
    54      if (config) config.call(idx, idx)
    55  
    56      return idx
    57    }
    58  
    59    lunr.version = "0.5.7"
    60    /*!
    61     * lunr.utils
    62     * Copyright (C) 2014 Oliver Nightingale
    63     */
    64  
    65    /**
    66     * A namespace containing utils for the rest of the lunr library
    67     */
    68    lunr.utils = {}
    69  
    70    /**
    71     * Print a warning message to the console.
    72     *
    73     * @param {String} message The message to be printed.
    74     * @memberOf Utils
    75     */
    76    lunr.utils.warn = (function (global) {
    77      return function (message) {
    78        if (global.console && console.warn) {
    79          console.warn(message)
    80        }
    81      }
    82    })(this)
    83  
    84    /*!
    85     * lunr.EventEmitter
    86     * Copyright (C) 2014 Oliver Nightingale
    87     */
    88  
    89    /**
    90     * lunr.EventEmitter is an event emitter for lunr. It manages adding and removing event handlers and triggering events and their handlers.
    91     *
    92     * @constructor
    93     */
    94    lunr.EventEmitter = function () {
    95      this.events = {}
    96    }
    97  
    98    /**
    99     * Binds a handler function to a specific event(s).
   100     *
   101     * Can bind a single function to many different events in one call.
   102     *
   103     * @param {String} [eventName] The name(s) of events to bind this function to.
   104     * @param {Function} handler The function to call when an event is fired.
   105     * @memberOf EventEmitter
   106     */
   107    lunr.EventEmitter.prototype.addListener = function () {
   108      var args = Array.prototype.slice.call(arguments),
   109          fn = args.pop(),
   110          names = args
   111  
   112      if (typeof fn !== "function") throw new TypeError ("last argument must be a function")
   113  
   114      names.forEach(function (name) {
   115        if (!this.hasHandler(name)) this.events[name] = []
   116        this.events[name].push(fn)
   117      }, this)
   118    }
   119  
   120    /**
   121     * Removes a handler function from a specific event.
   122     *
   123     * @param {String} eventName The name of the event to remove this function from.
   124     * @param {Function} handler The function to remove from an event.
   125     * @memberOf EventEmitter
   126     */
   127    lunr.EventEmitter.prototype.removeListener = function (name, fn) {
   128      if (!this.hasHandler(name)) return
   129  
   130      var fnIndex = this.events[name].indexOf(fn)
   131      this.events[name].splice(fnIndex, 1)
   132  
   133      if (!this.events[name].length) delete this.events[name]
   134    }
   135  
   136    /**
   137     * Calls all functions bound to the given event.
   138     *
   139     * Additional data can be passed to the event handler as arguments to `emit`
   140     * after the event name.
   141     *
   142     * @param {String} eventName The name of the event to emit.
   143     * @memberOf EventEmitter
   144     */
   145    lunr.EventEmitter.prototype.emit = function (name) {
   146      if (!this.hasHandler(name)) return
   147  
   148      var args = Array.prototype.slice.call(arguments, 1)
   149  
   150      this.events[name].forEach(function (fn) {
   151        fn.apply(undefined, args)
   152      })
   153    }
   154  
   155    /**
   156     * Checks whether a handler has ever been stored against an event.
   157     *
   158     * @param {String} eventName The name of the event to check.
   159     * @private
   160     * @memberOf EventEmitter
   161     */
   162    lunr.EventEmitter.prototype.hasHandler = function (name) {
   163      return name in this.events
   164    }
   165  
   166    /*!
   167     * lunr.tokenizer
   168     * Copyright (C) 2014 Oliver Nightingale
   169     */
   170  
   171    /**
   172     * A function for splitting a string into tokens ready to be inserted into
   173     * the search index.
   174     *
   175     * @module
   176     * @param {String} obj The string to convert into tokens
   177     * @returns {Array}
   178     */
   179    lunr.tokenizer = function (obj) {
   180      if (!arguments.length || obj == null || obj == undefined) return []
   181      if (Array.isArray(obj)) return obj.map(function (t) { return t.toLowerCase() })
   182  
   183      var str = obj.toString().replace(/^\s+/, '')
   184  
   185      for (var i = str.length - 1; i >= 0; i--) {
   186        if (/\S/.test(str.charAt(i))) {
   187          str = str.substring(0, i + 1)
   188          break
   189        }
   190      }
   191  
   192      return str
   193          .split(/(?:\s+|\-)/)
   194          .filter(function (token) {
   195            return !!token
   196          })
   197          .map(function (token) {
   198            return token.toLowerCase()
   199          })
   200    }
   201    /*!
   202     * lunr.Pipeline
   203     * Copyright (C) 2014 Oliver Nightingale
   204     */
   205  
   206    /**
   207     * lunr.Pipelines maintain an ordered list of functions to be applied to all
   208     * tokens in documents entering the search index and queries being ran against
   209     * the index.
   210     *
   211     * An instance of lunr.Index created with the lunr shortcut will contain a
   212     * pipeline with a stop word filter and an English language stemmer. Extra
   213     * functions can be added before or after either of these functions or these
   214     * default functions can be removed.
   215     *
   216     * When run the pipeline will call each function in turn, passing a token, the
   217     * index of that token in the original list of all tokens and finally a list of
   218     * all the original tokens.
   219     *
   220     * The output of functions in the pipeline will be passed to the next function
   221     * in the pipeline. To exclude a token from entering the index the function
   222     * should return undefined, the rest of the pipeline will not be called with
   223     * this token.
   224     *
   225     * For serialisation of pipelines to work, all functions used in an instance of
   226     * a pipeline should be registered with lunr.Pipeline. Registered functions can
   227     * then be loaded. If trying to load a serialised pipeline that uses functions
   228     * that are not registered an error will be thrown.
   229     *
   230     * If not planning on serialising the pipeline then registering pipeline functions
   231     * is not necessary.
   232     *
   233     * @constructor
   234     */
   235    lunr.Pipeline = function () {
   236      this._stack = []
   237    }
   238  
   239    lunr.Pipeline.registeredFunctions = {}
   240  
   241    /**
   242     * Register a function with the pipeline.
   243     *
   244     * Functions that are used in the pipeline should be registered if the pipeline
   245     * needs to be serialised, or a serialised pipeline needs to be loaded.
   246     *
   247     * Registering a function does not add it to a pipeline, functions must still be
   248     * added to instances of the pipeline for them to be used when running a pipeline.
   249     *
   250     * @param {Function} fn The function to check for.
   251     * @param {String} label The label to register this function with
   252     * @memberOf Pipeline
   253     */
   254    lunr.Pipeline.registerFunction = function (fn, label) {
   255      if (label in this.registeredFunctions) {
   256        lunr.utils.warn('Overwriting existing registered function: ' + label)
   257      }
   258  
   259      fn.label = label
   260      lunr.Pipeline.registeredFunctions[fn.label] = fn
   261    }
   262  
   263    /**
   264     * Warns if the function is not registered as a Pipeline function.
   265     *
   266     * @param {Function} fn The function to check for.
   267     * @private
   268     * @memberOf Pipeline
   269     */
   270    lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {
   271      var isRegistered = fn.label && (fn.label in this.registeredFunctions)
   272  
   273      if (!isRegistered) {
   274        lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn)
   275      }
   276    }
   277  
   278    /**
   279     * Loads a previously serialised pipeline.
   280     *
   281     * All functions to be loaded must already be registered with lunr.Pipeline.
   282     * If any function from the serialised data has not been registered then an
   283     * error will be thrown.
   284     *
   285     * @param {Object} serialised The serialised pipeline to load.
   286     * @returns {lunr.Pipeline}
   287     * @memberOf Pipeline
   288     */
   289    lunr.Pipeline.load = function (serialised) {
   290      var pipeline = new lunr.Pipeline
   291  
   292      serialised.forEach(function (fnName) {
   293        var fn = lunr.Pipeline.registeredFunctions[fnName]
   294  
   295        if (fn) {
   296          pipeline.add(fn)
   297        } else {
   298          throw new Error ('Cannot load un-registered function: ' + fnName)
   299        }
   300      })
   301  
   302      return pipeline
   303    }
   304  
   305    /**
   306     * Adds new functions to the end of the pipeline.
   307     *
   308     * Logs a warning if the function has not been registered.
   309     *
   310     * @param {Function} functions Any number of functions to add to the pipeline.
   311     * @memberOf Pipeline
   312     */
   313    lunr.Pipeline.prototype.add = function () {
   314      var fns = Array.prototype.slice.call(arguments)
   315  
   316      fns.forEach(function (fn) {
   317        lunr.Pipeline.warnIfFunctionNotRegistered(fn)
   318        this._stack.push(fn)
   319      }, this)
   320    }
   321  
   322    /**
   323     * Adds a single function after a function that already exists in the
   324     * pipeline.
   325     *
   326     * Logs a warning if the function has not been registered.
   327     *
   328     * @param {Function} existingFn A function that already exists in the pipeline.
   329     * @param {Function} newFn The new function to add to the pipeline.
   330     * @memberOf Pipeline
   331     */
   332    lunr.Pipeline.prototype.after = function (existingFn, newFn) {
   333      lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
   334  
   335      var pos = this._stack.indexOf(existingFn) + 1
   336      this._stack.splice(pos, 0, newFn)
   337    }
   338  
   339    /**
   340     * Adds a single function before a function that already exists in the
   341     * pipeline.
   342     *
   343     * Logs a warning if the function has not been registered.
   344     *
   345     * @param {Function} existingFn A function that already exists in the pipeline.
   346     * @param {Function} newFn The new function to add to the pipeline.
   347     * @memberOf Pipeline
   348     */
   349    lunr.Pipeline.prototype.before = function (existingFn, newFn) {
   350      lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
   351  
   352      var pos = this._stack.indexOf(existingFn)
   353      this._stack.splice(pos, 0, newFn)
   354    }
   355  
   356    /**
   357     * Removes a function from the pipeline.
   358     *
   359     * @param {Function} fn The function to remove from the pipeline.
   360     * @memberOf Pipeline
   361     */
   362    lunr.Pipeline.prototype.remove = function (fn) {
   363      var pos = this._stack.indexOf(fn)
   364      this._stack.splice(pos, 1)
   365    }
   366  
   367    /**
   368     * Runs the current list of functions that make up the pipeline against the
   369     * passed tokens.
   370     *
   371     * @param {Array} tokens The tokens to run through the pipeline.
   372     * @returns {Array}
   373     * @memberOf Pipeline
   374     */
   375    lunr.Pipeline.prototype.run = function (tokens) {
   376      var out = [],
   377          tokenLength = tokens.length,
   378          stackLength = this._stack.length
   379  
   380      for (var i = 0; i < tokenLength; i++) {
   381        var token = tokens[i]
   382  
   383        for (var j = 0; j < stackLength; j++) {
   384          token = this._stack[j](token, i, tokens)
   385          if (token === void 0) break
   386        };
   387  
   388        if (token !== void 0) out.push(token)
   389      };
   390  
   391      return out
   392    }
   393  
   394    /**
   395     * Resets the pipeline by removing any existing processors.
   396     *
   397     * @memberOf Pipeline
   398     */
   399    lunr.Pipeline.prototype.reset = function () {
   400      this._stack = []
   401    }
   402  
   403    /**
   404     * Returns a representation of the pipeline ready for serialisation.
   405     *
   406     * Logs a warning if the function has not been registered.
   407     *
   408     * @returns {Array}
   409     * @memberOf Pipeline
   410     */
   411    lunr.Pipeline.prototype.toJSON = function () {
   412      return this._stack.map(function (fn) {
   413        lunr.Pipeline.warnIfFunctionNotRegistered(fn)
   414  
   415        return fn.label
   416      })
   417    }
   418    /*!
   419     * lunr.Vector
   420     * Copyright (C) 2014 Oliver Nightingale
   421     */
   422  
   423    /**
   424     * lunr.Vectors implement vector related operations for
   425     * a series of elements.
   426     *
   427     * @constructor
   428     */
   429    lunr.Vector = function () {
   430      this._magnitude = null
   431      this.list = undefined
   432      this.length = 0
   433    }
   434  
   435    /**
   436     * lunr.Vector.Node is a simple struct for each node
   437     * in a lunr.Vector.
   438     *
   439     * @private
   440     * @param {Number} The index of the node in the vector.
   441     * @param {Object} The data at this node in the vector.
   442     * @param {lunr.Vector.Node} The node directly after this node in the vector.
   443     * @constructor
   444     * @memberOf Vector
   445     */
   446    lunr.Vector.Node = function (idx, val, next) {
   447      this.idx = idx
   448      this.val = val
   449      this.next = next
   450    }
   451  
   452    /**
   453     * Inserts a new value at a position in a vector.
   454     *
   455     * @param {Number} The index at which to insert a value.
   456     * @param {Object} The object to insert in the vector.
   457     * @memberOf Vector.
   458     */
   459    lunr.Vector.prototype.insert = function (idx, val) {
   460      var list = this.list
   461  
   462      if (!list) {
   463        this.list = new lunr.Vector.Node (idx, val, list)
   464        return this.length++
   465      }
   466  
   467      var prev = list,
   468          next = list.next
   469  
   470      while (next != undefined) {
   471        if (idx < next.idx) {
   472          prev.next = new lunr.Vector.Node (idx, val, next)
   473          return this.length++
   474        }
   475  
   476        prev = next, next = next.next
   477      }
   478  
   479      prev.next = new lunr.Vector.Node (idx, val, next)
   480      return this.length++
   481    }
   482  
   483    /**
   484     * Calculates the magnitude of this vector.
   485     *
   486     * @returns {Number}
   487     * @memberOf Vector
   488     */
   489    lunr.Vector.prototype.magnitude = function () {
   490      if (this._magniture) return this._magnitude
   491      var node = this.list,
   492          sumOfSquares = 0,
   493          val
   494  
   495      while (node) {
   496        val = node.val
   497        sumOfSquares += val * val
   498        node = node.next
   499      }
   500  
   501      return this._magnitude = Math.sqrt(sumOfSquares)
   502    }
   503  
   504    /**
   505     * Calculates the dot product of this vector and another vector.
   506     *
   507     * @param {lunr.Vector} otherVector The vector to compute the dot product with.
   508     * @returns {Number}
   509     * @memberOf Vector
   510     */
   511    lunr.Vector.prototype.dot = function (otherVector) {
   512      var node = this.list,
   513          otherNode = otherVector.list,
   514          dotProduct = 0
   515  
   516      while (node && otherNode) {
   517        if (node.idx < otherNode.idx) {
   518          node = node.next
   519        } else if (node.idx > otherNode.idx) {
   520          otherNode = otherNode.next
   521        } else {
   522          dotProduct += node.val * otherNode.val
   523          node = node.next
   524          otherNode = otherNode.next
   525        }
   526      }
   527  
   528      return dotProduct
   529    }
   530  
   531    /**
   532     * Calculates the cosine similarity between this vector and another
   533     * vector.
   534     *
   535     * @param {lunr.Vector} otherVector The other vector to calculate the
   536     * similarity with.
   537     * @returns {Number}
   538     * @memberOf Vector
   539     */
   540    lunr.Vector.prototype.similarity = function (otherVector) {
   541      return this.dot(otherVector) / (this.magnitude() * otherVector.magnitude())
   542    }
   543    /*!
   544     * lunr.SortedSet
   545     * Copyright (C) 2014 Oliver Nightingale
   546     */
   547  
   548    /**
   549     * lunr.SortedSets are used to maintain an array of uniq values in a sorted
   550     * order.
   551     *
   552     * @constructor
   553     */
   554    lunr.SortedSet = function () {
   555      this.length = 0
   556      this.elements = []
   557    }
   558  
   559    /**
   560     * Loads a previously serialised sorted set.
   561     *
   562     * @param {Array} serialisedData The serialised set to load.
   563     * @returns {lunr.SortedSet}
   564     * @memberOf SortedSet
   565     */
   566    lunr.SortedSet.load = function (serialisedData) {
   567      var set = new this
   568  
   569      set.elements = serialisedData
   570      set.length = serialisedData.length
   571  
   572      return set
   573    }
   574  
   575    /**
   576     * Inserts new items into the set in the correct position to maintain the
   577     * order.
   578     *
   579     * @param {Object} The objects to add to this set.
   580     * @memberOf SortedSet
   581     */
   582    lunr.SortedSet.prototype.add = function () {
   583      Array.prototype.slice.call(arguments).forEach(function (element) {
   584        if (~this.indexOf(element)) return
   585        this.elements.splice(this.locationFor(element), 0, element)
   586      }, this)
   587  
   588      this.length = this.elements.length
   589    }
   590  
   591    /**
   592     * Converts this sorted set into an array.
   593     *
   594     * @returns {Array}
   595     * @memberOf SortedSet
   596     */
   597    lunr.SortedSet.prototype.toArray = function () {
   598      return this.elements.slice()
   599    }
   600  
   601    /**
   602     * Creates a new array with the results of calling a provided function on every
   603     * element in this sorted set.
   604     *
   605     * Delegates to Array.prototype.map and has the same signature.
   606     *
   607     * @param {Function} fn The function that is called on each element of the
   608     * set.
   609     * @param {Object} ctx An optional object that can be used as the context
   610     * for the function fn.
   611     * @returns {Array}
   612     * @memberOf SortedSet
   613     */
   614    lunr.SortedSet.prototype.map = function (fn, ctx) {
   615      return this.elements.map(fn, ctx)
   616    }
   617  
   618    /**
   619     * Executes a provided function once per sorted set element.
   620     *
   621     * Delegates to Array.prototype.forEach and has the same signature.
   622     *
   623     * @param {Function} fn The function that is called on each element of the
   624     * set.
   625     * @param {Object} ctx An optional object that can be used as the context
   626     * @memberOf SortedSet
   627     * for the function fn.
   628     */
   629    lunr.SortedSet.prototype.forEach = function (fn, ctx) {
   630      return this.elements.forEach(fn, ctx)
   631    }
   632  
   633    /**
   634     * Returns the index at which a given element can be found in the
   635     * sorted set, or -1 if it is not present.
   636     *
   637     * @param {Object} elem The object to locate in the sorted set.
   638     * @param {Number} start An optional index at which to start searching from
   639     * within the set.
   640     * @param {Number} end An optional index at which to stop search from within
   641     * the set.
   642     * @returns {Number}
   643     * @memberOf SortedSet
   644     */
   645    lunr.SortedSet.prototype.indexOf = function (elem, start, end) {
   646      var start = start || 0,
   647          end = end || this.elements.length,
   648          sectionLength = end - start,
   649          pivot = start + Math.floor(sectionLength / 2),
   650          pivotElem = this.elements[pivot]
   651  
   652      if (sectionLength <= 1) {
   653        if (pivotElem === elem) {
   654          return pivot
   655        } else {
   656          return -1
   657        }
   658      }
   659  
   660      if (pivotElem < elem) return this.indexOf(elem, pivot, end)
   661      if (pivotElem > elem) return this.indexOf(elem, start, pivot)
   662      if (pivotElem === elem) return pivot
   663    }
   664  
   665    /**
   666     * Returns the position within the sorted set that an element should be
   667     * inserted at to maintain the current order of the set.
   668     *
   669     * This function assumes that the element to search for does not already exist
   670     * in the sorted set.
   671     *
   672     * @param {Object} elem The elem to find the position for in the set
   673     * @param {Number} start An optional index at which to start searching from
   674     * within the set.
   675     * @param {Number} end An optional index at which to stop search from within
   676     * the set.
   677     * @returns {Number}
   678     * @memberOf SortedSet
   679     */
   680    lunr.SortedSet.prototype.locationFor = function (elem, start, end) {
   681      var start = start || 0,
   682          end = end || this.elements.length,
   683          sectionLength = end - start,
   684          pivot = start + Math.floor(sectionLength / 2),
   685          pivotElem = this.elements[pivot]
   686  
   687      if (sectionLength <= 1) {
   688        if (pivotElem > elem) return pivot
   689        if (pivotElem < elem) return pivot + 1
   690      }
   691  
   692      if (pivotElem < elem) return this.locationFor(elem, pivot, end)
   693      if (pivotElem > elem) return this.locationFor(elem, start, pivot)
   694    }
   695  
   696    /**
   697     * Creates a new lunr.SortedSet that contains the elements in the intersection
   698     * of this set and the passed set.
   699     *
   700     * @param {lunr.SortedSet} otherSet The set to intersect with this set.
   701     * @returns {lunr.SortedSet}
   702     * @memberOf SortedSet
   703     */
   704    lunr.SortedSet.prototype.intersect = function (otherSet) {
   705      var intersectSet = new lunr.SortedSet,
   706          i = 0, j = 0,
   707          a_len = this.length, b_len = otherSet.length,
   708          a = this.elements, b = otherSet.elements
   709  
   710      while (true) {
   711        if (i > a_len - 1 || j > b_len - 1) break
   712  
   713        if (a[i] === b[j]) {
   714          intersectSet.add(a[i])
   715          i++, j++
   716          continue
   717        }
   718  
   719        if (a[i] < b[j]) {
   720          i++
   721          continue
   722        }
   723  
   724        if (a[i] > b[j]) {
   725          j++
   726          continue
   727        }
   728      };
   729  
   730      return intersectSet
   731    }
   732  
   733    /**
   734     * Makes a copy of this set
   735     *
   736     * @returns {lunr.SortedSet}
   737     * @memberOf SortedSet
   738     */
   739    lunr.SortedSet.prototype.clone = function () {
   740      var clone = new lunr.SortedSet
   741  
   742      clone.elements = this.toArray()
   743      clone.length = clone.elements.length
   744  
   745      return clone
   746    }
   747  
   748    /**
   749     * Creates a new lunr.SortedSet that contains the elements in the union
   750     * of this set and the passed set.
   751     *
   752     * @param {lunr.SortedSet} otherSet The set to union with this set.
   753     * @returns {lunr.SortedSet}
   754     * @memberOf SortedSet
   755     */
   756    lunr.SortedSet.prototype.union = function (otherSet) {
   757      var longSet, shortSet, unionSet
   758  
   759      if (this.length >= otherSet.length) {
   760        longSet = this, shortSet = otherSet
   761      } else {
   762        longSet = otherSet, shortSet = this
   763      }
   764  
   765      unionSet = longSet.clone()
   766  
   767      unionSet.add.apply(unionSet, shortSet.toArray())
   768  
   769      return unionSet
   770    }
   771  
   772    /**
   773     * Returns a representation of the sorted set ready for serialisation.
   774     *
   775     * @returns {Array}
   776     * @memberOf SortedSet
   777     */
   778    lunr.SortedSet.prototype.toJSON = function () {
   779      return this.toArray()
   780    }
   781    /*!
   782     * lunr.Index
   783     * Copyright (C) 2014 Oliver Nightingale
   784     */
   785  
   786    /**
   787     * lunr.Index is object that manages a search index.  It contains the indexes
   788     * and stores all the tokens and document lookups.  It also provides the main
   789     * user facing API for the library.
   790     *
   791     * @constructor
   792     */
   793    lunr.Index = function () {
   794      this._fields = []
   795      this._ref = 'id'
   796      this.pipeline = new lunr.Pipeline
   797      this.documentStore = new lunr.Store
   798      this.tokenStore = new lunr.TokenStore
   799      this.corpusTokens = new lunr.SortedSet
   800      this.eventEmitter =  new lunr.EventEmitter
   801  
   802      this._idfCache = {}
   803  
   804      this.on('add', 'remove', 'update', (function () {
   805        this._idfCache = {}
   806      }).bind(this))
   807    }
   808  
   809    /**
   810     * Bind a handler to events being emitted by the index.
   811     *
   812     * The handler can be bound to many events at the same time.
   813     *
   814     * @param {String} [eventName] The name(s) of events to bind the function to.
   815     * @param {Function} handler The serialised set to load.
   816     * @memberOf Index
   817     */
   818    lunr.Index.prototype.on = function () {
   819      var args = Array.prototype.slice.call(arguments)
   820      return this.eventEmitter.addListener.apply(this.eventEmitter, args)
   821    }
   822  
   823    /**
   824     * Removes a handler from an event being emitted by the index.
   825     *
   826     * @param {String} eventName The name of events to remove the function from.
   827     * @param {Function} handler The serialised set to load.
   828     * @memberOf Index
   829     */
   830    lunr.Index.prototype.off = function (name, fn) {
   831      return this.eventEmitter.removeListener(name, fn)
   832    }
   833  
   834    /**
   835     * Loads a previously serialised index.
   836     *
   837     * Issues a warning if the index being imported was serialised
   838     * by a different version of lunr.
   839     *
   840     * @param {Object} serialisedData The serialised set to load.
   841     * @returns {lunr.Index}
   842     * @memberOf Index
   843     */
   844    lunr.Index.load = function (serialisedData) {
   845      if (serialisedData.version !== lunr.version) {
   846        lunr.utils.warn('version mismatch: current ' + lunr.version + ' importing ' + serialisedData.version)
   847      }
   848  
   849      var idx = new this
   850  
   851      idx._fields = serialisedData.fields
   852      idx._ref = serialisedData.ref
   853  
   854      idx.documentStore = lunr.Store.load(serialisedData.documentStore)
   855      idx.tokenStore = lunr.TokenStore.load(serialisedData.tokenStore)
   856      idx.corpusTokens = lunr.SortedSet.load(serialisedData.corpusTokens)
   857      idx.pipeline = lunr.Pipeline.load(serialisedData.pipeline)
   858  
   859      return idx
   860    }
   861  
   862    /**
   863     * Adds a field to the list of fields that will be searchable within documents
   864     * in the index.
   865     *
   866     * An optional boost param can be passed to affect how much tokens in this field
   867     * rank in search results, by default the boost value is 1.
   868     *
   869     * Fields should be added before any documents are added to the index, fields
   870     * that are added after documents are added to the index will only apply to new
   871     * documents added to the index.
   872     *
   873     * @param {String} fieldName The name of the field within the document that
   874     * should be indexed
   875     * @param {Number} boost An optional boost that can be applied to terms in this
   876     * field.
   877     * @returns {lunr.Index}
   878     * @memberOf Index
   879     */
   880    lunr.Index.prototype.field = function (fieldName, opts) {
   881      var opts = opts || {},
   882          field = { name: fieldName, boost: opts.boost || 1 }
   883  
   884      this._fields.push(field)
   885      return this
   886    }
   887  
   888    /**
   889     * Sets the property used to uniquely identify documents added to the index,
   890     * by default this property is 'id'.
   891     *
   892     * This should only be changed before adding documents to the index, changing
   893     * the ref property without resetting the index can lead to unexpected results.
   894     *
   895     * @param {String} refName The property to use to uniquely identify the
   896     * documents in the index.
   897     * @param {Boolean} emitEvent Whether to emit add events, defaults to true
   898     * @returns {lunr.Index}
   899     * @memberOf Index
   900     */
   901    lunr.Index.prototype.ref = function (refName) {
   902      this._ref = refName
   903      return this
   904    }
   905  
   906    /**
   907     * Add a document to the index.
   908     *
   909     * This is the way new documents enter the index, this function will run the
   910     * fields from the document through the index's pipeline and then add it to
   911     * the index, it will then show up in search results.
   912     *
   913     * An 'add' event is emitted with the document that has been added and the index
   914     * the document has been added to. This event can be silenced by passing false
   915     * as the second argument to add.
   916     *
   917     * @param {Object} doc The document to add to the index.
   918     * @param {Boolean} emitEvent Whether or not to emit events, default true.
   919     * @memberOf Index
   920     */
   921    lunr.Index.prototype.add = function (doc, emitEvent) {
   922      var docTokens = {},
   923          allDocumentTokens = new lunr.SortedSet,
   924          docRef = doc[this._ref],
   925          emitEvent = emitEvent === undefined ? true : emitEvent
   926  
   927      this._fields.forEach(function (field) {
   928        var fieldTokens = this.pipeline.run(lunr.tokenizer(doc[field.name]))
   929  
   930        docTokens[field.name] = fieldTokens
   931        lunr.SortedSet.prototype.add.apply(allDocumentTokens, fieldTokens)
   932      }, this)
   933  
   934      this.documentStore.set(docRef, allDocumentTokens)
   935      lunr.SortedSet.prototype.add.apply(this.corpusTokens, allDocumentTokens.toArray())
   936  
   937      for (var i = 0; i < allDocumentTokens.length; i++) {
   938        var token = allDocumentTokens.elements[i]
   939        var tf = this._fields.reduce(function (memo, field) {
   940          var fieldLength = docTokens[field.name].length
   941  
   942          if (!fieldLength) return memo
   943  
   944          var tokenCount = docTokens[field.name].filter(function (t) { return t === token }).length
   945  
   946          return memo + (tokenCount / fieldLength * field.boost)
   947        }, 0)
   948  
   949        this.tokenStore.add(token, { ref: docRef, tf: tf })
   950      };
   951  
   952      if (emitEvent) this.eventEmitter.emit('add', doc, this)
   953    }
   954  
   955    /**
   956     * Removes a document from the index.
   957     *
   958     * To make sure documents no longer show up in search results they can be
   959     * removed from the index using this method.
   960     *
   961     * The document passed only needs to have the same ref property value as the
   962     * document that was added to the index, they could be completely different
   963     * objects.
   964     *
   965     * A 'remove' event is emitted with the document that has been removed and the index
   966     * the document has been removed from. This event can be silenced by passing false
   967     * as the second argument to remove.
   968     *
   969     * @param {Object} doc The document to remove from the index.
   970     * @param {Boolean} emitEvent Whether to emit remove events, defaults to true
   971     * @memberOf Index
   972     */
   973    lunr.Index.prototype.remove = function (doc, emitEvent) {
   974      var docRef = doc[this._ref],
   975          emitEvent = emitEvent === undefined ? true : emitEvent
   976  
   977      if (!this.documentStore.has(docRef)) return
   978  
   979      var docTokens = this.documentStore.get(docRef)
   980  
   981      this.documentStore.remove(docRef)
   982  
   983      docTokens.forEach(function (token) {
   984        this.tokenStore.remove(token, docRef)
   985      }, this)
   986  
   987      if (emitEvent) this.eventEmitter.emit('remove', doc, this)
   988    }
   989  
   990    /**
   991     * Updates a document in the index.
   992     *
   993     * When a document contained within the index gets updated, fields changed,
   994     * added or removed, to make sure it correctly matched against search queries,
   995     * it should be updated in the index.
   996     *
   997     * This method is just a wrapper around `remove` and `add`
   998     *
   999     * An 'update' event is emitted with the document that has been updated and the index.
  1000     * This event can be silenced by passing false as the second argument to update. Only
  1001     * an update event will be fired, the 'add' and 'remove' events of the underlying calls
  1002     * are silenced.
  1003     *
  1004     * @param {Object} doc The document to update in the index.
  1005     * @param {Boolean} emitEvent Whether to emit update events, defaults to true
  1006     * @see Index.prototype.remove
  1007     * @see Index.prototype.add
  1008     * @memberOf Index
  1009     */
  1010    lunr.Index.prototype.update = function (doc, emitEvent) {
  1011      var emitEvent = emitEvent === undefined ? true : emitEvent
  1012  
  1013      this.remove(doc, false)
  1014      this.add(doc, false)
  1015  
  1016      if (emitEvent) this.eventEmitter.emit('update', doc, this)
  1017    }
  1018  
  1019    /**
  1020     * Calculates the inverse document frequency for a token within the index.
  1021     *
  1022     * @param {String} token The token to calculate the idf of.
  1023     * @see Index.prototype.idf
  1024     * @private
  1025     * @memberOf Index
  1026     */
  1027    lunr.Index.prototype.idf = function (term) {
  1028      var cacheKey = "@" + term
  1029      if (Object.prototype.hasOwnProperty.call(this._idfCache, cacheKey)) return this._idfCache[cacheKey]
  1030  
  1031      var documentFrequency = this.tokenStore.count(term),
  1032          idf = 1
  1033  
  1034      if (documentFrequency > 0) {
  1035        idf = 1 + Math.log(this.tokenStore.length / documentFrequency)
  1036      }
  1037  
  1038      return this._idfCache[cacheKey] = idf
  1039    }
  1040  
  1041    /**
  1042     * Searches the index using the passed query.
  1043     *
  1044     * Queries should be a string, multiple words are allowed and will lead to an
  1045     * AND based query, e.g. `idx.search('foo bar')` will run a search for
  1046     * documents containing both 'foo' and 'bar'.
  1047     *
  1048     * All query tokens are passed through the same pipeline that document tokens
  1049     * are passed through, so any language processing involved will be run on every
  1050     * query term.
  1051     *
  1052     * Each query term is expanded, so that the term 'he' might be expanded to
  1053     * 'hello' and 'help' if those terms were already included in the index.
  1054     *
  1055     * Matching documents are returned as an array of objects, each object contains
  1056     * the matching document ref, as set for this index, and the similarity score
  1057     * for this document against the query.
  1058     *
  1059     * @param {String} query The query to search the index with.
  1060     * @returns {Object}
  1061     * @see Index.prototype.idf
  1062     * @see Index.prototype.documentVector
  1063     * @memberOf Index
  1064     */
  1065    lunr.Index.prototype.search = function (query) {
  1066      var queryTokens = this.pipeline.run(lunr.tokenizer(query)),
  1067          queryVector = new lunr.Vector,
  1068          documentSets = [],
  1069          fieldBoosts = this._fields.reduce(function (memo, f) { return memo + f.boost }, 0)
  1070  
  1071      var hasSomeToken = queryTokens.some(function (token) {
  1072        return this.tokenStore.has(token)
  1073      }, this)
  1074  
  1075      if (!hasSomeToken) return []
  1076  
  1077      queryTokens
  1078          .forEach(function (token, i, tokens) {
  1079            var tf = 1 / tokens.length * this._fields.length * fieldBoosts,
  1080                self = this
  1081  
  1082            var set = this.tokenStore.expand(token).reduce(function (memo, key) {
  1083              var pos = self.corpusTokens.indexOf(key),
  1084                  idf = self.idf(key),
  1085                  similarityBoost = 1,
  1086                  set = new lunr.SortedSet
  1087  
  1088              // if the expanded key is not an exact match to the token then
  1089              // penalise the score for this key by how different the key is
  1090              // to the token.
  1091              if (key !== token) {
  1092                var diff = Math.max(3, key.length - token.length)
  1093                similarityBoost = 1 / Math.log(diff)
  1094              }
  1095  
  1096              // calculate the query tf-idf score for this token
  1097              // applying an similarityBoost to ensure exact matches
  1098              // these rank higher than expanded terms
  1099              if (pos > -1) queryVector.insert(pos, tf * idf * similarityBoost)
  1100  
  1101              // add all the documents that have this key into a set
  1102              Object.keys(self.tokenStore.get(key)).forEach(function (ref) { set.add(ref) })
  1103  
  1104              return memo.union(set)
  1105            }, new lunr.SortedSet)
  1106  
  1107            documentSets.push(set)
  1108          }, this)
  1109  
  1110      var documentSet = documentSets.reduce(function (memo, set) {
  1111        return memo.intersect(set)
  1112      })
  1113  
  1114      return documentSet
  1115          .map(function (ref) {
  1116            return { ref: ref, score: queryVector.similarity(this.documentVector(ref)) }
  1117          }, this)
  1118          .sort(function (a, b) {
  1119            return b.score - a.score
  1120          })
  1121    }
  1122  
  1123    /**
  1124     * Generates a vector containing all the tokens in the document matching the
  1125     * passed documentRef.
  1126     *
  1127     * The vector contains the tf-idf score for each token contained in the
  1128     * document with the passed documentRef.  The vector will contain an element
  1129     * for every token in the indexes corpus, if the document does not contain that
  1130     * token the element will be 0.
  1131     *
  1132     * @param {Object} documentRef The ref to find the document with.
  1133     * @returns {lunr.Vector}
  1134     * @private
  1135     * @memberOf Index
  1136     */
  1137    lunr.Index.prototype.documentVector = function (documentRef) {
  1138      var documentTokens = this.documentStore.get(documentRef),
  1139          documentTokensLength = documentTokens.length,
  1140          documentVector = new lunr.Vector
  1141  
  1142      for (var i = 0; i < documentTokensLength; i++) {
  1143        var token = documentTokens.elements[i],
  1144            tf = this.tokenStore.get(token)[documentRef].tf,
  1145            idf = this.idf(token)
  1146  
  1147        documentVector.insert(this.corpusTokens.indexOf(token), tf * idf)
  1148      };
  1149  
  1150      return documentVector
  1151    }
  1152  
  1153    /**
  1154     * Returns a representation of the index ready for serialisation.
  1155     *
  1156     * @returns {Object}
  1157     * @memberOf Index
  1158     */
  1159    lunr.Index.prototype.toJSON = function () {
  1160      return {
  1161        version: lunr.version,
  1162        fields: this._fields,
  1163        ref: this._ref,
  1164        documentStore: this.documentStore.toJSON(),
  1165        tokenStore: this.tokenStore.toJSON(),
  1166        corpusTokens: this.corpusTokens.toJSON(),
  1167        pipeline: this.pipeline.toJSON()
  1168      }
  1169    }
  1170  
  1171    /**
  1172     * Applies a plugin to the current index.
  1173     *
  1174     * A plugin is a function that is called with the index as its context.
  1175     * Plugins can be used to customise or extend the behaviour the index
  1176     * in some way. A plugin is just a function, that encapsulated the custom
  1177     * behaviour that should be applied to the index.
  1178     *
  1179     * The plugin function will be called with the index as its argument, additional
  1180     * arguments can also be passed when calling use. The function will be called
  1181     * with the index as its context.
  1182     *
  1183     * Example:
  1184     *
  1185     *     var myPlugin = function (idx, arg1, arg2) {
  1186   *       // `this` is the index to be extended
  1187   *       // apply any extensions etc here.
  1188   *     }
  1189     *
  1190     *     var idx = lunr(function () {
  1191   *       this.use(myPlugin, 'arg1', 'arg2')
  1192   *     })
  1193     *
  1194     * @param {Function} plugin The plugin to apply.
  1195     * @memberOf Index
  1196     */
  1197    lunr.Index.prototype.use = function (plugin) {
  1198      var args = Array.prototype.slice.call(arguments, 1)
  1199      args.unshift(this)
  1200      plugin.apply(this, args)
  1201    }
  1202    /*!
  1203     * lunr.Store
  1204     * Copyright (C) 2014 Oliver Nightingale
  1205     */
  1206  
  1207    /**
  1208     * lunr.Store is a simple key-value store used for storing sets of tokens for
  1209     * documents stored in index.
  1210     *
  1211     * @constructor
  1212     * @module
  1213     */
  1214    lunr.Store = function () {
  1215      this.store = {}
  1216      this.length = 0
  1217    }
  1218  
  1219    /**
  1220     * Loads a previously serialised store
  1221     *
  1222     * @param {Object} serialisedData The serialised store to load.
  1223     * @returns {lunr.Store}
  1224     * @memberOf Store
  1225     */
  1226    lunr.Store.load = function (serialisedData) {
  1227      var store = new this
  1228  
  1229      store.length = serialisedData.length
  1230      store.store = Object.keys(serialisedData.store).reduce(function (memo, key) {
  1231        memo[key] = lunr.SortedSet.load(serialisedData.store[key])
  1232        return memo
  1233      }, {})
  1234  
  1235      return store
  1236    }
  1237  
  1238    /**
  1239     * Stores the given tokens in the store against the given id.
  1240     *
  1241     * @param {Object} id The key used to store the tokens against.
  1242     * @param {Object} tokens The tokens to store against the key.
  1243     * @memberOf Store
  1244     */
  1245    lunr.Store.prototype.set = function (id, tokens) {
  1246      if (!this.has(id)) this.length++
  1247      this.store[id] = tokens
  1248    }
  1249  
  1250    /**
  1251     * Retrieves the tokens from the store for a given key.
  1252     *
  1253     * @param {Object} id The key to lookup and retrieve from the store.
  1254     * @returns {Object}
  1255     * @memberOf Store
  1256     */
  1257    lunr.Store.prototype.get = function (id) {
  1258      return this.store[id]
  1259    }
  1260  
  1261    /**
  1262     * Checks whether the store contains a key.
  1263     *
  1264     * @param {Object} id The id to look up in the store.
  1265     * @returns {Boolean}
  1266     * @memberOf Store
  1267     */
  1268    lunr.Store.prototype.has = function (id) {
  1269      return id in this.store
  1270    }
  1271  
  1272    /**
  1273     * Removes the value for a key in the store.
  1274     *
  1275     * @param {Object} id The id to remove from the store.
  1276     * @memberOf Store
  1277     */
  1278    lunr.Store.prototype.remove = function (id) {
  1279      if (!this.has(id)) return
  1280  
  1281      delete this.store[id]
  1282      this.length--
  1283    }
  1284  
  1285    /**
  1286     * Returns a representation of the store ready for serialisation.
  1287     *
  1288     * @returns {Object}
  1289     * @memberOf Store
  1290     */
  1291    lunr.Store.prototype.toJSON = function () {
  1292      return {
  1293        store: this.store,
  1294        length: this.length
  1295      }
  1296    }
  1297  
  1298    /*!
  1299     * lunr.stemmer
  1300     * Copyright (C) 2014 Oliver Nightingale
  1301     * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
  1302     */
  1303  
  1304    /**
  1305     * lunr.stemmer is an english language stemmer, this is a JavaScript
  1306     * implementation of the PorterStemmer taken from http://tartaurs.org/~martin
  1307     *
  1308     * @module
  1309     * @param {String} str The string to stem
  1310     * @returns {String}
  1311     * @see lunr.Pipeline
  1312     */
  1313    lunr.stemmer = (function(){
  1314      var step2list = {
  1315            "ational" : "ate",
  1316            "tional" : "tion",
  1317            "enci" : "ence",
  1318            "anci" : "ance",
  1319            "izer" : "ize",
  1320            "bli" : "ble",
  1321            "alli" : "al",
  1322            "entli" : "ent",
  1323            "eli" : "e",
  1324            "ousli" : "ous",
  1325            "ization" : "ize",
  1326            "ation" : "ate",
  1327            "ator" : "ate",
  1328            "alism" : "al",
  1329            "iveness" : "ive",
  1330            "fulness" : "ful",
  1331            "ousness" : "ous",
  1332            "aliti" : "al",
  1333            "iviti" : "ive",
  1334            "biliti" : "ble",
  1335            "logi" : "log"
  1336          },
  1337  
  1338          step3list = {
  1339            "icate" : "ic",
  1340            "ative" : "",
  1341            "alize" : "al",
  1342            "iciti" : "ic",
  1343            "ical" : "ic",
  1344            "ful" : "",
  1345            "ness" : ""
  1346          },
  1347  
  1348          c = "[^aeiou]",          // consonant
  1349          v = "[aeiouy]",          // vowel
  1350          C = c + "[^aeiouy]*",    // consonant sequence
  1351          V = v + "[aeiou]*",      // vowel sequence
  1352  
  1353          mgr0 = "^(" + C + ")?" + V + C,               // [C]VC... is m>0
  1354          meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$",  // [C]VC[V] is m=1
  1355          mgr1 = "^(" + C + ")?" + V + C + V + C,       // [C]VCVC... is m>1
  1356          s_v = "^(" + C + ")?" + v;                   // vowel in stem
  1357  
  1358      var re_mgr0 = new RegExp(mgr0);
  1359      var re_mgr1 = new RegExp(mgr1);
  1360      var re_meq1 = new RegExp(meq1);
  1361      var re_s_v = new RegExp(s_v);
  1362  
  1363      var re_1a = /^(.+?)(ss|i)es$/;
  1364      var re2_1a = /^(.+?)([^s])s$/;
  1365      var re_1b = /^(.+?)eed$/;
  1366      var re2_1b = /^(.+?)(ed|ing)$/;
  1367      var re_1b_2 = /.$/;
  1368      var re2_1b_2 = /(at|bl|iz)$/;
  1369      var re3_1b_2 = new RegExp("([^aeiouylsz])\\1$");
  1370      var re4_1b_2 = new RegExp("^" + C + v + "[^aeiouwxy]$");
  1371  
  1372      var re_1c = /^(.+?[^aeiou])y$/;
  1373      var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
  1374  
  1375      var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
  1376  
  1377      var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
  1378      var re2_4 = /^(.+?)(s|t)(ion)$/;
  1379  
  1380      var re_5 = /^(.+?)e$/;
  1381      var re_5_1 = /ll$/;
  1382      var re3_5 = new RegExp("^" + C + v + "[^aeiouwxy]$");
  1383  
  1384      var porterStemmer = function porterStemmer(w) {
  1385        var   stem,
  1386            suffix,
  1387            firstch,
  1388            re,
  1389            re2,
  1390            re3,
  1391            re4;
  1392  
  1393        if (w.length < 3) { return w; }
  1394  
  1395        firstch = w.substr(0,1);
  1396        if (firstch == "y") {
  1397          w = firstch.toUpperCase() + w.substr(1);
  1398        }
  1399  
  1400        // Step 1a
  1401        re = re_1a
  1402        re2 = re2_1a;
  1403  
  1404        if (re.test(w)) { w = w.replace(re,"$1$2"); }
  1405        else if (re2.test(w)) { w = w.replace(re2,"$1$2"); }
  1406  
  1407        // Step 1b
  1408        re = re_1b;
  1409        re2 = re2_1b;
  1410        if (re.test(w)) {
  1411          var fp = re.exec(w);
  1412          re = re_mgr0;
  1413          if (re.test(fp[1])) {
  1414            re = re_1b_2;
  1415            w = w.replace(re,"");
  1416          }
  1417        } else if (re2.test(w)) {
  1418          var fp = re2.exec(w);
  1419          stem = fp[1];
  1420          re2 = re_s_v;
  1421          if (re2.test(stem)) {
  1422            w = stem;
  1423            re2 = re2_1b_2;
  1424            re3 = re3_1b_2;
  1425            re4 = re4_1b_2;
  1426            if (re2.test(w)) {  w = w + "e"; }
  1427            else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,""); }
  1428            else if (re4.test(w)) { w = w + "e"; }
  1429          }
  1430        }
  1431  
  1432        // 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)
  1433        re = re_1c;
  1434        if (re.test(w)) {
  1435          var fp = re.exec(w);
  1436          stem = fp[1];
  1437          w = stem + "i";
  1438        }
  1439  
  1440        // Step 2
  1441        re = re_2;
  1442        if (re.test(w)) {
  1443          var fp = re.exec(w);
  1444          stem = fp[1];
  1445          suffix = fp[2];
  1446          re = re_mgr0;
  1447          if (re.test(stem)) {
  1448            w = stem + step2list[suffix];
  1449          }
  1450        }
  1451  
  1452        // Step 3
  1453        re = re_3;
  1454        if (re.test(w)) {
  1455          var fp = re.exec(w);
  1456          stem = fp[1];
  1457          suffix = fp[2];
  1458          re = re_mgr0;
  1459          if (re.test(stem)) {
  1460            w = stem + step3list[suffix];
  1461          }
  1462        }
  1463  
  1464        // Step 4
  1465        re = re_4;
  1466        re2 = re2_4;
  1467        if (re.test(w)) {
  1468          var fp = re.exec(w);
  1469          stem = fp[1];
  1470          re = re_mgr1;
  1471          if (re.test(stem)) {
  1472            w = stem;
  1473          }
  1474        } else if (re2.test(w)) {
  1475          var fp = re2.exec(w);
  1476          stem = fp[1] + fp[2];
  1477          re2 = re_mgr1;
  1478          if (re2.test(stem)) {
  1479            w = stem;
  1480          }
  1481        }
  1482  
  1483        // Step 5
  1484        re = re_5;
  1485        if (re.test(w)) {
  1486          var fp = re.exec(w);
  1487          stem = fp[1];
  1488          re = re_mgr1;
  1489          re2 = re_meq1;
  1490          re3 = re3_5;
  1491          if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {
  1492            w = stem;
  1493          }
  1494        }
  1495  
  1496        re = re_5_1;
  1497        re2 = re_mgr1;
  1498        if (re.test(w) && re2.test(w)) {
  1499          re = re_1b_2;
  1500          w = w.replace(re,"");
  1501        }
  1502  
  1503        // and turn initial Y back to y
  1504  
  1505        if (firstch == "y") {
  1506          w = firstch.toLowerCase() + w.substr(1);
  1507        }
  1508  
  1509        return w;
  1510      };
  1511  
  1512      return porterStemmer;
  1513    })();
  1514  
  1515    lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer')
  1516    /*!
  1517     * lunr.stopWordFilter
  1518     * Copyright (C) 2014 Oliver Nightingale
  1519     */
  1520  
  1521    /**
  1522     * lunr.stopWordFilter is an English language stop word list filter, any words
  1523     * contained in the list will not be passed through the filter.
  1524     *
  1525     * This is intended to be used in the Pipeline. If the token does not pass the
  1526     * filter then undefined will be returned.
  1527     *
  1528     * @module
  1529     * @param {String} token The token to pass through the filter
  1530     * @returns {String}
  1531     * @see lunr.Pipeline
  1532     */
  1533    lunr.stopWordFilter = function (token) {
  1534      if (lunr.stopWordFilter.stopWords.indexOf(token) === -1) return token
  1535    }
  1536  
  1537    lunr.stopWordFilter.stopWords = new lunr.SortedSet
  1538    lunr.stopWordFilter.stopWords.length = 119
  1539    lunr.stopWordFilter.stopWords.elements = [
  1540      "",
  1541      "a",
  1542      "able",
  1543      "about",
  1544      "across",
  1545      "after",
  1546      "all",
  1547      "almost",
  1548      "also",
  1549      "am",
  1550      "among",
  1551      "an",
  1552      "and",
  1553      "any",
  1554      "are",
  1555      "as",
  1556      "at",
  1557      "be",
  1558      "because",
  1559      "been",
  1560      "but",
  1561      "by",
  1562      "can",
  1563      "cannot",
  1564      "could",
  1565      "dear",
  1566      "did",
  1567      "do",
  1568      "does",
  1569      "either",
  1570      "else",
  1571      "ever",
  1572      "every",
  1573      "for",
  1574      "from",
  1575      "get",
  1576      "got",
  1577      "had",
  1578      "has",
  1579      "have",
  1580      "he",
  1581      "her",
  1582      "hers",
  1583      "him",
  1584      "his",
  1585      "how",
  1586      "however",
  1587      "i",
  1588      "if",
  1589      "in",
  1590      "into",
  1591      "is",
  1592      "it",
  1593      "its",
  1594      "just",
  1595      "least",
  1596      "let",
  1597      "like",
  1598      "likely",
  1599      "may",
  1600      "me",
  1601      "might",
  1602      "most",
  1603      "must",
  1604      "my",
  1605      "neither",
  1606      "no",
  1607      "nor",
  1608      "not",
  1609      "of",
  1610      "off",
  1611      "often",
  1612      "on",
  1613      "only",
  1614      "or",
  1615      "other",
  1616      "our",
  1617      "own",
  1618      "rather",
  1619      "said",
  1620      "say",
  1621      "says",
  1622      "she",
  1623      "should",
  1624      "since",
  1625      "so",
  1626      "some",
  1627      "than",
  1628      "that",
  1629      "the",
  1630      "their",
  1631      "them",
  1632      "then",
  1633      "there",
  1634      "these",
  1635      "they",
  1636      "this",
  1637      "tis",
  1638      "to",
  1639      "too",
  1640      "twas",
  1641      "us",
  1642      "wants",
  1643      "was",
  1644      "we",
  1645      "were",
  1646      "what",
  1647      "when",
  1648      "where",
  1649      "which",
  1650      "while",
  1651      "who",
  1652      "whom",
  1653      "why",
  1654      "will",
  1655      "with",
  1656      "would",
  1657      "yet",
  1658      "you",
  1659      "your"
  1660    ]
  1661  
  1662    lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter')
  1663    /*!
  1664     * lunr.trimmer
  1665     * Copyright (C) 2014 Oliver Nightingale
  1666     */
  1667  
  1668    /**
  1669     * lunr.trimmer is a pipeline function for trimming non word
  1670     * characters from the begining and end of tokens before they
  1671     * enter the index.
  1672     *
  1673     * This implementation may not work correctly for non latin
  1674     * characters and should either be removed or adapted for use
  1675     * with languages with non-latin characters.
  1676     *
  1677     * @module
  1678     * @param {String} token The token to pass through the filter
  1679     * @returns {String}
  1680     * @see lunr.Pipeline
  1681     */
  1682    lunr.trimmer = function (token) {
  1683      return token
  1684          .replace(/^\W+/, '')
  1685          .replace(/\W+$/, '')
  1686    }
  1687  
  1688    lunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer')
  1689    /*!
  1690     * lunr.stemmer
  1691     * Copyright (C) 2014 Oliver Nightingale
  1692     * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
  1693     */
  1694  
  1695    /**
  1696     * lunr.TokenStore is used for efficient storing and lookup of the reverse
  1697     * index of token to document ref.
  1698     *
  1699     * @constructor
  1700     */
  1701    lunr.TokenStore = function () {
  1702      this.root = { docs: {} }
  1703      this.length = 0
  1704    }
  1705  
  1706    /**
  1707     * Loads a previously serialised token store
  1708     *
  1709     * @param {Object} serialisedData The serialised token store to load.
  1710     * @returns {lunr.TokenStore}
  1711     * @memberOf TokenStore
  1712     */
  1713    lunr.TokenStore.load = function (serialisedData) {
  1714      var store = new this
  1715  
  1716      store.root = serialisedData.root
  1717      store.length = serialisedData.length
  1718  
  1719      return store
  1720    }
  1721  
  1722    /**
  1723     * Adds a new token doc pair to the store.
  1724     *
  1725     * By default this function starts at the root of the current store, however
  1726     * it can start at any node of any token store if required.
  1727     *
  1728     * @param {String} token The token to store the doc under
  1729     * @param {Object} doc The doc to store against the token
  1730     * @param {Object} root An optional node at which to start looking for the
  1731     * correct place to enter the doc, by default the root of this lunr.TokenStore
  1732     * is used.
  1733     * @memberOf TokenStore
  1734     */
  1735    lunr.TokenStore.prototype.add = function (token, doc, root) {
  1736      var root = root || this.root,
  1737          key = token[0],
  1738          rest = token.slice(1)
  1739  
  1740      if (!(key in root)) root[key] = {docs: {}}
  1741  
  1742      if (rest.length === 0) {
  1743        root[key].docs[doc.ref] = doc
  1744        this.length += 1
  1745        return
  1746      } else {
  1747        return this.add(rest, doc, root[key])
  1748      }
  1749    }
  1750  
  1751    /**
  1752     * Checks whether this key is contained within this lunr.TokenStore.
  1753     *
  1754     * By default this function starts at the root of the current store, however
  1755     * it can start at any node of any token store if required.
  1756     *
  1757     * @param {String} token The token to check for
  1758     * @param {Object} root An optional node at which to start
  1759     * @memberOf TokenStore
  1760     */
  1761    lunr.TokenStore.prototype.has = function (token) {
  1762      if (!token) return false
  1763  
  1764      var node = this.root
  1765  
  1766      for (var i = 0; i < token.length; i++) {
  1767        if (!node[token[i]]) return false
  1768  
  1769        node = node[token[i]]
  1770      }
  1771  
  1772      return true
  1773    }
  1774  
  1775    /**
  1776     * Retrieve a node from the token store for a given token.
  1777     *
  1778     * By default this function starts at the root of the current store, however
  1779     * it can start at any node of any token store if required.
  1780     *
  1781     * @param {String} token The token to get the node for.
  1782     * @param {Object} root An optional node at which to start.
  1783     * @returns {Object}
  1784     * @see TokenStore.prototype.get
  1785     * @memberOf TokenStore
  1786     */
  1787    lunr.TokenStore.prototype.getNode = function (token) {
  1788      if (!token) return {}
  1789  
  1790      var node = this.root
  1791  
  1792      for (var i = 0; i < token.length; i++) {
  1793        if (!node[token[i]]) return {}
  1794  
  1795        node = node[token[i]]
  1796      }
  1797  
  1798      return node
  1799    }
  1800  
  1801    /**
  1802     * Retrieve the documents for a node for the given token.
  1803     *
  1804     * By default this function starts at the root of the current store, however
  1805     * it can start at any node of any token store if required.
  1806     *
  1807     * @param {String} token The token to get the documents for.
  1808     * @param {Object} root An optional node at which to start.
  1809     * @returns {Object}
  1810     * @memberOf TokenStore
  1811     */
  1812    lunr.TokenStore.prototype.get = function (token, root) {
  1813      return this.getNode(token, root).docs || {}
  1814    }
  1815  
  1816    lunr.TokenStore.prototype.count = function (token, root) {
  1817      return Object.keys(this.get(token, root)).length
  1818    }
  1819  
  1820    /**
  1821     * Remove the document identified by ref from the token in the store.
  1822     *
  1823     * By default this function starts at the root of the current store, however
  1824     * it can start at any node of any token store if required.
  1825     *
  1826     * @param {String} token The token to get the documents for.
  1827     * @param {String} ref The ref of the document to remove from this token.
  1828     * @param {Object} root An optional node at which to start.
  1829     * @returns {Object}
  1830     * @memberOf TokenStore
  1831     */
  1832    lunr.TokenStore.prototype.remove = function (token, ref) {
  1833      if (!token) return
  1834      var node = this.root
  1835  
  1836      for (var i = 0; i < token.length; i++) {
  1837        if (!(token[i] in node)) return
  1838        node = node[token[i]]
  1839      }
  1840  
  1841      delete node.docs[ref]
  1842    }
  1843  
  1844    /**
  1845     * Find all the possible suffixes of the passed token using tokens
  1846     * currently in the store.
  1847     *
  1848     * @param {String} token The token to expand.
  1849     * @returns {Array}
  1850     * @memberOf TokenStore
  1851     */
  1852    lunr.TokenStore.prototype.expand = function (token, memo) {
  1853      var root = this.getNode(token),
  1854          docs = root.docs || {},
  1855          memo = memo || []
  1856  
  1857      if (Object.keys(docs).length) memo.push(token)
  1858  
  1859      Object.keys(root)
  1860          .forEach(function (key) {
  1861            if (key === 'docs') return
  1862  
  1863            memo.concat(this.expand(token + key, memo))
  1864          }, this)
  1865  
  1866      return memo
  1867    }
  1868  
  1869    /**
  1870     * Returns a representation of the token store ready for serialisation.
  1871     *
  1872     * @returns {Object}
  1873     * @memberOf TokenStore
  1874     */
  1875    lunr.TokenStore.prototype.toJSON = function () {
  1876      return {
  1877        root: this.root,
  1878        length: this.length
  1879      }
  1880    }
  1881  
  1882  
  1883    /**
  1884     * export the module via AMD, CommonJS or as a browser global
  1885     * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js
  1886     */
  1887    ;(function (root, factory) {
  1888      if (typeof define === 'function' && define.amd) {
  1889        // AMD. Register as an anonymous module.
  1890        define(factory)
  1891      } else if (typeof exports === 'object') {
  1892        /**
  1893         * Node. Does not work with strict CommonJS, but
  1894         * only CommonJS-like enviroments that support module.exports,
  1895         * like Node.
  1896         */
  1897        module.exports = factory()
  1898      } else {
  1899        // Browser globals (root is window)
  1900        root.lunr = factory()
  1901      }
  1902    }(this, function () {
  1903      /**
  1904       * Just return a value to define the module export.
  1905       * This example returns an object, but the module
  1906       * can return a function as the exported value.
  1907       */
  1908      return lunr
  1909    }))
  1910  })()