PageSourceSearch

https://www.nameswitch.co.uk/js/lunr.js

js nameswitch.co.uk collected 2026-10-04 05:03:22 UTC 96,598 bytes, 2,977 lines download raw bytes

vendor: 4,410 bytes, lines 1-157
1/**
2 * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.1.5
3 * Copyright (C) 2017 Oliver Nightingale
4 * @license MIT
5 */
6
7;(function(){
8
9    /**
10     * A convenience function for configuring and constructing
11     * a new lunr Index.
12     *
13     * A lunr.Builder instance is created and the pipeline setup
14     * with a trimmer, stop word filter and stemmer.
15     *
16     * This builder object is yielded to the configuration function
17     * that is passed as a parameter, allowing the list of fields
18     * and other builder parameters to be customised.
19     *
20     * All documents _must_ be added within the passed config function.
21     *
22     * @example
23     * var idx = lunr(function () {
24     *   this.field('title')
25     *   this.field('body')
26     *   this.ref('id')
27     *
28     *   documents.forEach(function (doc) {
29     *     this.add(doc)
30     *   }, this)
31     * })
32     *
33     * @see {@link lunr.Builder}
34     * @see {@link lunr.Pipeline}
35     * @see {@link lunr.trimmer}
36     * @see {@link lunr.stopWordFilter}
37     * @see {@link lunr.stemmer}
38     * @namespace {function} lunr
39     */
40    var lunr = function (config) {
41      var builder = new lunr.Builder
42    
43      builder.pipeline.add(
44        lunr.trimmer,
45        lunr.stopWordFilter,
46        lunr.stemmer
47      )
48    
49      builder.searchPipeline.add(
50        lunr.stemmer
51      )
52    
53      config.call(builder, builder)
54      return builder.build()
55    }
56    
57    lunr.version = "2.1.5"
58    /*!
59     * lunr.utils
60     * Copyright (C) 2017 Oliver Nightingale
61     */
62    
63    /**
64     * A namespace containing utils for the rest of the lunr library
65     */
66    lunr.utils = {}
67    
68    /**
69     * Print a warning message to the console.
70     *
71     * @param {String} message The message to be printed.
72     * @memberOf Utils
73     */
74    lunr.utils.warn = (function (global) {
75      /* eslint-disable no-console */
76      return function (message) {
77        if (global.console && console.warn) {
78          console.warn(message)
79        }
80      }
81      /* eslint-enable no-console */
82    })(this)
83    
84    /**
85     * Convert an object to a string.
86     *
87     * In the case of `null` and `undefined` the function returns
88     * the empty string, in all other cases the result of calling
89     * `toString` on the passed object is returned.
90     *
91     * @param {Any} obj The object to convert to a string.
92     * @return {String} string representation of the passed object.
93     * @memberOf Utils
94     */
95    lunr.utils.asString = function (obj) {
96      if (obj === void 0 || obj === null) {
97        return ""
98      } else {
99        return obj.toString()
100      }
101    }
102    lunr.FieldRef = function (docRef, fieldName, stringValue) {
103      this.docRef = docRef
104      this.fieldName = fieldName
105      this._stringValue = stringValue
106    }
107    
108    lunr.FieldRef.joiner = "/"
109    
110    lunr.FieldRef.fromString = function (s) {
111      var n = s.indexOf(lunr.FieldRef.joiner)
112    
113      if (n === -1) {
114        throw "malformed field ref string"
115      }
116    
117      var fieldRef = s.slice(0, n),
118          docRef = s.slice(n + 1)
119    
120      return new lunr.FieldRef (docRef, fieldRef, s)
121    }
122    
123    lunr.FieldRef.prototype.toString = function () {
124      if (this._stringValue == undefined) {
125        this._stringValue = this.fieldName + lunr.FieldRef.joiner + this.docRef
126      }
127    
128      return this._stringValue
129    }
130    /**
131     * A function to calculate the inverse document frequency for
132     * a posting. This is shared between the builder and the index
133     *
134     * @private
135     * @param {object} posting - The posting for a given term
136     * @param {number} documentCount - The total number of documents.
137     */
138    lunr.idf = function (posting, documentCount) {
139      var documentsWithTerm = 0
140    
141      for (var fieldName in posting) {
142        if (fieldName == '_index') continue // Ignore the term index, its not a field
143        documentsWithTerm += Object.keys(posting[fieldName]).length
144      }
145    
146      var x = (documentCount - documentsWithTerm + 0.5) / (documentsWithTerm + 0.5)
147    
148      return Math.log(1 + Math.abs(x))
149    }
150    
151    /**
152     * A token wraps a string representation of a token
153     * as it is passed through the text processing pipeline.
154     *
155     * @constructor
156     * @param {string} [str=''] - The string token being wrapped.
157     * @param {object} [metadata={}
157] - Metadata associated with this token.
158     */
159    lunr.Token = function (str, metadata) {
160      this.str = str || ""
161      this.metadata = metadata || {}
162    }
163    
164    /**
165     * Returns the token string that is being wrapped by this object.
166     *
167     * @returns {string}
168     */
169    lunr.Token.prototype.toString = function () {
170      return this.str
171    }
172    
173    /**
174     * A token update function is used when updating or optionally
175     * when cloning a token.
176     *
177     * @callback lunr.Token~updateFunction
178     * @param {string} str - The string representation of the token.
179     * @param {Object} metadata - All metadata associated with this token.
180     */
181    
182    /**
183     * Applies the given function to the wrapped string token.
184     *
185     * @example
186     * token.update(function (str, metadata) {
187     *   return str.toUpperCase()
188     * })
189     *
190     * @param {lunr.Token~updateFunction} fn - A function to apply to the token string.
191     * @returns {lunr.Token}
192     */
193    lunr.Token.prototype.update = function (fn) {
194      this.str = fn(this.str, this.metadata)
195      return this
196    }
197    
198    /**
199     * Creates a clone of this token. Optionally a function can be
200     * applied to the cloned token.
201     *
202     * @param {lunr.Token~updateFunction} [fn] - An optional function to apply to the cloned token.
203     * @returns {lunr.Token}
204     */
205    lunr.Token.prototype.clone = function (fn) {
206      fn = fn || function (s) { return s }
207      return new lunr.Token (fn(this.str, this.metadata), this.metadata)
208    }
209    /*!
210     * lunr.tokenizer
211     * Copyright (C) 2017 Oliver Nightingale
212     */
213    
214    /**
215     * A function for splitting a string into tokens ready to be inserted into
216     * the search index. Uses `lunr.tokenizer.separator` to split strings, change
217     * the value of this property to change how strings are split into tokens.
218     *
219     * This tokenizer will convert its parameter to a string by calling `toString` and
220     * then will split this string on the character in `lunr.tokenizer.separator`.
221     * Arrays will have their elements converted to strings and wrapped in a lunr.Token.
222     *
223     * @static
224     * @param {?(string|object|object[])} obj - The object to convert into tokens
225     * @returns {lunr.Token[]}
226     */
227    lunr.tokenizer = function (obj) {
228      if (obj == null || obj == undefined) {
229        return []
230      }
231    
232      if (Array.isArray(obj)) {
233        return obj.map(function (t) {
234          return new lunr.Token(lunr.utils.asString(t).toLowerCase())
235        })
236      }
237    
238      var str = obj.toString().trim().toLowerCase(),
239          len = str.length,
240          tokens = []
241    
242      for (var sliceEnd = 0, sliceStart = 0; sliceEnd <= len; sliceEnd++) {
243        var char = str.charAt(sliceEnd),
244            sliceLength = sliceEnd - sliceStart
245    
246        if ((char.match(lunr.tokenizer.separator) || sliceEnd == len)) {
247    
248          if (sliceLength > 0) {
249            tokens.push(
250              new lunr.Token (str.slice(sliceStart, sliceEnd), {
251                position: [sliceStart, sliceLength],
252                index: tokens.length
253              })
254            )
255          }
256    
257          sliceStart = sliceEnd + 1
258        }
259    
260      }
261    
262      return tokens
263    }
264    
265    /**
266     * The separator used to split a string into tokens. Override this property to change the behaviour of
267     * `lunr.tokenizer` behaviour when tokenizing strings. By default this splits on whitespace and hyphens.
268     *
269     * @static
270     * @see lunr.tokenizer
271     */
272    lunr.tokenizer.separator = /[\s\-]+/
273    /*!
274     * lunr.Pipeline
275     * Copyright (C) 2017 Oliver Nightingale
276     */
277    
278    /**
279     * lunr.Pipelines maintain an ordered list of functions to be applied to all
280     * tokens in documents entering the search index and queries being ran against
281     * the index.
282     *
283     * An instance of lunr.Index created with the lunr shortcut will contain a
284     * pipeline with a stop word filter and an English language stemmer. Extra
285     * functions can be added before or after either of these functions or these
286     * default functions can be removed.
287     *
288     * When run the pipeline will call each function in turn, passing a token, the
289     * index of that token in the original list of all tokens and finally a list of
290     * all the original tokens.
291     *
292     * The output of functions in the pipeline will be passed to the next function
293     * in the pipeline. To exclude a token from entering the index the function
294     * should return undefined, the rest of the pipeline will not be called with
295     * this token.
296     *
297     * For serialisation of pipelines to work, all functions used in an instance of
298     * a pipeline should be registered with lunr.Pipeline. Registered functions can
299     * then be loaded. If trying to load a serialised pipeline that uses functions
300     * that are not registered an error will be thrown.
301     *
302     * If not planning on serialising the pipeline then registering pipeline functions
303     * is not necessary.
304     *
305     * @constructor
306     */
307    lunr.Pipeline = function () {
308      this._stack = []
309    }
310    
311    lunr.Pipeline.registeredFunctions = Object.create(null)
312    
313    /**
314     * A pipeline function maps lunr.Token to lunr.Token. A lunr.Token contains the token
315     * string as well as all known metadata. A pipeline function can mutate the token string
316     * or mutate (or add) metadata for a given token.
317     *
318     * A pipeline function can indicate that the passed token should be discarded by returning
319     * null. This token will not be passed to any downstream pipeline functions and will not be
320     * added to the index.
321     *
322     * Multiple tokens can be returned by returning an array of tokens. Each token will be passed
323     * to any downstream pipeline functions and all will returned tokens will be added to the index.
324     *
325     * Any number of pipeline functions may be chained together using a lunr.Pipeline.
326     *
327     * @interface lunr.PipelineFunction
328     * @param {lunr.Token} token - A token from the document being processed.
329     * @param {number} i - The index of this token in the complete list of tokens for this document/field.
330     * @param {lunr.Token[]} tokens - All tokens for this document/field.
331     * @returns {(?lunr.Token|lunr.Token[])}
332     */
333    
334    /**
335     * Register a function with the pipeline.
336     *
337     * Functions that are used in the pipeline should be registered if the pipeline
338     * needs to be serialised, or a serialised pipeline needs to be loaded.
339     *
340     * Registering a function does not add it to a pipeline, functions must still be
341     * added to instances of the pipeline for them to be used when running a pipeline.
342     *
343     * @param {lunr.PipelineFunction} fn - The function to check for.
344     * @param {String} label - The label to register this function with
345     */
346    lunr.Pipeline.registerFunction = function (fn, label) {
347      if (label in this.registeredFunctions) {
348        lunr.utils.warn('Overwriting existing registered function: ' + label)
349      }
350    
351      fn.label = label
352      lunr.Pipeline.registeredFunctions[fn.label] = fn
353    }
354    
355    /**
356     * Warns if the function is not registered as a Pipeline function.
357     *
358     * @param {lunr.PipelineFunction} fn - The function to check for.
359     * @private
360     */
361    lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {
362      var isRegistered = fn.label && (fn.label in this.registeredFunctions)
363    
364      if (!isRegistered) {
365        lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn)
366      }
367    }
368    
369    /**
370     * Loads a previously serialised pipeline.
371     *
372     * All functions to be loaded must already be registered with lunr.Pipeline.
373     * If any function from the serialised data has not been registered then an
374     * error will be thrown.
375     *
376     * @param {Object}
376 serialised - The serialised pipeline to load.
377     * @returns {lunr.Pipeline}
378     */
379    lunr.Pipeline.load = function (serialised) {
380      var pipeline = new lunr.Pipeline
381    
382      serialised.forEach(function (fnName) {
383        var fn = lunr.Pipeline.registeredFunctions[fnName]
384    
385        if (fn) {
386          pipeline.add(fn)
387        } else {
388          throw new Error('Cannot load unregistered function: ' + fnName)
389        }
390      })
391    
392      return pipeline
393    }
394    
395    /**
396     * Adds new functions to the end of the pipeline.
397     *
398     * Logs a warning if the function has not been registered.
399     *
400     * @param {lunr.PipelineFunction[]} functions - Any number of functions to add to the pipeline.
401     */
402    lunr.Pipeline.prototype.add = function () {
403      var fns = Array.prototype.slice.call(arguments)
404    
405      fns.forEach(function (fn) {
406        lunr.Pipeline.warnIfFunctionNotRegistered(fn)
407        this._stack.push(fn)
408      }, this)
409    }
410    
411    /**
412     * Adds a single function after a function that already exists in the
413     * pipeline.
414     *
415     * Logs a warning if the function has not been registered.
416     *
417     * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline.
418     * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline.
419     */
420    lunr.Pipeline.prototype.after = function (existingFn, newFn) {
421      lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
422    
423      var pos = this._stack.indexOf(existingFn)
424      if (pos == -1) {
425        throw new Error('Cannot find existingFn')
426      }
427    
428      pos = pos + 1
429      this._stack.splice(pos, 0, newFn)
430    }
431    
432    /**
433     * Adds a single function before a function that already exists in the
434     * pipeline.
435     *
436     * Logs a warning if the function has not been registered.
437     *
438     * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline.
439     * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline.
440     */
441    lunr.Pipeline.prototype.before = function (existingFn, newFn) {
442      lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
443    
444      var pos = this._stack.indexOf(existingFn)
445      if (pos == -1) {
446        throw new Error('Cannot find existingFn')
447      }
448    
449      this._stack.splice(pos, 0, newFn)
450    }
451    
452    /**
453     * Removes a function from the pipeline.
454     *
455     * @param {lunr.PipelineFunction} fn The function to remove from the pipeline.
456     */
457    lunr.Pipeline.prototype.remove = function (fn) {
458      var pos = this._stack.indexOf(fn)
459      if (pos == -1) {
460        return
461      }
462    
463      this._stack.splice(pos, 1)
464    }
465    
466    /**
467     * Runs the current list of functions that make up the pipeline against the
468     * passed tokens.
469     *
470     * @param {Array} tokens The tokens to run through the pipeline.
471     * @returns {Array}
472     */
473    lunr.Pipeline.prototype.run = function (tokens) {
474      var stackLength = this._stack.length
475    
476      for (var i = 0; i < stackLength; i++) {
477        var fn = this._stack[i]
478    
479        tokens = tokens.reduce(function (memo, token, j) {
480          var result = fn(token, j, tokens)
481    
482          if (result === void 0 || result === '') return memo
483    
484          return memo.concat(result)
485        }, [])
486      }
487    
488      return tokens
489    }
490    
491    /**
492     * Convenience method for passing a string through a pipeline and getting
493     * strings out. This method takes care of wrapping the passed string in a
494     * token and mapping the resulting tokens back to strings.
495     *
496     * @param {string} str - The string to pass through the pipeline.
497     * @returns {string[]}
498     */
499    lunr.Pipeline.prototype.runString = function (str) {
500      var token = new lunr.Token (str)
501    
502      return this.run([token]).map(function (t) {
503        return t.toString()
504      })
505    }
506    
507    /**
508     * Resets the pipeline by removing any existing processors.
509     *
510     */
511    lunr.Pipeline.prototype.reset = function () {
512      this._stack = []
513    }
514    
515    /**
516     * Returns a representation of the pipeline ready for serialisation.
517     *
518     * Logs a warning if the function has not been registered.
519     *
520     * @returns {Array}
521     */
522    lunr.Pipeline.prototype.toJSON = function () {
523      return this._stack.map(function (fn) {
524        lunr.Pipeline.warnIfFunctionNotRegistered(fn)
525    
526        return fn.label
527      })
528    }
529    /*!
530     * lunr.Vector
531     * Copyright (C) 2017 Oliver Nightingale
532     */
533    
534    /**
535     * A vector is used to construct the vector space of documents and queries. These
536     * vectors support operations to determine the similarity between two documents or
537     * a document and a query.
538     *
539     * Normally no parameters are required for initializing a vector, but in the case of
540     * loading a previously dumped vector the raw elements can be provided to the constructor.
541     *
542     * For performance reasons vectors are implemented with a flat array, where an elements
543     * index is immediately followed by its value. E.g. [index, value, index, value]. This
544     * allows the underlying array to be as sparse as possible and still offer decent
545     * performance when being used for vector calculations.
546     *
547     * @constructor
548     * @param {Number[]} [elements] - The flat list of element index and element value pairs.
549     */
550    lunr.Vector = function (elements) {
551      this._magnitude = 0
552      this.elements = elements || []
553    }
554    
555    
556    /**
557     * Calculates the position within the vector to insert a given index.
558     *
559     * This is used internally by insert and upsert. If there are duplicate indexes then
560     * the position is returned as if the value for that index were to be updated, but it
561     * is the callers responsibility to check whether there is a duplicate at that index
562     *
563     * @param {Number} insertIdx - The index at which the element should be inserted.
564     * @returns {Number}
565     */
566    lunr.Vector.prototype.positionForIndex = function (index) {
567      // For an empty vector the tuple can be inserted at the beginning
568      if (this.elements.length == 0) {
569        return 0
570      }
571    
572      var start = 0,
573          end = this.elements.length / 2,
vendor: 10,595 bytes, lines 574-953
574          sliceLength = end - start,
575          pivotPoint = Math.floor(sliceLength / 2),
576          pivotIndex = this.elements[pivotPoint * 2]
577    
578      while (sliceLength > 1) {
579        if (pivotIndex < index) {
580          start = pivotPoint
581        }
582    
583        if (pivotIndex > index) {
584          end = pivotPoint
585        }
586    
587        if (pivotIndex == index) {
588          break
589        }
590    
591        sliceLength = end - start
592        pivotPoint = start + Math.floor(sliceLength / 2)
593        pivotIndex = this.elements[pivotPoint * 2]
594      }
595    
596      if (pivotIndex == index) {
597        return pivotPoint * 2
598      }
599    
600      if (pivotIndex > index) {
601        return pivotPoint * 2
602      }
603    
604      if (pivotIndex < index) {
605        return (pivotPoint + 1) * 2
606      }
607    }
608    
609    /**
610     * Inserts an element at an index within the vector.
611     *
612     * Does not allow duplicates, will throw an error if there is already an entry
613     * for this index.
614     *
615     * @param {Number} insertIdx - The index at which the element should be inserted.
616     * @param {Number} val - The value to be inserted into the vector.
617     */
618    lunr.Vector.prototype.insert = function (insertIdx, val) {
619      this.upsert(insertIdx, val, function () {
620        throw "duplicate index"
621      })
622    }
623    
624    /**
625     * Inserts or updates an existing index within the vector.
626     *
627     * @param {Number} insertIdx - The index at which the element should be inserted.
628     * @param {Number} val - The value to be inserted into the vector.
629     * @param {function} fn - A function that is called for updates, the existing value and the
630     * requested value are passed as arguments
631     */
632    lunr.Vector.prototype.upsert = function (insertIdx, val, fn) {
633      this._magnitude = 0
634      var position = this.positionForIndex(insertIdx)
635    
636      if (this.elements[position] == insertIdx) {
637        this.elements[position + 1] = fn(this.elements[position + 1], val)
638      } else {
639        this.elements.splice(position, 0, insertIdx, val)
640      }
641    }
642    
643    /**
644     * Calculates the magnitude of this vector.
645     *
646     * @returns {Number}
647     */
648    lunr.Vector.prototype.magnitude = function () {
649      if (this._magnitude) return this._magnitude
650    
651      var sumOfSquares = 0,
652          elementsLength = this.elements.length
653    
654      for (var i = 1; i < elementsLength; i += 2) {
655        var val = this.elements[i]
656        sumOfSquares += val * val
657      }
658    
659      return this._magnitude = Math.sqrt(sumOfSquares)
660    }
661    
662    /**
663     * Calculates the dot product of this vector and another vector.
664     *
665     * @param {lunr.Vector} otherVector - The vector to compute the dot product with.
666     * @returns {Number}
667     */
668    lunr.Vector.prototype.dot = function (otherVector) {
669      var dotProduct = 0,
670          a = this.elements, b = otherVector.elements,
671          aLen = a.length, bLen = b.length,
672          aVal = 0, bVal = 0,
673          i = 0, j = 0
674    
675      while (i < aLen && j < bLen) {
676        aVal = a[i], bVal = b[j]
677        if (aVal < bVal) {
678          i += 2
679        } else if (aVal > bVal) {
680          j += 2
681        } else if (aVal == bVal) {
682          dotProduct += a[i + 1] * b[j + 1]
683          i += 2
684          j += 2
685        }
686      }
687    
688      return dotProduct
689    }
690    
691    /**
692     * Calculates the cosine similarity between this vector and another
693     * vector.
694     *
695     * @param {lunr.Vector} otherVector - The other vector to calculate the
696     * similarity with.
697     * @returns {Number}
698     */
699    lunr.Vector.prototype.similarity = function (otherVector) {
700      return this.dot(otherVector) / (this.magnitude() * otherVector.magnitude())
701    }
702    
703    /**
704     * Converts the vector to an array of the elements within the vector.
705     *
706     * @returns {Number[]}
707     */
708    lunr.Vector.prototype.toArray = function () {
709      var output = new Array (this.elements.length / 2)
710    
711      for (var i = 1, j = 0; i < this.elements.length; i += 2, j++) {
712        output[j] = this.elements[i]
713      }
714    
715      return output
716    }
717    
718    /**
719     * A JSON serializable representation of the vector.
720     *
721     * @returns {Number[]}
722     */
723    lunr.Vector.prototype.toJSON = function () {
724      return this.elements
725    }
726    /* eslint-disable */
727    /*!
728     * lunr.stemmer
729     * Copyright (C) 2017 Oliver Nightingale
730     * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
731     */
732    
733    /**
734     * lunr.stemmer is an english language stemmer, this is a JavaScript
735     * implementation of the PorterStemmer taken from http://tartarus.org/~martin
736     *
737     * @static
738     * @implements {lunr.PipelineFunction}
739     * @param {lunr.Token} token - The string to stem
740     * @returns {lunr.Token}
741     * @see {@link lunr.Pipeline}
742     */
743    lunr.stemmer = (function(){
744      var step2list = {
745          "ational" : "ate",
746          "tional" : "tion",
747          "enci" : "ence",
748          "anci" : "ance",
749          "izer" : "ize",
750          "bli" : "ble",
751          "alli" : "al",
752          "entli" : "ent",
753          "eli" : "e",
754          "ousli" : "ous",
755          "ization" : "ize",
756          "ation" : "ate",
757          "ator" : "ate",
758          "alism" : "al",
759          "iveness" : "ive",
760          "fulness" : "ful",
761          "ousness" : "ous",
762          "aliti" : "al",
763          "iviti" : "ive",
764          "biliti" : "ble",
765          "logi" : "log"
766        },
767    
768        step3list = {
769          "icate" : "ic",
770          "ative" : "",
771          "alize" : "al",
772          "iciti" : "ic",
773          "ical" : "ic",
774          "ful" : "",
775          "ness" : ""
776        },
777    
778        c = "[^aeiou]",          // consonant
779        v = "[aeiouy]",          // vowel
780        C = c + "[^aeiouy]*",    // consonant sequence
781        V = v + "[aeiou]*",      // vowel sequence
782    
783        mgr0 = "^(" + C + ")?" + V + C,               // [C]VC... is m>0
784        meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$",  // [C]VC[V] is m=1
785        mgr1 = "^(" + C + ")?" + V + C + V + C,       // [C]VCVC... is m>1
786        s_v = "^(" + C + ")?" + v;                   // vowel in stem
787    
788      var re_mgr0 = new RegExp(mgr0);
789      var re_mgr1 = new RegExp(mgr1);
790      var re_meq1 = new RegExp(meq1);
791      var re_s_v = new RegExp(s_v);
792    
793      var re_1a = /^(.+?)(ss|i)es$/;
794      var re2_1a = /^(.+?)([^s])s$/;
795      var re_1b = /^(.+?)eed$/;
796      var re2_1b = /^(.+?)(ed|ing)$/;
797      var re_1b_2 = /.$/;
798      var re2_1b_2 = /(at|bl|iz)$/;
799      var re3_1b_2 = new RegExp("([^aeiouylsz])\\1$");
800      var re4_1b_2 = new RegExp("^" + C + v + "[^aeiouwxy]$");
801    
802      var re_1c = /^(.+?[^aeiou])y$/;
803      var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
804    
805      var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
806    
807      var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
808      var re2_4 = /^(.+?)(s|t)(ion)$/;
809    
810      var re_5 = /^(.+?)e$/;
811      var re_5_1 = /ll$/;
812      var re3_5 = new RegExp("^" + C + v + "[^aeiouwxy]$");
813    
814      var porterStemmer = function porterStemmer(w) {
815        var stem,
816          suffix,
817          firstch,
818          re,
819          re2,
820          re3,
821          re4;
822    
823        if (w.length < 3) { return w; }
824    
825        firstch = w.substr(0,1);
826        if (firstch == "y") {
827          w = firstch.toUpperCase() + w.substr(1);
828        }
829    
830        // Step 1a
831        re = re_1a
832        re2 = re2_1a;
833    
834        if (re.test(w)) { w = w.replace(re,"$1$2"); }
835        else if (re2.test(w)) { w = w.replace(re2,"$1$2"); }
836    
837        // Step 1b
838        re = re_1b;
839        re2 = re2_1b;
840        if (re.test(w)) {
841          var fp = re.exec(w);
842          re = re_mgr0;
843          if (re.test(fp[1])) {
844            re = re_1b_2;
845            w = w.replace(re,"");
846          }
847        } else if (re2.test(w)) {
848          var fp = re2.exec(w);
849          stem = fp[1];
850          re2 = re_s_v;
851          if (re2.test(stem)) {
852            w = stem;
853            re2 = re2_1b_2;
854            re3 = re3_1b_2;
855            re4 = re4_1b_2;
856            if (re2.test(w)) { w = w + "e"; }
857            else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,""); }
858            else if (re4.test(w)) { w = w + "e"; }
859          }
860        }
861    
862        // 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)
863        re = re_1c;
864        if (re.test(w)) {
865          var fp = re.exec(w);
866          stem = fp[1];
867          w = stem + "i";
868        }
869    
870        // Step 2
871        re = re_2;
872        if (re.test(w)) {
873          var fp = re.exec(w);
874          stem = fp[1];
875          suffix = fp[2];
876          re = re_mgr0;
877          if (re.test(stem)) {
878            w = stem + step2list[suffix];
879          }
880        }
881    
882        // Step 3
883        re = re_3;
884        if (re.test(w)) {
885          var fp = re.exec(w);
886          stem = fp[1];
887          suffix = fp[2];
888          re = re_mgr0;
889          if (re.test(stem)) {
890            w = stem + step3list[suffix];
891          }
892        }
893    
894        // Step 4
895        re = re_4;
896        re2 = re2_4;
897        if (re.test(w)) {
898          var fp = re.exec(w);
899          stem = fp[1];
900          re = re_mgr1;
901          if (re.test(stem)) {
902            w = stem;
903          }
904        } else if (re2.test(w)) {
905          var fp = re2.exec(w);
906          stem = fp[1] + fp[2];
907          re2 = re_mgr1;
908          if (re2.test(stem)) {
909            w = stem;
910          }
911        }
912    
913        // Step 5
914        re = re_5;
915        if (re.test(w)) {
916          var fp = re.exec(w);
917          stem = fp[1];
918          re = re_mgr1;
919          re2 = re_meq1;
920          re3 = re3_5;
921          if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {
922            w = stem;
923          }
924        }
925    
926        re = re_5_1;
927        re2 = re_mgr1;
928        if (re.test(w) && re2.test(w)) {
929          re = re_1b_2;
930          w = w.replace(re,"");
931        }
932    
933        // and turn initial Y back to y
934    
935        if (firstch == "y") {
936          w = firstch.toLowerCase() + w.substr(1);
937        }
938    
939        return w;
940      };
941    
942      return function (token) {
943        return token.update(porterStemmer);
944      }
945    })();
946    
947    lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer')
948    /*!
949     * lunr.stopWordFilter
950     * Copyright (C) 2017 Oliver Nightingale
951     */
952    
953    /**
954     * lunr.generateStopWordFilter builds a stopWordFilter function from the provided
955     * list of stop words.
956     *
957     * The built in lunr.stopWordFilter is built using this generator and can be used
958     * to generate custom stopWordFilters for applications or non English languages.
959     *
960     * @param {Array} token The token to pass through the filter
961     * @returns {lunr.PipelineFunction}
962     * @see lunr.Pipeline
963     * @see lunr.stopWordFilter
964     */
965    lunr.generateStopWordFilter = function (stopWords) {
966      var words = stopWords.reduce(function (memo, stopWord) {
967        memo[stopWord] = stopWord
968        return memo
969      }, {})
970    
971      return function (token) {
972        if (token && words[token.toString()] !== token.toString()) return token
973      }
974    }
975    
976    /**
977     * lunr.stopWordFilter is an English language stop word list filter, any words
978     * contained in the list will not be passed through the filter.
979     *
980     * This is intended to be used in the Pipeline. If the token does not pass the
981     * filter then undefined will be returned.
982     *
983     * @implements {lunr.PipelineFunction}
984     * @params {lunr.Token} token - A token to check for being a stop word.
985     * @returns {lunr.Token}
986     * @see {@link lunr.Pipeline}
987     */
988    lunr.stopWordFilter = lunr.generateStopWordFilter([
989      'a',
990      'able',
991      'about',
992      'across',
993      'after',
994      'all',
995      'almost',
996      'also',
997      'am',
998      'among',
999      'an',
1000      'and',
1001      'any',
1002      'are',
1003      'as',
1004      'at',
1005      'be',
1006      'because',
1007      'been',
1008      'but',
1009      'by',
1010      'can',
1011      'cannot',
1012      'could',
1013      'dear',
1014      'did',
1015      'do',
1016      'does',
1017      'either',
1018      'else',
1019      'ever',
1020      'every',
1021      'for',
1022      'from',
1023      'get',
1024      'got',
1025      'had',
1026      'has',
1027      'have',
1028      'he',
1029      'her',
1030      'hers',
1031      'him',
1032      'his',
1033      'how',
1034      'however',
1035      'i',
1036      'if',
1037      'in',
1038      'into',
1039      'is',
1040      'it',
1041      'its',
1042      'just',
1043      'least',
1044      'let',
1045      'like',
1046      'likely',
1047      'may',
1048      'me',
1049      'might',
1050      'most',
1051      'must',
1052      'my',
1053      'neither',
1054      'no',
1055      'nor',
1056      'not',
1057      'of',
1058      'off',
1059      'often',
1060      'on',
1061      'only',
1062      'or',
1063      'other',
1064      'our',
1065      'own',
1066      'rather',
1067      'said',
1068      'say',
1069      'says',
1070      'she',
1071      'should',
1072      'since',
1073      'so',
1074      'some',
1075      'than',
1076      'that',
1077      'the',
1078      'their',
1079      'them',
1080      'then',
1081      'there',
1082      'these',
1083      'they',
1084      'this',
1085      'tis',
1086      'to',
1087      'too',
1088      'twas',
1089      'us',
1090      'wants',
1091      'was',
1092      'we',
1093      'were',
1094      'what',
1095      'when',
1096      'where',
1097      'which',
1098      'while',
1099      'who',
1100      'whom',
1101      'why',
1102      'will',
1103      'with',
1104      'would',
1105      'yet',
1106      'you',
1107      'your'
1108    ])
1109    
1110    lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter')
1111    /*!
1112     * lunr.trimmer
1113     * Copyright (C) 2017 Oliver Nightingale
1114     */
1115    
1116    /**
1117     * lunr.trimmer is a pipeline function for trimming non word
1118     * characters from the beginning and end of tokens before they
1119     * enter the index.
1120     *
1121     * This implementation may not work correctly for non latin
1122     * characters and should either be removed or adapted for use
1123     * with languages with non-latin characters.
1124     *
1125     * @static
1126     * @implements {lunr.PipelineFunction}
1127     * @param {lunr.Token} token The token to pass through the filter
1128     * @returns {lunr.Token}
1129     * @see lunr.Pipeline
1130     */
1131    lunr.trimmer = function (token) {
1132      return token.update(function (s) {
1133        return s.replace(/^\W+/, '').replace(/\W+$/, '')
1134      })
1135    }
1136    
1137    lunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer')
1138    /*!
1139     * lunr.TokenSet
1140     * Copyright (C) 2017 Oliver Nightingale
1141     */
1142    
1143    /**
1144     * A token set is used to store the unique list of all tokens
1145     * within an index. Token sets are also used to represent an
1146     * incoming query to the index, this query token set and index
1147     * token set are then intersected to find which tokens to look
1148     * up in the inverted index.
1149     *
1150     * A token set can hold multiple tokens, as in the case of the
1151     * index token set, or it can hold a single token as in the
1152     * case of a simple query token set.
1153     *
1154     * Additionally token sets are used to perform wildcard matching.
1155     * Leading, contained and trailing wildcards are supported, and
1156     * from this edit distance matching can also be provided.
1157     *
1158     * Token sets are implemented as a minimal finite state automata,
1159     * where both common prefixes and suffixes are shared between tokens.
1160     * This helps to reduce the space used for storing the token set.
1161     *
1162     * @constructor
1163     */
1164    lunr.TokenSet = function () {
1165      this.final = false
1166      this.edges = {}
1167      this.id = lunr.TokenSet._nextId
1168      lunr.TokenSet._nextId += 1
1169    }
1170    
1171    /**
1172     * Keeps track of the next, auto increment, identifier to assign
1173     * to a new tokenSet.
1174     *
1175     * TokenSets require a unique identifier to be correctly minimised.
1176     *
1177     * @private
1178     */
1179    lunr.TokenSet._nextId = 1
1180    
1181    /**
1182     * Creates a TokenSet instance from the given sorted array of words.
1183     *
1184     * @param {String[]}
1184 arr - A sorted array of strings to create the set from.
1185     * @returns {lunr.TokenSet}
1186     * @throws Will throw an error if the input array is not sorted.
1187     */
1188    lunr.TokenSet.fromArray = function (arr) {
1189      var builder = new lunr.TokenSet.Builder
1190    
1191      for (var i = 0, len = arr.length; i < len; i++) {
1192        builder.insert(arr[i])
1193      }
1194    
1195      builder.finish()
1196      return builder.root
1197    }
1198    
1199    /**
1200     * Creates a token set from a query clause.
1201     *
1202     * @private
1203     * @param {Object} clause - A single clause from lunr.Query.
1204     * @param {string} clause.term - The query clause term.
1205     * @param {number} [clause.editDistance] - The optional edit distance for the term.
1206     * @returns {lunr.TokenSet}
1207     */
1208    lunr.TokenSet.fromClause = function (clause) {
1209      if ('editDistance' in clause) {
1210        return lunr.TokenSet.fromFuzzyString(clause.term, clause.editDistance)
1211      } else {
1212        return lunr.TokenSet.fromString(clause.term)
1213      }
1214    }
1215    
1216    /**
1217     * Creates a token set representing a single string with a specified
1218     * edit distance.
1219     *
1220     * Insertions, deletions, substitutions and transpositions are each
1221     * treated as an edit distance of 1.
1222     *
1223     * Increasing the allowed edit distance will have a dramatic impact
1224     * on the performance of both creating and intersecting these TokenSets.
1225     * It is advised to keep the edit distance less than 3.
1226     *
1227     * @param {string} str - The string to create the token set from.
1228     * @param {number} editDistance - The allowed edit distance to match.
1229     * @returns {lunr.Vector}
1230     */
1231    lunr.TokenSet.fromFuzzyString = function (str, editDistance) {
1232      var root = new lunr.TokenSet
1233    
1234      var stack = [{
1235        node: root,
1236        editsRemaining: editDistance,
1237        str: str
1238      }]
1239    
1240      while (stack.length) {
1241        var frame = stack.pop()
1242    
1243        // no edit
1244        if (frame.str.length > 0) {
1245          var char = frame.str.charAt(0),
1246              noEditNode
1247    
1248          if (char in frame.node.edges) {
1249            noEditNode = frame.node.edges[char]
1250          } else {
1251            noEditNode = new lunr.TokenSet
1252            frame.node.edges[char] = noEditNode
1253          }
1254    
1255          if (frame.str.length == 1) {
1256            noEditNode.final = true
1257          } else {
1258            stack.push({
1259              node: noEditNode,
1260              editsRemaining: frame.editsRemaining,
1261              str: frame.str.slice(1)
1262            })
1263          }
1264        }
1265    
1266        // deletion
1267        // can only do a deletion if we have enough edits remaining
1268        // and if there are characters left to delete in the string
1269        if (frame.editsRemaining > 0 && frame.str.length > 1) {
1270          var char = frame.str.charAt(1),
1271              deletionNode
1272    
1273          if (char in frame.node.edges) {
1274            deletionNode = frame.node.edges[char]
1275          } else {
1276            deletionNode = new lunr.TokenSet
1277            frame.node.edges[char] = deletionNode
1278          }
1279    
1280          if (frame.str.length <= 2) {
1281            deletionNode.final = true
1282          } else {
1283            stack.push({
1284              node: deletionNode,
1285              editsRemaining: frame.editsRemaining - 1,
1286              str: frame.str.slice(2)
1287            })
1288          }
1289        }
1290    
1291        // deletion
1292        // just removing the last character from the str
1293        if (frame.editsRemaining > 0 && frame.str.length == 1) {
1294          frame.node.final = true
1295        }
1296    
1297        // substitution
1298        // can only do a substitution if we have enough edits remaining
1299        // and if there are characters left to substitute
1300        if (frame.editsRemaining > 0 && frame.str.length >= 1) {
1301          if ("*" in frame.node.edges) {
1302            var substitutionNode = frame.node.edges["*"]
1303          } else {
1304            var substitutionNode = new lunr.TokenSet
1305            frame.node.edges["*"] = substitutionNode
1306          }
1307    
1308          if (frame.str.length == 1) {
1309            substitutionNode.final = true
1310          } else {
1311            stack.push({
1312              node: substitutionNode,
1313              editsRemaining: frame.editsRemaining - 1,
1314              str: frame.str.slice(1)
1315            })
1316          }
1317        }
1318    
1319        // insertion
1320        // can only do insertion if there are edits remaining
1321        if (frame.editsRemaining > 0) {
1322          if ("*" in frame.node.edges) {
1323            var insertionNode = frame.node.edges["*"]
1324          } else {
1325            var insertionNode = new lunr.TokenSet
1326            frame.node.edges["*"] = insertionNode
1327          }
1328    
1329          if (frame.str.length == 0) {
1330            insertionNode.final = true
1331          } else {
1332            stack.push({
1333              node: insertionNode,
1334              editsRemaining: frame.editsRemaining - 1,
1335              str: frame.str
1336            })
1337          }
1338        }
1339    
1340        // transposition
1341        // can only do a transposition if there are edits remaining
1342        // and there are enough characters to transpose
1343        if (frame.editsRemaining > 0 && frame.str.length > 1) {
1344          var charA = frame.str.charAt(0),
1345              charB = frame.str.charAt(1),
1346              transposeNode
1347    
1348          if (charB in frame.node.edges) {
1349            transposeNode = frame.node.edges[charB]
1350          } else {
1351            transposeNode = new lunr.TokenSet
1352            frame.node.edges[charB] = transposeNode
1353          }
1354    
1355          if (frame.str.length == 1) {
1356            transposeNode.final = true
1357          } else {
1358            stack.push({
1359              node: transposeNode,
1360              editsRemaining: frame.editsRemaining - 1,
1361              str: charA + frame.str.slice(2)
1362            })
1363          }
1364        }
1365      }
1366    
1367      return root
1368    }
1369    
1370    /**
1371     * Creates a TokenSet from a string.
1372     *
1373     * The string may contain one or more wildcard characters (*)
1374     * that will allow wildcard matching when intersecting with
1375     * another TokenSet.
1376     *
1377     * @param {string} str - The string to create a TokenSet from.
1378     * @returns {lunr.TokenSet}
1379     */
1380    lunr.TokenSet.fromString = function (str) {
1381      var node = new lunr.TokenSet,
1382          root = node,
1383          wildcardFound = false
1384    
1385      /*
1386       * Iterates through all characters within the passed string
1387       * appending a node for each character.
1388       *
1389       * As soon as a wildcard character is found then a self
1390       * referencing edge is introduced to continually match
1391       * any number of any characters.
1392       */
1393      for (var i = 0, len = str.length; i < len; i++) {
1394        var char = str[i],
1395            final = (i == len - 1)
1396    
1397        if (char == "*") {
1398          wildcardFound = true
1399          node.edges[char] = node
1400          node.final = final
1401    
1402        } else {
1403          var next = new lunr.TokenSet
1404          next.final = final
1405    
1406          node.edges[char] = next
1407          node = next
1408    
1409          // TODO: is this needed anymore?
1410          if (wildcardFound) {
1411            node.edges["*"] = root
1412          }
1413        }
1414      }
1415    
1416      return root
1417    }
1418    
1419    /**
vendor: 6,628 bytes, lines 1420-1647
1420     * Converts this TokenSet into an array of strings
1421     * contained within the TokenSet.
1422     *
1423     * @returns {string[]}
1424     */
1425    lunr.TokenSet.prototype.toArray = function () {
1426      var words = []
1427    
1428      var stack = [{
1429        prefix: "",
1430        node: this
1431      }]
1432    
1433      while (stack.length) {
1434        var frame = stack.pop(),
1435            edges = Object.keys(frame.node.edges),
1436            len = edges.length
1437    
1438        if (frame.node.final) {
1439          words.push(frame.prefix)
1440        }
1441    
1442        for (var i = 0; i < len; i++) {
1443          var edge = edges[i]
1444    
1445          stack.push({
1446            prefix: frame.prefix.concat(edge),
1447            node: frame.node.edges[edge]
1448          })
1449        }
1450      }
1451    
1452      return words
1453    }
1454    
1455    /**
1456     * Generates a string representation of a TokenSet.
1457     *
1458     * This is intended to allow TokenSets to be used as keys
1459     * in objects, largely to aid the construction and minimisation
1460     * of a TokenSet. As such it is not designed to be a human
1461     * friendly representation of the TokenSet.
1462     *
1463     * @returns {string}
1464     */
1465    lunr.TokenSet.prototype.toString = function () {
1466      // NOTE: Using Object.keys here as this.edges is very likely
1467      // to enter 'hash-mode' with many keys being added
1468      //
1469      // avoiding a for-in loop here as it leads to the function
1470      // being de-optimised (at least in V8). From some simple
1471      // benchmarks the performance is comparable, but allowing
1472      // V8 to optimize may mean easy performance wins in the future.
1473    
1474      if (this._str) {
1475        return this._str
1476      }
1477    
1478      var str = this.final ? '1' : '0',
1479          labels = Object.keys(this.edges).sort(),
1480          len = labels.length
1481    
1482      for (var i = 0; i < len; i++) {
1483        var label = labels[i],
1484            node = this.edges[label]
1485    
1486        str = str + label + node.id
1487      }
1488    
1489      return str
1490    }
1491    
1492    /**
1493     * Returns a new TokenSet that is the intersection of
1494     * this TokenSet and the passed TokenSet.
1495     *
1496     * This intersection will take into account any wildcards
1497     * contained within the TokenSet.
1498     *
1499     * @param {lunr.TokenSet} b - An other TokenSet to intersect with.
1500     * @returns {lunr.TokenSet}
1501     */
1502    lunr.TokenSet.prototype.intersect = function (b) {
1503      var output = new lunr.TokenSet,
1504          frame = undefined
1505    
1506      var stack = [{
1507        qNode: b,
1508        output: output,
1509        node: this
1510      }]
1511    
1512      while (stack.length) {
1513        frame = stack.pop()
1514    
1515        // NOTE: As with the #toString method, we are using
1516        // Object.keys and a for loop instead of a for-in loop
1517        // as both of these objects enter 'hash' mode, causing
1518        // the function to be de-optimised in V8
1519        var qEdges = Object.keys(frame.qNode.edges),
1520            qLen = qEdges.length,
1521            nEdges = Object.keys(frame.node.edges),
1522            nLen = nEdges.length
1523    
1524        for (var q = 0; q < qLen; q++) {
1525          var qEdge = qEdges[q]
1526    
1527          for (var n = 0; n < nLen; n++) {
1528            var nEdge = nEdges[n]
1529    
1530            if (nEdge == qEdge || qEdge == '*') {
1531              var node = frame.node.edges[nEdge],
1532                  qNode = frame.qNode.edges[qEdge],
1533                  final = node.final && qNode.final,
1534                  next = undefined
1535    
1536              if (nEdge in frame.output.edges) {
1537                // an edge already exists for this character
1538                // no need to create a new node, just set the finality
1539                // bit unless this node is already final
1540                next = frame.output.edges[nEdge]
1541                next.final = next.final || final
1542    
1543              } else {
1544                // no edge exists yet, must create one
1545                // set the finality bit and insert it
1546                // into the output
1547                next = new lunr.TokenSet
1548                next.final = final
1549                frame.output.edges[nEdge] = next
1550              }
1551    
1552              stack.push({
1553                qNode: qNode,
1554                output: next,
1555                node: node
1556              })
1557            }
1558          }
1559        }
1560      }
1561    
1562      return output
1563    }
1564    lunr.TokenSet.Builder = function () {
1565      this.previousWord = ""
1566      this.root = new lunr.TokenSet
1567      this.uncheckedNodes = []
1568      this.minimizedNodes = {}
1569    }
1570    
1571    lunr.TokenSet.Builder.prototype.insert = function (word) {
1572      var node,
1573          commonPrefix = 0
1574    
1575      if (word < this.previousWord) {
1576        throw new Error ("Out of order word insertion")
1577      }
1578    
1579      for (var i = 0; i < word.length && i < this.previousWord.length; i++) {
1580        if (word[i] != this.previousWord[i]) break
1581        commonPrefix++
1582      }
1583    
1584      this.minimize(commonPrefix)
1585    
1586      if (this.uncheckedNodes.length == 0) {
1587        node = this.root
1588      } else {
1589        node = this.uncheckedNodes[this.uncheckedNodes.length - 1].child
1590      }
1591    
1592      for (var i = commonPrefix; i < word.length; i++) {
1593        var nextNode = new lunr.TokenSet,
1594            char = word[i]
1595    
1596        node.edges[char] = nextNode
1597    
1598        this.uncheckedNodes.push({
1599          parent: node,
1600          char: char,
1601          child: nextNode
1602        })
1603    
1604        node = nextNode
1605      }
1606    
1607      node.final = true
1608      this.previousWord = word
1609    }
1610    
1611    lunr.TokenSet.Builder.prototype.finish = function () {
1612      this.minimize(0)
1613    }
1614    
1615    lunr.TokenSet.Builder.prototype.minimize = function (downTo) {
1616      for (var i = this.uncheckedNodes.length - 1; i >= downTo; i--) {
1617        var node = this.uncheckedNodes[i],
1618            childKey = node.child.toString()
1619    
1620        if (childKey in this.minimizedNodes) {
1621          node.parent.edges[node.char] = this.minimizedNodes[childKey]
1622        } else {
1623          // Cache the key for this node since
1624          // we know it can't change anymore
1625          node.child._str = childKey
1626    
1627          this.minimizedNodes[childKey] = node.child
1628        }
1629    
1630        this.uncheckedNodes.pop()
1631      }
1632    }
1633    /*!
1634     * lunr.Index
1635     * Copyright (C) 2017 Oliver Nightingale
1636     */
1637    
1638    /**
1639     * An index contains the built index of all documents and provides a query interface
1640     * to the index.
1641     *
1642     * Usually instances of lunr.Index will not be created using this constructor, instead
1643     * lunr.Builder should be used to construct new indexes, or lunr.Index.load should be
1644     * used to load previously built and serialized indexes.
1645     *
1646     * @constructor
1647     * @param {Object}
1647 attrs - The attributes of the built search index.
1648     * @param {Object} attrs.invertedIndex - An index of term/field to document reference.
1649     * @param {Object<string, lunr.Vector>} attrs.documentVectors - Document vectors keyed by document reference.
1650     * @param {lunr.TokenSet} attrs.tokenSet - An set of all corpus tokens.
1651     * @param {string[]} attrs.fields - The names of indexed document fields.
1652     * @param {lunr.Pipeline} attrs.pipeline - The pipeline to use for search terms.
1653     */
1654    lunr.Index = function (attrs) {
1655      this.invertedIndex = attrs.invertedIndex
1656      this.fieldVectors = attrs.fieldVectors
1657      this.tokenSet = attrs.tokenSet
1658      this.fields = attrs.fields
1659      this.pipeline = attrs.pipeline
1660    }
1661    
1662    /**
1663     * A result contains details of a document matching a search query.
1664     * @typedef {Object} lunr.Index~Result
1665     * @property {string} ref - The reference of the document this result represents.
1666     * @property {number} score - A number between 0 and 1 representing how similar this document is to the query.
1667     * @property {lunr.MatchData} matchData - Contains metadata about this match including which term(s) caused the match.
1668     */
1669    
1670    /**
1671     * Although lunr provides the ability to create queries using lunr.Query, it also provides a simple
1672     * query language which itself is parsed into an instance of lunr.Query.
1673     *
1674     * For programmatically building queries it is advised to directly use lunr.Query, the query language
1675     * is best used for human entered text rather than program generated text.
1676     *
1677     * At its simplest queries can just be a single term, e.g. `hello`, multiple terms are also supported
1678     * and will be combined with OR, e.g `hello world` will match documents that contain either 'hello'
1679     * or 'world', though those that contain both will rank higher in the results.
1680     *
1681     * Wildcards can be included in terms to match one or more unspecified characters, these wildcards can
1682     * be inserted anywhere within the term, and more than one wildcard can exist in a single term. Adding
1683     * wildcards will increase the number of documents that will be found but can also have a negative
1684     * impact on query performance, especially with wildcards at the beginning of a term.
1685     *
1686     * Terms can be restricted to specific fields, e.g. `title:hello`, only documents with the term
1687     * hello in the title field will match this query. Using a field not present in the index will lead
1688     * to an error being thrown.
1689     *
1690     * Modifiers can also be added to terms, lunr supports edit distance and boost modifiers on terms. A term
1691     * boost will make documents matching that term score higher, e.g. `foo^5`. Edit distance is also supported
1692     * to provide fuzzy matching, e.g. 'hello~2' will match documents with hello with an edit distance of 2.
1693     * Avoid large values for edit distance to improve query performance.
1694     *
1695     * To escape special characters the backslash character '\' can be used, this allows searches to include
1696     * characters that would normally be considered modifiers, e.g. `foo\~2` will search for a term "foo~2" instead
1697     * of attempting to apply a boost of 2 to the search term "foo".
1698     *
1699     * @typedef {string} lunr.Index~QueryString
1700     * @example <caption>Simple single term query</caption>
1701     * hello
1702     * @example <caption>Multiple term query</caption>
1703     * hello world
1704     * @example <caption>term scoped to a field</caption>
1705     * title:hello
1706     * @example <caption>term with a boost of 10</caption>
1707     * hello^10
1708     * @example <caption>term with an edit distance of 2</caption>
1709     * hello~2
1710     */
1711    
1712    /**
1713     * Performs a search against the index using lunr query syntax.
1714     *
1715     * Results will be returned sorted by their score, the most relevant results
1716     * will be returned first.
1717     *
1718     * For more programmatic querying use lunr.Index#query.
1719     *
1720     * @param {lunr.Index~QueryString} queryString - A string containing a lunr query.
1721     * @throws {lunr.QueryParseError} If the passed query string cannot be parsed.
1722     * @returns {lunr.Index~Result[]}
1723     */
1724    lunr.Index.prototype.search = function (queryString) {
1725      return this.query(function (query) {
1726        var parser = new lunr.QueryParser(queryString, query)
1727        parser.parse()
1728      })
1729    }
1730    
1731    /**
1732     * A query builder callback provides a query object to be used to express
1733     * the query to perform on the index.
1734     *
1735     * @callback lunr.Index~queryBuilder
1736     * @param {lunr.Query} query - The query object to build up.
1737     * @this lunr.Query
1738     */
1739    
1740    /**
1741     * Performs a query against the index using the yielded lunr.Query object.
1742     *
1743     * If performing programmatic queries against the index, this method is preferred
1744     * over lunr.Index#search so as to avoid the additional query parsing overhead.
1745     *
1746     * A query object is yielded to the supplied function which should be used to
1747     * express the query to be run against the index.
1748     *
1749     * Note that although this function takes a callback parameter it is _not_ an
1750     * asynchronous operation, the callback is just yielded a query object to be
1751     * customized.
1752     *
1753     * @param {lunr.Index~queryBuilder} fn - A function that is used to build the query.
1754     * @returns {lunr.Index~Result[]}
1755     */
1756    lunr.Index.prototype.query = function (fn) {
1757      // for each query clause
1758      // * process terms
1759      // * expand terms from token set
1760      // * find matching documents and metadata
1761      // * get document vectors
1762      // * score documents
1763    
1764      var query = new lunr.Query(this.fields),
1765          matchingFields = Object.create(null),
1766          queryVectors = Object.create(null),
1767          termFieldCache = Object.create(null)
1768    
1769      fn.call(query, query)
1770    
1771      for (var i = 0; i < query.clauses.length; i++) {
1772        /*
1773         * Unless the pipeline has been disabled for this term, which is
1774         * the case for terms with wildcards, we need to pass the clause
1775         * term through the search pipeline. A pipeline returns an array
1776         * of processed terms. Pipeline functions may expand the passed
1777         * term, which means we may end up performing multiple index lookups
1778         * for a single query term.
1779         */
1780        var clause = query.clauses[i],
1781            terms = null
1782    
1783        if (clause.usePipeline) {
1784          terms = this.pipeline.runString(clause.term)
1785        } else {
1786          terms = [clause.term]
1787        }
1788    
1789        for (var m = 0; m < terms.length; m++) {
1790          var term = terms[m]
1791    
1792          /*
1793           * Each term returned from the pipeline needs to use the same query
1794           * clause object, e.g. the same boost and or edit distance. The
1795           * simplest way to do this is to re-use the clause object but mutate
1796           * its term property.
1797           */
1798          clause.term = term
1799    
1800          /*
1801           * From the term in the clause we create a token set which will then
1802           * be used to intersect the indexes token set to get a list of terms
1803           * to lookup in the inverted index
1804           */
1805          var termTokenSet = lunr.TokenSet.fromClause(clause),
1806              expandedTerms = this.tokenSet.intersect(termTokenSet).toArray()
1807    
1808          for (var j = 0; j < expandedTerms.length; j++) {
1809            /*
1810             * For each term get the posting and termIndex, this is required for
1811             * building the query vector.
1812             */
1813            var expandedTerm = expandedTerms[j],
1814                posting = this.invertedIndex[expandedTerm],
1815                termIndex = posting._index
1816    
1817            for (var k = 0; k < clause.fields.length; k++) {
1818              /*
1819               * For each field that this query term is scoped by (by default
1820               * all fields are in scope) we need to get all the document refs
1821               * that have this term in that field.
1822               *
1823               * The posting is the entry in the invertedIndex for the matching
1824               * term from above.
1825               */
1826              var field = clause.fields[k],
1827                  fieldPosting = posting[field],
1828                  matchingDocumentRefs = Object.keys(fieldPosting),
1829                  termField = expandedTerm + "/" + field
1830    
1831              /*
1832               * To support field level boosts a query vector is created per
1833               * field. This vector is populated using the termIndex found for
1834               * the term and a unit value with the appropriate boost applied.
1835               *
1836               * If the query vector for this field does not exist yet it needs
1837               * to be created.
1838               */
1839              if (queryVectors[field] === undefined) {
1840                queryVectors[field] = new lunr.Vector
1841              }
1842    
1843              /*
1844               * Using upsert because there could already be an entry in the vector
1845               * for the term we are working with. In that case we just add the scores
1846               * together.
1847               */
1848              queryVectors[field].upsert(termIndex, 1 * clause.boost, function (a, b) { return a + b })
1849    
1850              /**
1851               * If we've already seen this term, field combo then we've already collected
1852               * the matching documents and metadata, no need to go through all that again
1853               */
1854              if (termFieldCache[termField]) {
1855                continue
1856              }
1857    
1858              for (var l = 0; l < matchingDocumentRefs.length; l++) {
1859                /*
1860                 * All metadata for this term/field/document triple
1861                 * are then extracted and collected into an instance
1862                 * of lunr.MatchData ready to be returned in the query
1863                 * results
1864                 */
1865                var matchingDocumentRef = matchingDocumentRefs[l],
1866                    matchingFieldRef = new lunr.FieldRef (matchingDocumentRef, field),
1867                    metadata = fieldPosting[matchingDocumentRef],
1868                    fieldMatch
1869    
1870                if ((fieldMatch = matchingFields[matchingFieldRef]) === undefined) {
1871                  matchingFields[matchingFieldRef] = new lunr.MatchData (expandedTerm, field, metadata)
1872                } else {
1873                  fieldMatch.add(expandedTerm, field, metadata)
1874                }
1875    
1876              }
1877    
1878              termFieldCache[termField] = true
1879            }
1880          }
1881        }
1882      }
1883    
1884      var matchingFieldRefs = Object.keys(matchingFields),
1885          results = [],
1886          matches = Object.create(null)
1887    
1888      for (var i = 0; i < matchingFieldRefs.length; i++) {
1889        /*
1890         * Currently we have document fields that match the query, but we
1891         * need to return documents. The matchData and scores are combined
1892         * from multiple fields belonging to the same document.
1893         *
1894         * Scores are calculated by field, using the query vectors created
1895         * above, and combined into a final document score using addition.
1896         */
1897        var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]),
vendor: 36,745 bytes, lines 1898-2977
1898            docRef = fieldRef.docRef,
1899            fieldVector = this.fieldVectors[fieldRef],
1900            score = queryVectors[fieldRef.fieldName].similarity(fieldVector),
1901            docMatch
1902    
1903        if ((docMatch = matches[docRef]) !== undefined) {
1904          docMatch.score += score
1905          docMatch.matchData.combine(matchingFields[fieldRef])
1906        } else {
1907          var match = {
1908            ref: docRef,
1909            score: score,
1910            matchData: matchingFields[fieldRef]
1911          }
1912          matches[docRef] = match
1913          results.push(match)
1914        }
1915      }
1916    
1917      /*
1918       * Sort the results objects by score, highest first.
1919       */
1920      return results.sort(function (a, b) {
1921        return b.score - a.score
1922      })
1923    }
1924    
1925    /**
1926     * Prepares the index for JSON serialization.
1927     *
1928     * The schema for this JSON blob will be described in a
1929     * separate JSON schema file.
1930     *
1931     * @returns {Object}
1932     */
1933    lunr.Index.prototype.toJSON = function () {
1934      var invertedIndex = Object.keys(this.invertedIndex)
1935        .sort()
1936        .map(function (term) {
1937          return [term, this.invertedIndex[term]]
1938        }, this)
1939    
1940      var fieldVectors = Object.keys(this.fieldVectors)
1941        .map(function (ref) {
1942          return [ref, this.fieldVectors[ref].toJSON()]
1943        }, this)
1944    
1945      return {
1946        version: lunr.version,
1947        fields: this.fields,
1948        fieldVectors: fieldVectors,
1949        invertedIndex: invertedIndex,
1950        pipeline: this.pipeline.toJSON()
1951      }
1952    }
1953    
1954    /**
1955     * Loads a previously serialized lunr.Index
1956     *
1957     * @param {Object} serializedIndex - A previously serialized lunr.Index
1958     * @returns {lunr.Index}
1959     */
1960    lunr.Index.load = function (serializedIndex) {
1961      var attrs = {},
1962          fieldVectors = {},
1963          serializedVectors = serializedIndex.fieldVectors,
1964          invertedIndex = {},
1965          serializedInvertedIndex = serializedIndex.invertedIndex,
1966          tokenSetBuilder = new lunr.TokenSet.Builder,
1967          pipeline = lunr.Pipeline.load(serializedIndex.pipeline)
1968    
1969      if (serializedIndex.version != lunr.version) {
1970        lunr.utils.warn("Version mismatch when loading serialised index. Current version of lunr '" + lunr.version + "' does not match serialized index '" + serializedIndex.version + "'")
1971      }
1972    
1973      for (var i = 0; i < serializedVectors.length; i++) {
1974        var tuple = serializedVectors[i],
1975            ref = tuple[0],
1976            elements = tuple[1]
1977    
1978        fieldVectors[ref] = new lunr.Vector(elements)
1979      }
1980    
1981      for (var i = 0; i < serializedInvertedIndex.length; i++) {
1982        var tuple = serializedInvertedIndex[i],
1983            term = tuple[0],
1984            posting = tuple[1]
1985    
1986        tokenSetBuilder.insert(term)
1987        invertedIndex[term] = posting
1988      }
1989    
1990      tokenSetBuilder.finish()
1991    
1992      attrs.fields = serializedIndex.fields
1993    
1994      attrs.fieldVectors = fieldVectors
1995      attrs.invertedIndex = invertedIndex
1996      attrs.tokenSet = tokenSetBuilder.root
1997      attrs.pipeline = pipeline
1998    
1999      return new lunr.Index(attrs)
2000    }
2001    /*!
2002     * lunr.Builder
2003     * Copyright (C) 2017 Oliver Nightingale
2004     */
2005    
2006    /**
2007     * lunr.Builder performs indexing on a set of documents and
2008     * returns instances of lunr.Index ready for querying.
2009     *
2010     * All configuration of the index is done via the builder, the
2011     * fields to index, the document reference, the text processing
2012     * pipeline and document scoring parameters are all set on the
2013     * builder before indexing.
2014     *
2015     * @constructor
2016     * @property {string} _ref - Internal reference to the document reference field.
2017     * @property {string[]} _fields - Internal reference to the document fields to index.
2018     * @property {object} invertedIndex - The inverted index maps terms to document fields.
2019     * @property {object} documentTermFrequencies - Keeps track of document term frequencies.
2020     * @property {object} documentLengths - Keeps track of the length of documents added to the index.
2021     * @property {lunr.tokenizer} tokenizer - Function for splitting strings into tokens for indexing.
2022     * @property {lunr.Pipeline} pipeline - The pipeline performs text processing on tokens before indexing.
2023     * @property {lunr.Pipeline} searchPipeline - A pipeline for processing search terms before querying the index.
2024     * @property {number} documentCount - Keeps track of the total number of documents indexed.
2025     * @property {number} _b - A parameter to control field length normalization, setting this to 0 disabled normalization, 1 fully normalizes field lengths, the default value is 0.75.
2026     * @property {number} _k1 - A parameter to control how quickly an increase in term frequency results in term frequency saturation, the default value is 1.2.
2027     * @property {number} termIndex - A counter incremented for each unique term, used to identify a terms position in the vector space.
2028     * @property {array} metadataWhitelist - A list of metadata keys that have been whitelisted for entry in the index.
2029     */
2030    lunr.Builder = function () {
2031      this._ref = "id"
2032      this._fields = []
2033      this.invertedIndex = Object.create(null)
2034      this.fieldTermFrequencies = {}
2035      this.fieldLengths = {}
2036      this.tokenizer = lunr.tokenizer
2037      this.pipeline = new lunr.Pipeline
2038      this.searchPipeline = new lunr.Pipeline
2039      this.documentCount = 0
2040      this._b = 0.75
2041      this._k1 = 1.2
2042      this.termIndex = 0
2043      this.metadataWhitelist = []
2044    }
2045    
2046    /**
2047     * Sets the document field used as the document reference. Every document must have this field.
2048     * The type of this field in the document should be a string, if it is not a string it will be
2049     * coerced into a string by calling toString.
2050     *
2051     * The default ref is 'id'.
2052     *
2053     * The ref should _not_ be changed during indexing, it should be set before any documents are
2054     * added to the index. Changing it during indexing can lead to inconsistent results.
2055     *
2056     * @param {string} ref - The name of the reference field in the document.
2057     */
2058    lunr.Builder.prototype.ref = function (ref) {
2059      this._ref = ref
2060    }
2061    
2062    /**
2063     * Adds a field to the list of document fields that will be indexed. Every document being
2064     * indexed should have this field. Null values for this field in indexed documents will
2065     * not cause errors but will limit the chance of that document being retrieved by searches.
2066     *
2067     * All fields should be added before adding documents to the index. Adding fields after
2068     * a document has been indexed will have no effect on already indexed documents.
2069     *
2070     * @param {string} field - The name of a field to index in all documents.
2071     */
2072    lunr.Builder.prototype.field = function (field) {
2073      this._fields.push(field)
2074    }
2075    
2076    /**
2077     * A parameter to tune the amount of field length normalisation that is applied when
2078     * calculating relevance scores. A value of 0 will completely disable any normalisation
2079     * and a value of 1 will fully normalise field lengths. The default is 0.75. Values of b
2080     * will be clamped to the range 0 - 1.
2081     *
2082     * @param {number} number - The value to set for this tuning parameter.
2083     */
2084    lunr.Builder.prototype.b = function (number) {
2085      if (number < 0) {
2086        this._b = 0
2087      } else if (number > 1) {
2088        this._b = 1
2089      } else {
2090        this._b = number
2091      }
2092    }
2093    
2094    /**
2095     * A parameter that controls the speed at which a rise in term frequency results in term
2096     * frequency saturation. The default value is 1.2. Setting this to a higher value will give
2097     * slower saturation levels, a lower value will result in quicker saturation.
2098     *
2099     * @param {number} number - The value to set for this tuning parameter.
2100     */
2101    lunr.Builder.prototype.k1 = function (number) {
2102      this._k1 = number
2103    }
2104    
2105    /**
2106     * Adds a document to the index.
2107     *
2108     * Before adding fields to the index the index should have been fully setup, with the document
2109     * ref and all fields to index already having been specified.
2110     *
2111     * The document must have a field name as specified by the ref (by default this is 'id') and
2112     * it should have all fields defined for indexing, though null or undefined values will not
2113     * cause errors.
2114     *
2115     * @param {object} doc - The document to add to the index.
2116     */
2117    lunr.Builder.prototype.add = function (doc) {
2118      var docRef = doc[this._ref]
2119    
2120      this.documentCount += 1
2121    
2122      for (var i = 0; i < this._fields.length; i++) {
2123        var fieldName = this._fields[i],
2124            field = doc[fieldName],
2125            tokens = this.tokenizer(field),
2126            terms = this.pipeline.run(tokens),
2127            fieldRef = new lunr.FieldRef (docRef, fieldName),
2128            fieldTerms = Object.create(null)
2129    
2130        this.fieldTermFrequencies[fieldRef] = fieldTerms
2131        this.fieldLengths[fieldRef] = 0
2132    
2133        // store the length of this field for this document
2134        this.fieldLengths[fieldRef] += terms.length
2135    
2136        // calculate term frequencies for this field
2137        for (var j = 0; j < terms.length; j++) {
2138          var term = terms[j]
2139    
2140          if (fieldTerms[term] == undefined) {
2141            fieldTerms[term] = 0
2142          }
2143    
2144          fieldTerms[term] += 1
2145    
2146          // add to inverted index
2147          // create an initial posting if one doesn't exist
2148          if (this.invertedIndex[term] == undefined) {
2149            var posting = Object.create(null)
2150            posting["_index"] = this.termIndex
2151            this.termIndex += 1
2152    
2153            for (var k = 0; k < this._fields.length; k++) {
2154              posting[this._fields[k]] = Object.create(null)
2155            }
2156    
2157            this.invertedIndex[term] = posting
2158          }
2159    
2160          // add an entry for this term/fieldName/docRef to the invertedIndex
2161          if (this.invertedIndex[term][fieldName][docRef] == undefined) {
2162            this.invertedIndex[term][fieldName][docRef] = Object.create(null)
2163          }
2164    
2165          // store all whitelisted metadata about this token in the
2166          // inverted index
2167          for (var l = 0; l < this.metadataWhitelist.length; l++) {
2168            var metadataKey = this.metadataWhitelist[l],
2169                metadata = term.metadata[metadataKey]
2170    
2171            if (this.invertedIndex[term][fieldName][docRef][metadataKey] == undefined) {
2172              this.invertedIndex[term][fieldName][docRef][metadataKey] = []
2173            }
2174    
2175            this.invertedIndex[term][fieldName][docRef][metadataKey].push(metadata)
2176          }
2177        }
2178    
2179      }
2180    }
2181    
2182    /**
2183     * Calculates the average document length for this index
2184     *
2185     * @private
2186     */
2187    lunr.Builder.prototype.calculateAverageFieldLengths = function () {
2188    
2189      var fieldRefs = Object.keys(this.fieldLengths),
2190          numberOfFields = fieldRefs.length,
2191          accumulator = {},
2192          documentsWithField = {}
2193    
2194      for (var i = 0; i < numberOfFields; i++) {
2195        var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]),
2196            field = fieldRef.fieldName
2197    
2198        documentsWithField[field] || (documentsWithField[field] = 0)
2199        documentsWithField[field] += 1
2200    
2201        accumulator[field] || (accumulator[field] = 0)
2202        accumulator[field] += this.fieldLengths[fieldRef]
2203      }
2204    
2205      for (var i = 0; i < this._fields.length; i++) {
2206        var field = this._fields[i]
2207        accumulator[field] = accumulator[field] / documentsWithField[field]
2208      }
2209    
2210      this.averageFieldLength = accumulator
2211    }
2212    
2213    /**
2214     * Builds a vector space model of every document using lunr.Vector
2215     *
2216     * @private
2217     */
2218    lunr.Builder.prototype.createFieldVectors = function () {
2219      var fieldVectors = {},
2220          fieldRefs = Object.keys(this.fieldTermFrequencies),
2221          fieldRefsLength = fieldRefs.length,
2222          termIdfCache = Object.create(null)
2223    
2224      for (var i = 0; i < fieldRefsLength; i++) {
2225        var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]),
2226            field = fieldRef.fieldName,
2227            fieldLength = this.fieldLengths[fieldRef],
2228            fieldVector = new lunr.Vector,
2229            termFrequencies = this.fieldTermFrequencies[fieldRef],
2230            terms = Object.keys(termFrequencies),
2231            termsLength = terms.length
2232    
2233        for (var j = 0; j < termsLength; j++) {
2234          var term = terms[j],
2235              tf = termFrequencies[term],
2236              termIndex = this.invertedIndex[term]._index,
2237              idf, score, scoreWithPrecision
2238    
2239          if (termIdfCache[term] === undefined) {
2240            idf = lunr.idf(this.invertedIndex[term], this.documentCount)
2241            termIdfCache[term] = idf
2242          } else {
2243            idf = termIdfCache[term]
2244          }
2245    
2246          score = idf * ((this._k1 + 1) * tf) / (this._k1 * (1 - this._b + this._b * (fieldLength / this.averageFieldLength[field])) + tf)
2247          scoreWithPrecision = Math.round(score * 1000) / 1000
2248          // Converts 1.23456789 to 1.234.
2249          // Reducing the precision so that the vectors take up less
2250          // space when serialised. Doing it now so that they behave
2251          // the same before and after serialisation. Also, this is
2252          // the fastest approach to reducing a number's precision in
2253          // JavaScript.
2254    
2255          fieldVector.insert(termIndex, scoreWithPrecision)
2256        }
2257    
2258        fieldVectors[fieldRef] = fieldVector
2259      }
2260    
2261      this.fieldVectors = fieldVectors
2262    }
2263    
2264    /**
2265     * Creates a token set of all tokens in the index using lunr.TokenSet
2266     *
2267     * @private
2268     */
2269    lunr.Builder.prototype.createTokenSet = function () {
2270      this.tokenSet = lunr.TokenSet.fromArray(
2271        Object.keys(this.invertedIndex).sort()
2272      )
2273    }
2274    
2275    /**
2276     * Builds the index, creating an instance of lunr.Index.
2277     *
2278     * This completes the indexing process and should only be called
2279     * once all documents have been added to the index.
2280     *
2281     * @returns {lunr.Index}
2282     */
2283    lunr.Builder.prototype.build = function () {
2284      this.calculateAverageFieldLengths()
2285      this.createFieldVectors()
2286      this.createTokenSet()
2287    
2288      return new lunr.Index({
2289        invertedIndex: this.invertedIndex,
2290        fieldVectors: this.fieldVectors,
2291        tokenSet: this.tokenSet,
2292        fields: this._fields,
2293        pipeline: this.searchPipeline
2294      })
2295    }
2296    
2297    /**
2298     * Applies a plugin to the index builder.
2299     *
2300     * A plugin is a function that is called with the index builder as its context.
2301     * Plugins can be used to customise or extend the behaviour of the index
2302     * in some way. A plugin is just a function, that encapsulated the custom
2303     * behaviour that should be applied when building the index.
2304     *
2305     * The plugin function will be called with the index builder as its argument, additional
2306     * arguments can also be passed when calling use. The function will be called
2307     * with the index builder as its context.
2308     *
2309     * @param {Function} plugin The plugin to apply.
2310     */
2311    lunr.Builder.prototype.use = function (fn) {
2312      var args = Array.prototype.slice.call(arguments, 1)
2313      args.unshift(this)
2314      fn.apply(this, args)
2315    }
2316    /**
2317     * Contains and collects metadata about a matching document.
2318     * A single instance of lunr.MatchData is returned as part of every
2319     * lunr.Index~Result.
2320     *
2321     * @constructor
2322     * @param {string} term - The term this match data is associated with
2323     * @param {string} field - The field in which the term was found
2324     * @param {object} metadata - The metadata recorded about this term in this field
2325     * @property {object} metadata - A cloned collection of metadata associated with this document.
2326     * @see {@link lunr.Index~Result}
2327     */
2328    lunr.MatchData = function (term, field, metadata) {
2329      var clonedMetadata = Object.create(null),
2330          metadataKeys = Object.keys(metadata)
2331    
2332      // Cloning the metadata to prevent the original
2333      // being mutated during match data combination.
2334      // Metadata is kept in an array within the inverted
2335      // index so cloning the data can be done with
2336      // Array#slice
2337      for (var i = 0; i < metadataKeys.length; i++) {
2338        var key = metadataKeys[i]
2339        clonedMetadata[key] = metadata[key].slice()
2340      }
2341    
2342      this.metadata = Object.create(null)
2343      this.metadata[term] = Object.create(null)
2344      this.metadata[term][field] = clonedMetadata
2345    }
2346    
2347    /**
2348     * An instance of lunr.MatchData will be created for every term that matches a
2349     * document. However only one instance is required in a lunr.Index~Result. This
2350     * method combines metadata from another instance of lunr.MatchData with this
2351     * objects metadata.
2352     *
2353     * @param {lunr.MatchData} otherMatchData - Another instance of match data to merge with this one.
2354     * @see {@link lunr.Index~Result}
2355     */
2356    lunr.MatchData.prototype.combine = function (otherMatchData) {
2357      var terms = Object.keys(otherMatchData.metadata)
2358    
2359      for (var i = 0; i < terms.length; i++) {
2360        var term = terms[i],
2361            fields = Object.keys(otherMatchData.metadata[term])
2362    
2363        if (this.metadata[term] == undefined) {
2364          this.metadata[term] = Object.create(null)
2365        }
2366    
2367        for (var j = 0; j < fields.length; j++) {
2368          var field = fields[j],
2369              keys = Object.keys(otherMatchData.metadata[term][field])
2370    
2371          if (this.metadata[term][field] == undefined) {
2372            this.metadata[term][field] = Object.create(null)
2373          }
2374    
2375          for (var k = 0; k < keys.length; k++) {
2376            var key = keys[k]
2377    
2378            if (this.metadata[term][field][key] == undefined) {
2379              this.metadata[term][field][key] = otherMatchData.metadata[term][field][key]
2380            } else {
2381              this.metadata[term][field][key] = this.metadata[term][field][key].concat(otherMatchData.metadata[term][field][key])
2382            }
2383    
2384          }
2385        }
2386      }
2387    }
2388    
2389    /**
2390     * Add metadata for a term/field pair to this instance of match data.
2391     *
2392     * @param {string} term - The term this match data is associated with
2393     * @param {string} field - The field in which the term was found
2394     * @param {object} metadata - The metadata recorded about this term in this field
2395     */
2396    lunr.MatchData.prototype.add = function (term, field, metadata) {
2397      if (!(term in this.metadata)) {
2398        this.metadata[term] = Object.create(null)
2399        this.metadata[term][field] = metadata
2400        return
2401      }
2402    
2403      if (!(field in this.metadata[term])) {
2404        this.metadata[term][field] = metadata
2405        return
2406      }
2407    
2408      var metadataKeys = Object.keys(metadata)
2409    
2410      for (var i = 0; i < metadataKeys.length; i++) {
2411        var key = metadataKeys[i]
2412    
2413        if (key in this.metadata[term][field]) {
2414          this.metadata[term][field][key] = this.metadata[term][field][key].concat(metadata[key])
2415        } else {
2416          this.metadata[term][field][key] = metadata[key]
2417        }
2418      }
2419    }
2420    /**
2421     * A lunr.Query provides a programmatic way of defining queries to be performed
2422     * against a {@link lunr.Index}.
2423     *
2424     * Prefer constructing a lunr.Query using the {@link lunr.Index#query} method
2425     * so the query object is pre-initialized with the right index fields.
2426     *
2427     * @constructor
2428     * @property {lunr.Query~Clause[]} clauses - An array of query clauses.
2429     * @property {string[]} allFields - An array of all available fields in a lunr.Index.
2430     */
2431    lunr.Query = function (allFields) {
2432      this.clauses = []
2433      this.allFields = allFields
2434    }
2435    
2436    /**
2437     * Constants for indicating what kind of automatic wildcard insertion will be used when constructing a query clause.
2438     *
2439     * This allows wildcards to be added to the beginning and end of a term without having to manually do any string
2440     * concatenation.
2441     *
2442     * The wildcard constants can be bitwise combined to select both leading and trailing wildcards.
2443     *
2444     * @constant
2445     * @default
2446     * @property {number} wildcard.NONE - The term will have no wildcards inserted, this is the default behaviour
2447     * @property {number} wildcard.LEADING - Prepend the term with a wildcard, unless a leading wildcard already exists
2448     * @property {number} wildcard.TRAILING - Append a wildcard to the term, unless a trailing wildcard already exists
2449     * @see lunr.Query~Clause
2450     * @see lunr.Query#clause
2451     * @see lunr.Query#term
2452     * @example <caption>query term with trailing wildcard</caption>
2453     * query.term('foo', { wildcard: lunr.Query.wildcard.TRAILING })
2454     * @example <caption>query term with leading and trailing wildcard</caption>
2455     * query.term('foo', {
2456     *   wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING
2457     * })
2458     */
2459    lunr.Query.wildcard = new String ("*")
2460    lunr.Query.wildcard.NONE = 0
2461    lunr.Query.wildcard.LEADING = 1
2462    lunr.Query.wildcard.TRAILING = 2
2463    
2464    /**
2465     * A single clause in a {@link lunr.Query} contains a term and details on how to
2466     * match that term against a {@link lunr.Index}.
2467     *
2468     * @typedef {Object} lunr.Query~Clause
2469     * @property {string[]} fields - The fields in an index this clause should be matched against.
2470     * @property {number} [boost=1] - Any boost that should be applied when matching this clause.
2471     * @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be.
2472     * @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline.
2473     * @property {number} [wildcard=0] - Whether the term should have wildcards appended or prepended.
2474     */
2475    
2476    /**
2477     * Adds a {@link lunr.Query~Clause} to this query.
2478     *
2479     * Unless the clause contains the fields to be matched all fields will be matched. In addition
2480     * a default boost of 1 is applied to the clause.
2481     *
2482     * @param {lunr.Query~Clause} clause - The clause to add to this query.
2483     * @see lunr.Query~Clause
2484     * @returns {lunr.Query}
2485     */
2486    lunr.Query.prototype.clause = function (clause) {
2487      if (!('fields' in clause)) {
2488        clause.fields = this.allFields
2489      }
2490    
2491      if (!('boost' in clause)) {
2492        clause.boost = 1
2493      }
2494    
2495      if (!('usePipeline' in clause)) {
2496        clause.usePipeline = true
2497      }
2498    
2499      if (!('wildcard' in clause)) {
2500        clause.wildcard = lunr.Query.wildcard.NONE
2501      }
2502    
2503      if ((clause.wildcard & lunr.Query.wildcard.LEADING) && (clause.term.charAt(0) != lunr.Query.wildcard)) {
2504        clause.term = "*" + clause.term
2505      }
2506    
2507      if ((clause.wildcard & lunr.Query.wildcard.TRAILING) && (clause.term.slice(-1) != lunr.Query.wildcard)) {
2508        clause.term = "" + clause.term + "*"
2509      }
2510    
2511      this.clauses.push(clause)
2512    
2513      return this
2514    }
2515    
2516    /**
2517     * Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause}
2518     * to the list of clauses that make up this query.
2519     *
2520     * @param {string} term - The term to add to the query.
2521     * @param {Object} [options] - Any additional properties to add to the query clause.
2522     * @returns {lunr.Query}
2523     * @see lunr.Query#clause
2524     * @see lunr.Query~Clause
2525     * @example <caption>adding a single term to a query</caption>
2526     * query.term("foo")
2527     * @example <caption>adding a single term to a query and specifying search fields, term boost and automatic trailing wildcard</caption>
2528     * query.term("foo", {
2529     *   fields: ["title"],
2530     *   boost: 10,
2531     *   wildcard: lunr.Query.wildcard.TRAILING
2532     * })
2533     */
2534    lunr.Query.prototype.term = function (term, options) {
2535      var clause = options || {}
2536      clause.term = term
2537    
2538      this.clause(clause)
2539    
2540      return this
2541    }
2542    lunr.QueryParseError = function (message, start, end) {
2543      this.name = "QueryParseError"
2544      this.message = message
2545      this.start = start
2546      this.end = end
2547    }
2548    
2549    lunr.QueryParseError.prototype = new Error
2550    lunr.QueryLexer = function (str) {
2551      this.lexemes = []
2552      this.str = str
2553      this.length = str.length
2554      this.pos = 0
2555      this.start = 0
2556      this.escapeCharPositions = []
2557    }
2558    
2559    lunr.QueryLexer.prototype.run = function () {
2560      var state = lunr.QueryLexer.lexText
2561    
2562      while (state) {
2563        state = state(this)
2564      }
2565    }
2566    
2567    lunr.QueryLexer.prototype.sliceString = function () {
2568      var subSlices = [],
2569          sliceStart = this.start,
2570          sliceEnd = this.pos
2571    
2572      for (var i = 0; i < this.escapeCharPositions.length; i++) {
2573        sliceEnd = this.escapeCharPositions[i]
2574        subSlices.push(this.str.slice(sliceStart, sliceEnd))
2575        sliceStart = sliceEnd + 1
2576      }
2577    
2578      subSlices.push(this.str.slice(sliceStart, this.pos))
2579      this.escapeCharPositions.length = 0
2580    
2581      return subSlices.join('')
2582    }
2583    
2584    lunr.QueryLexer.prototype.emit = function (type) {
2585      this.lexemes.push({
2586        type: type,
2587        str: this.sliceString(),
2588        start: this.start,
2589        end: this.pos
2590      })
2591    
2592      this.start = this.pos
2593    }
2594    
2595    lunr.QueryLexer.prototype.escapeCharacter = function () {
2596      this.escapeCharPositions.push(this.pos - 1)
2597      this.pos += 1
2598    }
2599    
2600    lunr.QueryLexer.prototype.next = function () {
2601      if (this.pos >= this.length) {
2602        return lunr.QueryLexer.EOS
2603      }
2604    
2605      var char = this.str.charAt(this.pos)
2606      this.pos += 1
2607      return char
2608    }
2609    
2610    lunr.QueryLexer.prototype.width = function () {
2611      return this.pos - this.start
2612    }
2613    
2614    lunr.QueryLexer.prototype.ignore = function () {
2615      if (this.start == this.pos) {
2616        this.pos += 1
2617      }
2618    
2619      this.start = this.pos
2620    }
2621    
2622    lunr.QueryLexer.prototype.backup = function () {
2623      this.pos -= 1
2624    }
2625    
2626    lunr.QueryLexer.prototype.acceptDigitRun = function () {
2627      var char, charCode
2628    
2629      do {
2630        char = this.next()
2631        charCode = char.charCodeAt(0)
2632      } while (charCode > 47 && charCode < 58)
2633    
2634      if (char != lunr.QueryLexer.EOS) {
2635        this.backup()
2636      }
2637    }
2638    
2639    lunr.QueryLexer.prototype.more = function () {
2640      return this.pos < this.length
2641    }
2642    
2643    lunr.QueryLexer.EOS = 'EOS'
2644    lunr.QueryLexer.FIELD = 'FIELD'
2645    lunr.QueryLexer.TERM = 'TERM'
2646    lunr.QueryLexer.EDIT_DISTANCE = 'EDIT_DISTANCE'
2647    lunr.QueryLexer.BOOST = 'BOOST'
2648    
2649    lunr.QueryLexer.lexField = function (lexer) {
2650      lexer.backup()
2651      lexer.emit(lunr.QueryLexer.FIELD)
2652      lexer.ignore()
2653      return lunr.QueryLexer.lexText
2654    }
2655    
2656    lunr.QueryLexer.lexTerm = function (lexer) {
2657      if (lexer.width() > 1) {
2658        lexer.backup()
2659        lexer.emit(lunr.QueryLexer.TERM)
2660      }
2661    
2662      lexer.ignore()
2663    
2664      if (lexer.more()) {
2665        return lunr.QueryLexer.lexText
2666      }
2667    }
2668    
2669    lunr.QueryLexer.lexEditDistance = function (lexer) {
2670      lexer.ignore()
2671      lexer.acceptDigitRun()
2672      lexer.emit(lunr.QueryLexer.EDIT_DISTANCE)
2673      return lunr.QueryLexer.lexText
2674    }
2675    
2676    lunr.QueryLexer.lexBoost = function (lexer) {
2677      lexer.ignore()
2678      lexer.acceptDigitRun()
2679      lexer.emit(lunr.QueryLexer.BOOST)
2680      return lunr.QueryLexer.lexText
2681    }
2682    
2683    lunr.QueryLexer.lexEOS = function (lexer) {
2684      if (lexer.width() > 0) {
2685        lexer.emit(lunr.QueryLexer.TERM)
2686      }
2687    }
2688    
2689    // This matches the separator used when tokenising fields
2690    // within a document. These should match otherwise it is
2691    // not possible to search for some tokens within a document.
2692    //
2693    // It is possible for the user to change the separator on the
2694    // tokenizer so it _might_ clash with any other of the special
2695    // characters already used within the search string, e.g. :.
2696    //
2697    // This means that it is possible to change the separator in
2698    // such a way that makes some words unsearchable using a search
2699    // string.
2700    lunr.QueryLexer.termSeparator = lunr.tokenizer.separator
2701    
2702    lunr.QueryLexer.lexText = function (lexer) {
2703      while (true) {
2704        var char = lexer.next()
2705    
2706        if (char == lunr.QueryLexer.EOS) {
2707          return lunr.QueryLexer.lexEOS
2708        }
2709    
2710        // Escape character is '\'
2711        if (char.charCodeAt(0) == 92) {
2712          lexer.escapeCharacter()
2713          continue
2714        }
2715    
2716        if (char == ":") {
2717          return lunr.QueryLexer.lexField
2718        }
2719    
2720        if (char == "~") {
2721          lexer.backup()
2722          if (lexer.width() > 0) {
2723            lexer.emit(lunr.QueryLexer.TERM)
2724          }
2725          return lunr.QueryLexer.lexEditDistance
2726        }
2727    
2728        if (char == "^") {
2729          lexer.backup()
2730          if (lexer.width() > 0) {
2731            lexer.emit(lunr.QueryLexer.TERM)
2732          }
2733          return lunr.QueryLexer.lexBoost
2734        }
2735    
2736        if (char.match(lunr.QueryLexer.termSeparator)) {
2737          return lunr.QueryLexer.lexTerm
2738        }
2739      }
2740    }
2741    
2742    lunr.QueryParser = function (str, query) {
2743      this.lexer = new lunr.QueryLexer (str)
2744      this.query = query
2745      this.currentClause = {}
2746      this.lexemeIdx = 0
2747    }
2748    
2749    lunr.QueryParser.prototype.parse = function () {
2750      this.lexer.run()
2751      this.lexemes = this.lexer.lexemes
2752    
2753      var state = lunr.QueryParser.parseFieldOrTerm
2754    
2755      while (state) {
2756        state = state(this)
2757      }
2758    
2759      return this.query
2760    }
2761    
2762    lunr.QueryParser.prototype.peekLexeme = function () {
2763      return this.lexemes[this.lexemeIdx]
2764    }
2765    
2766    lunr.QueryParser.prototype.consumeLexeme = function () {
2767      var lexeme = this.peekLexeme()
2768      this.lexemeIdx += 1
2769      return lexeme
2770    }
2771    
2772    lunr.QueryParser.prototype.nextClause = function () {
2773      var completedClause = this.currentClause
2774      this.query.clause(completedClause)
2775      this.currentClause = {}
2776    }
2777    
2778    lunr.QueryParser.parseFieldOrTerm = function (parser) {
2779      var lexeme = parser.peekLexeme()
2780    
2781      if (lexeme == undefined) {
2782        return
2783      }
2784    
2785      switch (lexeme.type) {
2786        case lunr.QueryLexer.FIELD:
2787          return lunr.QueryParser.parseField
2788        case lunr.QueryLexer.TERM:
2789          return lunr.QueryParser.parseTerm
2790        default:
2791          var errorMessage = "expected either a field or a term, found " + lexeme.type
2792    
2793          if (lexeme.str.length >= 1) {
2794            errorMessage += " with value '" + lexeme.str + "'"
2795          }
2796    
2797          throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)
2798      }
2799    }
2800    
2801    lunr.QueryParser.parseField = function (parser) {
2802      var lexeme = parser.consumeLexeme()
2803    
2804      if (lexeme == undefined) {
2805        return
2806      }
2807    
2808      if (parser.query.allFields.indexOf(lexeme.str) == -1) {
2809        var possibleFields = parser.query.allFields.map(function (f) { return "'" + f + "'" }).join(', '),
2810            errorMessage = "unrecognised field '" + lexeme.str + "', possible fields: " + possibleFields
2811    
2812        throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)
2813      }
2814    
2815      parser.currentClause.fields = [lexeme.str]
2816    
2817      var nextLexeme = parser.peekLexeme()
2818    
2819      if (nextLexeme == undefined) {
2820        var errorMessage = "expecting term, found nothing"
2821        throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)
2822      }
2823    
2824      switch (nextLexeme.type) {
2825        case lunr.QueryLexer.TERM:
2826          return lunr.QueryParser.parseTerm
2827        default:
2828          var errorMessage = "expecting term, found '" + nextLexeme.type + "'"
2829          throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
2830      }
2831    }
2832    
2833    lunr.QueryParser.parseTerm = function (parser) {
2834      var lexeme = parser.consumeLexeme()
2835    
2836      if (lexeme == undefined) {
2837        return
2838      }
2839    
2840      parser.currentClause.term = lexeme.str.toLowerCase()
2841    
2842      if (lexeme.str.indexOf("*") != -1) {
2843        parser.currentClause.usePipeline = false
2844      }
2845    
2846      var nextLexeme = parser.peekLexeme()
2847    
2848      if (nextLexeme == undefined) {
2849        parser.nextClause()
2850        return
2851      }
2852    
2853      switch (nextLexeme.type) {
2854        case lunr.QueryLexer.TERM:
2855          parser.nextClause()
2856          return lunr.QueryParser.parseTerm
2857        case lunr.QueryLexer.FIELD:
2858          parser.nextClause()
2859          return lunr.QueryParser.parseField
2860        case lunr.QueryLexer.EDIT_DISTANCE:
2861          return lunr.QueryParser.parseEditDistance
2862        case lunr.QueryLexer.BOOST:
2863          return lunr.QueryParser.parseBoost
2864        default:
2865          var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'"
2866          throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
2867      }
2868    }
2869    
2870    lunr.QueryParser.parseEditDistance = function (parser) {
2871      var lexeme = parser.consumeLexeme()
2872    
2873      if (lexeme == undefined) {
2874        return
2875      }
2876    
2877      var editDistance = parseInt(lexeme.str, 10)
2878    
2879      if (isNaN(editDistance)) {
2880        var errorMessage = "edit distance must be numeric"
2881        throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)
2882      }
2883    
2884      parser.currentClause.editDistance = editDistance
2885    
2886      var nextLexeme = parser.peekLexeme()
2887    
2888      if (nextLexeme == undefined) {
2889        parser.nextClause()
2890        return
2891      }
2892    
2893      switch (nextLexeme.type) {
2894        case lunr.QueryLexer.TERM:
2895          parser.nextClause()
2896          return lunr.QueryParser.parseTerm
2897        case lunr.QueryLexer.FIELD:
2898          parser.nextClause()
2899          return lunr.QueryParser.parseField
2900        case lunr.QueryLexer.EDIT_DISTANCE:
2901          return lunr.QueryParser.parseEditDistance
2902        case lunr.QueryLexer.BOOST:
2903          return lunr.QueryParser.parseBoost
2904        default:
2905          var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'"
2906          throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
2907      }
2908    }
2909    
2910    lunr.QueryParser.parseBoost = function (parser) {
2911      var lexeme = parser.consumeLexeme()
2912    
2913      if (lexeme == undefined) {
2914        return
2915      }
2916    
2917      var boost = parseInt(lexeme.str, 10)
2918    
2919      if (isNaN(boost)) {
2920        var errorMessage = "boost must be numeric"
2921        throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)
2922      }
2923    
2924      parser.currentClause.boost = boost
2925    
2926      var nextLexeme = parser.peekLexeme()
2927    
2928      if (nextLexeme == undefined) {
2929        parser.nextClause()
2930        return
2931      }
2932    
2933      switch (nextLexeme.type) {
2934        case lunr.QueryLexer.TERM:
2935          parser.nextClause()
2936          return lunr.QueryParser.parseTerm
2937        case lunr.QueryLexer.FIELD:
2938          parser.nextClause()
2939          return lunr.QueryParser.parseField
2940        case lunr.QueryLexer.EDIT_DISTANCE:
2941          return lunr.QueryParser.parseEditDistance
2942        case lunr.QueryLexer.BOOST:
2943          return lunr.QueryParser.parseBoost
2944        default:
2945          var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'"
2946          throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
2947      }
2948    }
2949    
2950      /**
2951       * export the module via AMD, CommonJS or as a browser global
2952       * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js
2953       */
2954      ;(function (root, factory) {
2955        if (typeof define === 'function' && define.amd) {
2956          // AMD. Register as an anonymous module.
2957          define(factory)
2958        } else if (typeof exports === 'object') {
2959          /**
2960           * Node. Does not work with strict CommonJS, but
2961           * only CommonJS-like enviroments that support module.exports,
2962           * like Node.
2963           */
2964          module.exports = factory()
2965        } else {
2966          // Browser globals (root is window)
2967          root.lunr = factory()
2968        }
2969      }(this, function () {
2970        /**
2971         * Just return a value to define the module export.
2972         * This example returns an object, but the module
2973         * can return a function as the exported value.
2974         */
2975        return lunr
2976      }))
2977    })();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.