solidus_api_v2 0.2.0

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