appscms-tools-theme 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appscms-tools-theme
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - vivek-appscms
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-09 00:00:00.000000000 Z
11
+ date: 2021-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -70,6 +70,7 @@ files:
70
70
  - _data/feature/en/split-pdf.json
71
71
  - _data/feature/hi/compress-pdf.json
72
72
  - _data/footer/en/data.json
73
+ - _data/head/en/data.json
73
74
  - _data/header/en/data.json
74
75
  - _data/home/en.json
75
76
  - _data/home/hi.json
@@ -88,7 +89,6 @@ files:
88
89
  - _includes/paginationPostPage.html
89
90
  - _includes/postbox.html
90
91
  - _includes/script.html
91
- - _includes/search-lunr.html
92
92
  - _includes/section/alertbar.html
93
93
  - _includes/section/count.html
94
94
  - _includes/uploader/index.html
@@ -115,8 +115,6 @@ files:
115
115
  - assets/images/logo.png
116
116
  - assets/instagram.svg
117
117
  - assets/js/TopScroll.js
118
- - assets/js/lunr.js
119
- - assets/js/lunrsearchengine.js
120
118
  - assets/linkdin.svg
121
119
  - assets/pdf.svg
122
120
  - assets/protect.svg
@@ -1,16 +0,0 @@
1
- <form class="bd-search m-0" onSubmit="return lunr_search(document.getElementById('lunrsearch').value);">
2
- <input type="text" class="form-control text-small launch-modal-search py-4" id="lunrsearch" name="q" maxlength="255"
3
- value="" placeholder="Type and enter..." style="border-radius: 50px; font-size: 12px;" />
4
- </form>
5
-
6
- <div id="lunrsearchresults">
7
- <ul></ul>
8
- </div>
9
-
10
- <script src=" {{site.url}}/assets/js/lunr.js"></script>
11
- <script src="{{site.url}}/assets/js/lunrsearchengine.js"></script>
12
-
13
-
14
- <script>
15
-
16
- </script>
data/assets/js/lunr.js DELETED
@@ -1,2977 +0,0 @@
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={}] - 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} 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,
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[]} 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
- /**
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} 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]),
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
- })();