minimal-mistakes-jekyll 4.13.0 → 4.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/LICENSE +21 -0
- data/README.md +33 -17
- data/_data/ui-text.yml +45 -0
- data/_includes/author-profile-custom-links.html +1 -1
- data/_includes/author-profile.html +25 -25
- data/_includes/breadcrumbs.html +3 -3
- data/_includes/comments-providers/scripts.html +2 -0
- data/_includes/comments-providers/utterances.html +20 -0
- data/_includes/comments.html +5 -2
- data/_includes/footer.html +1 -1
- data/_includes/head.html +1 -3
- data/_includes/page__hero.html +6 -1
- data/_includes/scripts.html +4 -4
- data/_includes/search/algolia-search-scripts.html +2 -2
- data/_includes/video +4 -2
- data/_layouts/default.html +7 -7
- data/_layouts/single.html +3 -0
- data/_sass/minimal-mistakes.scss +2 -2
- data/_sass/minimal-mistakes/_archive.scss +70 -0
- data/_sass/minimal-mistakes/_base.scss +1 -1
- data/_sass/minimal-mistakes/_mixins.scss +2 -2
- data/_sass/minimal-mistakes/_navigation.scss +6 -12
- data/_sass/minimal-mistakes/_search.scss +5 -4
- data/_sass/minimal-mistakes/_utilities.scss +10 -2
- data/_sass/minimal-mistakes/_variables.scss +7 -6
- data/_sass/minimal-mistakes/skins/_dark.scss +4 -18
- data/_sass/minimal-mistakes/skins/_neon.scss +4 -0
- data/_sass/minimal-mistakes/skins/_plum.scss +4 -0
- data/assets/js/lunr/lunr-store.js +4 -2
- data/assets/js/lunr/lunr.js +591 -84
- data/assets/js/lunr/lunr.min.js +3 -3
- data/assets/js/main.min.js +1 -1
- metadata +18 -4
- data/LICENSE.txt +0 -63
- data/_includes/base_path +0 -5
@@ -11,16 +11,17 @@
|
|
11
11
|
.search__toggle {
|
12
12
|
margin-left: 1rem;
|
13
13
|
margin-right: 1rem;
|
14
|
+
height: $nav-toggle-height;
|
14
15
|
border: 0;
|
15
16
|
outline: none;
|
16
|
-
color: $
|
17
|
+
color: $primary-color;
|
17
18
|
background-color: transparent;
|
18
19
|
cursor: pointer;
|
19
20
|
-webkit-transition: 0.2s;
|
20
21
|
transition: 0.2s;
|
21
22
|
|
22
23
|
&:hover {
|
23
|
-
color: $
|
24
|
+
color: mix(#000, $primary-color, 25%);
|
24
25
|
}
|
25
26
|
}
|
26
27
|
|
@@ -114,12 +115,12 @@
|
|
114
115
|
}
|
115
116
|
|
116
117
|
.archive__item-title .ais-Highlight {
|
117
|
-
color: $
|
118
|
+
color: $primary-color;
|
118
119
|
font-style: normal;
|
119
120
|
text-decoration: underline;
|
120
121
|
}
|
121
122
|
.archive__item-excerpt .ais-Highlight {
|
122
|
-
color: $
|
123
|
+
color: $primary-color;
|
123
124
|
font-style: normal;
|
124
125
|
font-weight: bold;
|
125
126
|
}
|
@@ -315,7 +315,7 @@ body:hover .visually-hidden button {
|
|
315
315
|
position: relative;
|
316
316
|
width: $navicon-width;
|
317
317
|
height: $navicon-height;
|
318
|
-
background:
|
318
|
+
background: $primary-color;
|
319
319
|
margin: auto;
|
320
320
|
-webkit-transition: 0.3s;
|
321
321
|
transition: 0.3s;
|
@@ -327,7 +327,7 @@ body:hover .visually-hidden button {
|
|
327
327
|
left: 0;
|
328
328
|
width: $navicon-width;
|
329
329
|
height: $navicon-height;
|
330
|
-
background:
|
330
|
+
background: $primary-color;
|
331
331
|
-webkit-transition: 0.3s;
|
332
332
|
transition: 0.3s;
|
333
333
|
}
|
@@ -366,6 +366,14 @@ body:hover .visually-hidden button {
|
|
366
366
|
}
|
367
367
|
}
|
368
368
|
|
369
|
+
.greedy-nav__toggle:hover {
|
370
|
+
.navicon,
|
371
|
+
.navicon:before,
|
372
|
+
.navicon:after {
|
373
|
+
background: mix(#000, $primary-color, 25%);
|
374
|
+
}
|
375
|
+
}
|
376
|
+
|
369
377
|
/*
|
370
378
|
Sticky, fixed to top content
|
371
379
|
========================================================================== */
|
@@ -62,11 +62,12 @@ $border-color: $lighter-gray !default;
|
|
62
62
|
$form-background-color: $lighter-gray !default;
|
63
63
|
$footer-background-color: $lighter-gray !default;
|
64
64
|
|
65
|
-
$primary-color: #
|
66
|
-
$success-color: #
|
67
|
-
$warning-color: #
|
65
|
+
$primary-color: #6f777d !default;
|
66
|
+
$success-color: #3fa63f !default;
|
67
|
+
$warning-color: #d67f05 !default;
|
68
68
|
$danger-color: #ee5f5b !default;
|
69
|
-
$info-color: #
|
69
|
+
$info-color: #3b9cba !default;
|
70
|
+
$focus-color: $primary-color !default;
|
70
71
|
|
71
72
|
/* YIQ color contrast */
|
72
73
|
$yiq-contrasted-dark-default: $dark-gray !default;
|
@@ -101,9 +102,9 @@ $youtube-color: #bb0000 !default;
|
|
101
102
|
$xing-color: #006567 !default;
|
102
103
|
|
103
104
|
/* links */
|
104
|
-
$link-color: $info-color !default;
|
105
|
+
$link-color: mix(#000, $info-color, 15%) !default;
|
105
106
|
$link-color-hover: mix(#000, $link-color, 25%) !default;
|
106
|
-
$link-color-visited: mix(#fff, $link-color,
|
107
|
+
$link-color-visited: mix(#fff, $link-color, 15%) !default;
|
107
108
|
$masthead-link-color: $primary-color !default;
|
108
109
|
$masthead-link-color-hover: mix(#000, $primary-color, 25%) !default;
|
109
110
|
$navicon-link-color-hover: mix(#fff, $primary-color, 75%) !default;
|
@@ -18,25 +18,11 @@ $masthead-link-color: $text-color !default;
|
|
18
18
|
$masthead-link-color-hover: mix(#000, $text-color, 20%) !default;
|
19
19
|
$navicon-link-color-hover: mix(#000, $background-color, 30%) !default;
|
20
20
|
|
21
|
-
/* dark syntax highlighting (base16) */
|
22
|
-
$base00: #ffffff !default;
|
23
|
-
$base01: #e0e0e0 !default;
|
24
|
-
$base02: #d0d0d0 !default;
|
25
|
-
$base03: #b0b0b0 !default;
|
26
|
-
$base04: #000000 !default;
|
27
|
-
$base05: #101010 !default;
|
28
|
-
$base06: #151515 !default;
|
29
|
-
$base07: #202020 !default;
|
30
|
-
$base08: #ff0086 !default;
|
31
|
-
$base09: #fd8900 !default;
|
32
|
-
$base0a: #aba800 !default;
|
33
|
-
$base0b: #00c918 !default;
|
34
|
-
$base0c: #1faaaa !default;
|
35
|
-
$base0d: #3777e6 !default;
|
36
|
-
$base0e: #ad00a1 !default;
|
37
|
-
$base0f: #cc6633 !default;
|
38
|
-
|
39
21
|
.author__urls.social-icons .svg-inline--fa,
|
40
22
|
.page__footer-follow .social-icons .svg-inline--fa {
|
41
23
|
color: inherit;
|
42
24
|
}
|
25
|
+
|
26
|
+
.ais-search-box .ais-search-box--input {
|
27
|
+
background-color: $form-background-color;
|
28
|
+
}
|
@@ -18,7 +18,8 @@ var store = [
|
|
18
18
|
"title": {{ doc.title | jsonify }},
|
19
19
|
"excerpt":
|
20
20
|
{%- if site.search_full_content == true -%}
|
21
|
-
{{ doc.content |
|
21
|
+
{{ doc.content | newline_to_br |
|
22
|
+
replace:"<br />", " " |
|
22
23
|
replace:"</p>", " " |
|
23
24
|
replace:"</h1>", " " |
|
24
25
|
replace:"</h2>", " " |
|
@@ -28,7 +29,8 @@ var store = [
|
|
28
29
|
replace:"</h6>", " "|
|
29
30
|
strip_html | strip_newlines | jsonify }},
|
30
31
|
{%- else -%}
|
31
|
-
{{ doc.content |
|
32
|
+
{{ doc.content | newline_to_br |
|
33
|
+
replace:"<br />", " " |
|
32
34
|
replace:"</p>", " " |
|
33
35
|
replace:"</h1>", " " |
|
34
36
|
replace:"</h2>", " " |
|
data/assets/js/lunr/lunr.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/**
|
2
|
-
* lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.
|
3
|
-
* Copyright (C)
|
2
|
+
* lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.3
|
3
|
+
* Copyright (C) 2018 Oliver Nightingale
|
4
4
|
* @license MIT
|
5
5
|
*/
|
6
6
|
|
@@ -54,14 +54,15 @@ var lunr = function (config) {
|
|
54
54
|
return builder.build()
|
55
55
|
}
|
56
56
|
|
57
|
-
lunr.version = "2.
|
57
|
+
lunr.version = "2.3.3"
|
58
58
|
/*!
|
59
59
|
* lunr.utils
|
60
|
-
* Copyright (C)
|
60
|
+
* Copyright (C) 2018 Oliver Nightingale
|
61
61
|
*/
|
62
62
|
|
63
63
|
/**
|
64
64
|
* A namespace containing utils for the rest of the lunr library
|
65
|
+
* @namespace lunr.utils
|
65
66
|
*/
|
66
67
|
lunr.utils = {}
|
67
68
|
|
@@ -69,7 +70,8 @@ lunr.utils = {}
|
|
69
70
|
* Print a warning message to the console.
|
70
71
|
*
|
71
72
|
* @param {String} message The message to be printed.
|
72
|
-
* @memberOf
|
73
|
+
* @memberOf lunr.utils
|
74
|
+
* @function
|
73
75
|
*/
|
74
76
|
lunr.utils.warn = (function (global) {
|
75
77
|
/* eslint-disable no-console */
|
@@ -90,7 +92,7 @@ lunr.utils.warn = (function (global) {
|
|
90
92
|
*
|
91
93
|
* @param {Any} obj The object to convert to a string.
|
92
94
|
* @return {String} string representation of the passed object.
|
93
|
-
* @memberOf
|
95
|
+
* @memberOf lunr.utils
|
94
96
|
*/
|
95
97
|
lunr.utils.asString = function (obj) {
|
96
98
|
if (obj === void 0 || obj === null) {
|
@@ -99,6 +101,52 @@ lunr.utils.asString = function (obj) {
|
|
99
101
|
return obj.toString()
|
100
102
|
}
|
101
103
|
}
|
104
|
+
|
105
|
+
/**
|
106
|
+
* Clones an object.
|
107
|
+
*
|
108
|
+
* Will create a copy of an existing object such that any mutations
|
109
|
+
* on the copy cannot affect the original.
|
110
|
+
*
|
111
|
+
* Only shallow objects are supported, passing a nested object to this
|
112
|
+
* function will cause a TypeError.
|
113
|
+
*
|
114
|
+
* Objects with primitives, and arrays of primitives are supported.
|
115
|
+
*
|
116
|
+
* @param {Object} obj The object to clone.
|
117
|
+
* @return {Object} a clone of the passed object.
|
118
|
+
* @throws {TypeError} when a nested object is passed.
|
119
|
+
* @memberOf Utils
|
120
|
+
*/
|
121
|
+
lunr.utils.clone = function (obj) {
|
122
|
+
if (obj === null || obj === undefined) {
|
123
|
+
return obj
|
124
|
+
}
|
125
|
+
|
126
|
+
var clone = Object.create(null),
|
127
|
+
keys = Object.keys(obj)
|
128
|
+
|
129
|
+
for (var i = 0; i < keys.length; i++) {
|
130
|
+
var key = keys[i],
|
131
|
+
val = obj[key]
|
132
|
+
|
133
|
+
if (Array.isArray(val)) {
|
134
|
+
clone[key] = val.slice()
|
135
|
+
continue
|
136
|
+
}
|
137
|
+
|
138
|
+
if (typeof val === 'string' ||
|
139
|
+
typeof val === 'number' ||
|
140
|
+
typeof val === 'boolean') {
|
141
|
+
clone[key] = val
|
142
|
+
continue
|
143
|
+
}
|
144
|
+
|
145
|
+
throw new TypeError("clone is not deep and does not support nested objects")
|
146
|
+
}
|
147
|
+
|
148
|
+
return clone
|
149
|
+
}
|
102
150
|
lunr.FieldRef = function (docRef, fieldName, stringValue) {
|
103
151
|
this.docRef = docRef
|
104
152
|
this.fieldName = fieldName
|
@@ -127,6 +175,139 @@ lunr.FieldRef.prototype.toString = function () {
|
|
127
175
|
|
128
176
|
return this._stringValue
|
129
177
|
}
|
178
|
+
/*!
|
179
|
+
* lunr.Set
|
180
|
+
* Copyright (C) 2018 Oliver Nightingale
|
181
|
+
*/
|
182
|
+
|
183
|
+
/**
|
184
|
+
* A lunr set.
|
185
|
+
*
|
186
|
+
* @constructor
|
187
|
+
*/
|
188
|
+
lunr.Set = function (elements) {
|
189
|
+
this.elements = Object.create(null)
|
190
|
+
|
191
|
+
if (elements) {
|
192
|
+
this.length = elements.length
|
193
|
+
|
194
|
+
for (var i = 0; i < this.length; i++) {
|
195
|
+
this.elements[elements[i]] = true
|
196
|
+
}
|
197
|
+
} else {
|
198
|
+
this.length = 0
|
199
|
+
}
|
200
|
+
}
|
201
|
+
|
202
|
+
/**
|
203
|
+
* A complete set that contains all elements.
|
204
|
+
*
|
205
|
+
* @static
|
206
|
+
* @readonly
|
207
|
+
* @type {lunr.Set}
|
208
|
+
*/
|
209
|
+
lunr.Set.complete = {
|
210
|
+
intersect: function (other) {
|
211
|
+
return other
|
212
|
+
},
|
213
|
+
|
214
|
+
union: function (other) {
|
215
|
+
return other
|
216
|
+
},
|
217
|
+
|
218
|
+
contains: function () {
|
219
|
+
return true
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
/**
|
224
|
+
* An empty set that contains no elements.
|
225
|
+
*
|
226
|
+
* @static
|
227
|
+
* @readonly
|
228
|
+
* @type {lunr.Set}
|
229
|
+
*/
|
230
|
+
lunr.Set.empty = {
|
231
|
+
intersect: function () {
|
232
|
+
return this
|
233
|
+
},
|
234
|
+
|
235
|
+
union: function (other) {
|
236
|
+
return other
|
237
|
+
},
|
238
|
+
|
239
|
+
contains: function () {
|
240
|
+
return false
|
241
|
+
}
|
242
|
+
}
|
243
|
+
|
244
|
+
/**
|
245
|
+
* Returns true if this set contains the specified object.
|
246
|
+
*
|
247
|
+
* @param {object} object - Object whose presence in this set is to be tested.
|
248
|
+
* @returns {boolean} - True if this set contains the specified object.
|
249
|
+
*/
|
250
|
+
lunr.Set.prototype.contains = function (object) {
|
251
|
+
return !!this.elements[object]
|
252
|
+
}
|
253
|
+
|
254
|
+
/**
|
255
|
+
* Returns a new set containing only the elements that are present in both
|
256
|
+
* this set and the specified set.
|
257
|
+
*
|
258
|
+
* @param {lunr.Set} other - set to intersect with this set.
|
259
|
+
* @returns {lunr.Set} a new set that is the intersection of this and the specified set.
|
260
|
+
*/
|
261
|
+
|
262
|
+
lunr.Set.prototype.intersect = function (other) {
|
263
|
+
var a, b, elements, intersection = []
|
264
|
+
|
265
|
+
if (other === lunr.Set.complete) {
|
266
|
+
return this
|
267
|
+
}
|
268
|
+
|
269
|
+
if (other === lunr.Set.empty) {
|
270
|
+
return other
|
271
|
+
}
|
272
|
+
|
273
|
+
if (this.length < other.length) {
|
274
|
+
a = this
|
275
|
+
b = other
|
276
|
+
} else {
|
277
|
+
a = other
|
278
|
+
b = this
|
279
|
+
}
|
280
|
+
|
281
|
+
elements = Object.keys(a.elements)
|
282
|
+
|
283
|
+
for (var i = 0; i < elements.length; i++) {
|
284
|
+
var element = elements[i]
|
285
|
+
if (element in b.elements) {
|
286
|
+
intersection.push(element)
|
287
|
+
}
|
288
|
+
}
|
289
|
+
|
290
|
+
return new lunr.Set (intersection)
|
291
|
+
}
|
292
|
+
|
293
|
+
/**
|
294
|
+
* Returns a new set combining the elements of this and the specified set.
|
295
|
+
*
|
296
|
+
* @param {lunr.Set} other - set to union with this set.
|
297
|
+
* @return {lunr.Set} a new set that is the union of this and the specified set.
|
298
|
+
*/
|
299
|
+
|
300
|
+
lunr.Set.prototype.union = function (other) {
|
301
|
+
if (other === lunr.Set.complete) {
|
302
|
+
return lunr.Set.complete
|
303
|
+
}
|
304
|
+
|
305
|
+
if (other === lunr.Set.empty) {
|
306
|
+
return this
|
307
|
+
}
|
308
|
+
|
309
|
+
return new lunr.Set(Object.keys(this.elements).concat(Object.keys(other.elements)))
|
310
|
+
}
|
130
311
|
/**
|
131
312
|
* A function to calculate the inverse document frequency for
|
132
313
|
* a posting. This is shared between the builder and the index
|
@@ -208,7 +389,7 @@ lunr.Token.prototype.clone = function (fn) {
|
|
208
389
|
}
|
209
390
|
/*!
|
210
391
|
* lunr.tokenizer
|
211
|
-
* Copyright (C)
|
392
|
+
* Copyright (C) 2018 Oliver Nightingale
|
212
393
|
*/
|
213
394
|
|
214
395
|
/**
|
@@ -220,18 +401,26 @@ lunr.Token.prototype.clone = function (fn) {
|
|
220
401
|
* then will split this string on the character in `lunr.tokenizer.separator`.
|
221
402
|
* Arrays will have their elements converted to strings and wrapped in a lunr.Token.
|
222
403
|
*
|
404
|
+
* Optional metadata can be passed to the tokenizer, this metadata will be cloned and
|
405
|
+
* added as metadata to every token that is created from the object to be tokenized.
|
406
|
+
*
|
223
407
|
* @static
|
224
408
|
* @param {?(string|object|object[])} obj - The object to convert into tokens
|
409
|
+
* @param {?object} metadata - Optional metadata to associate with every token
|
225
410
|
* @returns {lunr.Token[]}
|
411
|
+
* @see {@link lunr.Pipeline}
|
226
412
|
*/
|
227
|
-
lunr.tokenizer = function (obj) {
|
413
|
+
lunr.tokenizer = function (obj, metadata) {
|
228
414
|
if (obj == null || obj == undefined) {
|
229
415
|
return []
|
230
416
|
}
|
231
417
|
|
232
418
|
if (Array.isArray(obj)) {
|
233
419
|
return obj.map(function (t) {
|
234
|
-
return new lunr.Token(
|
420
|
+
return new lunr.Token(
|
421
|
+
lunr.utils.asString(t).toLowerCase(),
|
422
|
+
lunr.utils.clone(metadata)
|
423
|
+
)
|
235
424
|
})
|
236
425
|
}
|
237
426
|
|
@@ -246,11 +435,15 @@ lunr.tokenizer = function (obj) {
|
|
246
435
|
if ((char.match(lunr.tokenizer.separator) || sliceEnd == len)) {
|
247
436
|
|
248
437
|
if (sliceLength > 0) {
|
438
|
+
var tokenMetadata = lunr.utils.clone(metadata) || {}
|
439
|
+
tokenMetadata["position"] = [sliceStart, sliceLength]
|
440
|
+
tokenMetadata["index"] = tokens.length
|
441
|
+
|
249
442
|
tokens.push(
|
250
|
-
new lunr.Token (
|
251
|
-
|
252
|
-
|
253
|
-
|
443
|
+
new lunr.Token (
|
444
|
+
str.slice(sliceStart, sliceEnd),
|
445
|
+
tokenMetadata
|
446
|
+
)
|
254
447
|
)
|
255
448
|
}
|
256
449
|
|
@@ -272,7 +465,7 @@ lunr.tokenizer = function (obj) {
|
|
272
465
|
lunr.tokenizer.separator = /[\s\-]+/
|
273
466
|
/*!
|
274
467
|
* lunr.Pipeline
|
275
|
-
* Copyright (C)
|
468
|
+
* Copyright (C) 2018 Oliver Nightingale
|
276
469
|
*/
|
277
470
|
|
278
471
|
/**
|
@@ -475,14 +668,23 @@ lunr.Pipeline.prototype.run = function (tokens) {
|
|
475
668
|
|
476
669
|
for (var i = 0; i < stackLength; i++) {
|
477
670
|
var fn = this._stack[i]
|
671
|
+
var memo = []
|
478
672
|
|
479
|
-
|
480
|
-
var result = fn(
|
673
|
+
for (var j = 0; j < tokens.length; j++) {
|
674
|
+
var result = fn(tokens[j], j, tokens)
|
481
675
|
|
482
|
-
if (result === void 0 || result === '')
|
676
|
+
if (result === void 0 || result === '') continue
|
677
|
+
|
678
|
+
if (result instanceof Array) {
|
679
|
+
for (var k = 0; k < result.length; k++) {
|
680
|
+
memo.push(result[k])
|
681
|
+
}
|
682
|
+
} else {
|
683
|
+
memo.push(result)
|
684
|
+
}
|
685
|
+
}
|
483
686
|
|
484
|
-
|
485
|
-
}, [])
|
687
|
+
tokens = memo
|
486
688
|
}
|
487
689
|
|
488
690
|
return tokens
|
@@ -494,10 +696,12 @@ lunr.Pipeline.prototype.run = function (tokens) {
|
|
494
696
|
* token and mapping the resulting tokens back to strings.
|
495
697
|
*
|
496
698
|
* @param {string} str - The string to pass through the pipeline.
|
699
|
+
* @param {?object} metadata - Optional metadata to associate with the token
|
700
|
+
* passed to the pipeline.
|
497
701
|
* @returns {string[]}
|
498
702
|
*/
|
499
|
-
lunr.Pipeline.prototype.runString = function (str) {
|
500
|
-
var token = new lunr.Token (str)
|
703
|
+
lunr.Pipeline.prototype.runString = function (str, metadata) {
|
704
|
+
var token = new lunr.Token (str, metadata)
|
501
705
|
|
502
706
|
return this.run([token]).map(function (t) {
|
503
707
|
return t.toString()
|
@@ -528,7 +732,7 @@ lunr.Pipeline.prototype.toJSON = function () {
|
|
528
732
|
}
|
529
733
|
/*!
|
530
734
|
* lunr.Vector
|
531
|
-
* Copyright (C)
|
735
|
+
* Copyright (C) 2018 Oliver Nightingale
|
532
736
|
*/
|
533
737
|
|
534
738
|
/**
|
@@ -689,15 +893,14 @@ lunr.Vector.prototype.dot = function (otherVector) {
|
|
689
893
|
}
|
690
894
|
|
691
895
|
/**
|
692
|
-
* Calculates the
|
693
|
-
* vector.
|
896
|
+
* Calculates the similarity between this vector and another vector.
|
694
897
|
*
|
695
898
|
* @param {lunr.Vector} otherVector - The other vector to calculate the
|
696
899
|
* similarity with.
|
697
900
|
* @returns {Number}
|
698
901
|
*/
|
699
902
|
lunr.Vector.prototype.similarity = function (otherVector) {
|
700
|
-
return this.dot(otherVector) /
|
903
|
+
return this.dot(otherVector) / this.magnitude() || 0
|
701
904
|
}
|
702
905
|
|
703
906
|
/**
|
@@ -726,7 +929,7 @@ lunr.Vector.prototype.toJSON = function () {
|
|
726
929
|
/* eslint-disable */
|
727
930
|
/*!
|
728
931
|
* lunr.stemmer
|
729
|
-
* Copyright (C)
|
932
|
+
* Copyright (C) 2018 Oliver Nightingale
|
730
933
|
* Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
|
731
934
|
*/
|
732
935
|
|
@@ -739,6 +942,7 @@ lunr.Vector.prototype.toJSON = function () {
|
|
739
942
|
* @param {lunr.Token} token - The string to stem
|
740
943
|
* @returns {lunr.Token}
|
741
944
|
* @see {@link lunr.Pipeline}
|
945
|
+
* @function
|
742
946
|
*/
|
743
947
|
lunr.stemmer = (function(){
|
744
948
|
var step2list = {
|
@@ -947,7 +1151,7 @@ lunr.stemmer = (function(){
|
|
947
1151
|
lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer')
|
948
1152
|
/*!
|
949
1153
|
* lunr.stopWordFilter
|
950
|
-
* Copyright (C)
|
1154
|
+
* Copyright (C) 2018 Oliver Nightingale
|
951
1155
|
*/
|
952
1156
|
|
953
1157
|
/**
|
@@ -957,6 +1161,7 @@ lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer')
|
|
957
1161
|
* The built in lunr.stopWordFilter is built using this generator and can be used
|
958
1162
|
* to generate custom stopWordFilters for applications or non English languages.
|
959
1163
|
*
|
1164
|
+
* @function
|
960
1165
|
* @param {Array} token The token to pass through the filter
|
961
1166
|
* @returns {lunr.PipelineFunction}
|
962
1167
|
* @see lunr.Pipeline
|
@@ -980,6 +1185,7 @@ lunr.generateStopWordFilter = function (stopWords) {
|
|
980
1185
|
* This is intended to be used in the Pipeline. If the token does not pass the
|
981
1186
|
* filter then undefined will be returned.
|
982
1187
|
*
|
1188
|
+
* @function
|
983
1189
|
* @implements {lunr.PipelineFunction}
|
984
1190
|
* @params {lunr.Token} token - A token to check for being a stop word.
|
985
1191
|
* @returns {lunr.Token}
|
@@ -1110,7 +1316,7 @@ lunr.stopWordFilter = lunr.generateStopWordFilter([
|
|
1110
1316
|
lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter')
|
1111
1317
|
/*!
|
1112
1318
|
* lunr.trimmer
|
1113
|
-
* Copyright (C)
|
1319
|
+
* Copyright (C) 2018 Oliver Nightingale
|
1114
1320
|
*/
|
1115
1321
|
|
1116
1322
|
/**
|
@@ -1137,7 +1343,7 @@ lunr.trimmer = function (token) {
|
|
1137
1343
|
lunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer')
|
1138
1344
|
/*!
|
1139
1345
|
* lunr.TokenSet
|
1140
|
-
* Copyright (C)
|
1346
|
+
* Copyright (C) 2018 Oliver Nightingale
|
1141
1347
|
*/
|
1142
1348
|
|
1143
1349
|
/**
|
@@ -1379,14 +1585,13 @@ lunr.TokenSet.fromFuzzyString = function (str, editDistance) {
|
|
1379
1585
|
*/
|
1380
1586
|
lunr.TokenSet.fromString = function (str) {
|
1381
1587
|
var node = new lunr.TokenSet,
|
1382
|
-
root = node
|
1383
|
-
wildcardFound = false
|
1588
|
+
root = node
|
1384
1589
|
|
1385
1590
|
/*
|
1386
1591
|
* Iterates through all characters within the passed string
|
1387
1592
|
* appending a node for each character.
|
1388
1593
|
*
|
1389
|
-
*
|
1594
|
+
* When a wildcard character is found then a self
|
1390
1595
|
* referencing edge is introduced to continually match
|
1391
1596
|
* any number of any characters.
|
1392
1597
|
*/
|
@@ -1395,7 +1600,6 @@ lunr.TokenSet.fromString = function (str) {
|
|
1395
1600
|
final = (i == len - 1)
|
1396
1601
|
|
1397
1602
|
if (char == "*") {
|
1398
|
-
wildcardFound = true
|
1399
1603
|
node.edges[char] = node
|
1400
1604
|
node.final = final
|
1401
1605
|
|
@@ -1405,11 +1609,6 @@ lunr.TokenSet.fromString = function (str) {
|
|
1405
1609
|
|
1406
1610
|
node.edges[char] = next
|
1407
1611
|
node = next
|
1408
|
-
|
1409
|
-
// TODO: is this needed anymore?
|
1410
|
-
if (wildcardFound) {
|
1411
|
-
node.edges["*"] = root
|
1412
|
-
}
|
1413
1612
|
}
|
1414
1613
|
}
|
1415
1614
|
|
@@ -1436,6 +1635,11 @@ lunr.TokenSet.prototype.toArray = function () {
|
|
1436
1635
|
len = edges.length
|
1437
1636
|
|
1438
1637
|
if (frame.node.final) {
|
1638
|
+
/* In Safari, at this point the prefix is sometimes corrupted, see:
|
1639
|
+
* https://github.com/olivernn/lunr.js/issues/279 Calling any
|
1640
|
+
* String.prototype method forces Safari to "cast" this string to what
|
1641
|
+
* it's supposed to be, fixing the bug. */
|
1642
|
+
frame.prefix.charAt(0)
|
1439
1643
|
words.push(frame.prefix)
|
1440
1644
|
}
|
1441
1645
|
|
@@ -1632,7 +1836,7 @@ lunr.TokenSet.Builder.prototype.minimize = function (downTo) {
|
|
1632
1836
|
}
|
1633
1837
|
/*!
|
1634
1838
|
* lunr.Index
|
1635
|
-
* Copyright (C)
|
1839
|
+
* Copyright (C) 2018 Oliver Nightingale
|
1636
1840
|
*/
|
1637
1841
|
|
1638
1842
|
/**
|
@@ -1646,7 +1850,7 @@ lunr.TokenSet.Builder.prototype.minimize = function (downTo) {
|
|
1646
1850
|
* @constructor
|
1647
1851
|
* @param {Object} attrs - The attributes of the built search index.
|
1648
1852
|
* @param {Object} attrs.invertedIndex - An index of term/field to document reference.
|
1649
|
-
* @param {Object<string, lunr.Vector>} attrs.
|
1853
|
+
* @param {Object<string, lunr.Vector>} attrs.fieldVectors - Field vectors
|
1650
1854
|
* @param {lunr.TokenSet} attrs.tokenSet - An set of all corpus tokens.
|
1651
1855
|
* @param {string[]} attrs.fields - The names of indexed document fields.
|
1652
1856
|
* @param {lunr.Pipeline} attrs.pipeline - The pipeline to use for search terms.
|
@@ -1692,6 +1896,12 @@ lunr.Index = function (attrs) {
|
|
1692
1896
|
* to provide fuzzy matching, e.g. 'hello~2' will match documents with hello with an edit distance of 2.
|
1693
1897
|
* Avoid large values for edit distance to improve query performance.
|
1694
1898
|
*
|
1899
|
+
* Each term also supports a presence modifier. By default a term's presence in document is optional, however
|
1900
|
+
* this can be changed to either required or prohibited. For a term's presence to be required in a document the
|
1901
|
+
* term should be prefixed with a '+', e.g. `+foo bar` is a search for documents that must contain 'foo' and
|
1902
|
+
* optionally contain 'bar'. Conversely a leading '-' sets the terms presence to prohibited, i.e. it must not
|
1903
|
+
* appear in a document, e.g. `-foo bar` is a search for documents that do not contain 'foo' but may contain 'bar'.
|
1904
|
+
*
|
1695
1905
|
* To escape special characters the backslash character '\' can be used, this allows searches to include
|
1696
1906
|
* characters that would normally be considered modifiers, e.g. `foo\~2` will search for a term "foo~2" instead
|
1697
1907
|
* of attempting to apply a boost of 2 to the search term "foo".
|
@@ -1707,13 +1917,16 @@ lunr.Index = function (attrs) {
|
|
1707
1917
|
* hello^10
|
1708
1918
|
* @example <caption>term with an edit distance of 2</caption>
|
1709
1919
|
* hello~2
|
1920
|
+
* @example <caption>terms with presence modifiers</caption>
|
1921
|
+
* -foo +bar baz
|
1710
1922
|
*/
|
1711
1923
|
|
1712
1924
|
/**
|
1713
1925
|
* Performs a search against the index using lunr query syntax.
|
1714
1926
|
*
|
1715
1927
|
* Results will be returned sorted by their score, the most relevant results
|
1716
|
-
* will be returned first.
|
1928
|
+
* will be returned first. For details on how the score is calculated, please see
|
1929
|
+
* the {@link https://lunrjs.com/guides/searching.html#scoring|guide}.
|
1717
1930
|
*
|
1718
1931
|
* For more programmatic querying use lunr.Index#query.
|
1719
1932
|
*
|
@@ -1764,7 +1977,18 @@ lunr.Index.prototype.query = function (fn) {
|
|
1764
1977
|
var query = new lunr.Query(this.fields),
|
1765
1978
|
matchingFields = Object.create(null),
|
1766
1979
|
queryVectors = Object.create(null),
|
1767
|
-
termFieldCache = Object.create(null)
|
1980
|
+
termFieldCache = Object.create(null),
|
1981
|
+
requiredMatches = Object.create(null),
|
1982
|
+
prohibitedMatches = Object.create(null)
|
1983
|
+
|
1984
|
+
/*
|
1985
|
+
* To support field level boosts a query vector is created per
|
1986
|
+
* field. An empty vector is eagerly created to support negated
|
1987
|
+
* queries.
|
1988
|
+
*/
|
1989
|
+
for (var i = 0; i < this.fields.length; i++) {
|
1990
|
+
queryVectors[this.fields[i]] = new lunr.Vector
|
1991
|
+
}
|
1768
1992
|
|
1769
1993
|
fn.call(query, query)
|
1770
1994
|
|
@@ -1778,10 +2002,13 @@ lunr.Index.prototype.query = function (fn) {
|
|
1778
2002
|
* for a single query term.
|
1779
2003
|
*/
|
1780
2004
|
var clause = query.clauses[i],
|
1781
|
-
terms = null
|
2005
|
+
terms = null,
|
2006
|
+
clauseMatches = lunr.Set.complete
|
1782
2007
|
|
1783
2008
|
if (clause.usePipeline) {
|
1784
|
-
terms = this.pipeline.runString(clause.term
|
2009
|
+
terms = this.pipeline.runString(clause.term, {
|
2010
|
+
fields: clause.fields
|
2011
|
+
})
|
1785
2012
|
} else {
|
1786
2013
|
terms = [clause.term]
|
1787
2014
|
}
|
@@ -1805,6 +2032,21 @@ lunr.Index.prototype.query = function (fn) {
|
|
1805
2032
|
var termTokenSet = lunr.TokenSet.fromClause(clause),
|
1806
2033
|
expandedTerms = this.tokenSet.intersect(termTokenSet).toArray()
|
1807
2034
|
|
2035
|
+
/*
|
2036
|
+
* If a term marked as required does not exist in the tokenSet it is
|
2037
|
+
* impossible for the search to return any matches. We set all the field
|
2038
|
+
* scoped required matches set to empty and stop examining any further
|
2039
|
+
* clauses.
|
2040
|
+
*/
|
2041
|
+
if (expandedTerms.length === 0 && clause.presence === lunr.Query.presence.REQUIRED) {
|
2042
|
+
for (var k = 0; k < clause.fields.length; k++) {
|
2043
|
+
var field = clause.fields[k]
|
2044
|
+
requiredMatches[field] = lunr.Set.empty
|
2045
|
+
}
|
2046
|
+
|
2047
|
+
break
|
2048
|
+
}
|
2049
|
+
|
1808
2050
|
for (var j = 0; j < expandedTerms.length; j++) {
|
1809
2051
|
/*
|
1810
2052
|
* For each term get the posting and termIndex, this is required for
|
@@ -1826,26 +2068,50 @@ lunr.Index.prototype.query = function (fn) {
|
|
1826
2068
|
var field = clause.fields[k],
|
1827
2069
|
fieldPosting = posting[field],
|
1828
2070
|
matchingDocumentRefs = Object.keys(fieldPosting),
|
1829
|
-
termField = expandedTerm + "/" + field
|
2071
|
+
termField = expandedTerm + "/" + field,
|
2072
|
+
matchingDocumentsSet = new lunr.Set(matchingDocumentRefs)
|
1830
2073
|
|
1831
2074
|
/*
|
1832
|
-
*
|
1833
|
-
*
|
1834
|
-
* the term and a unit value with the appropriate boost applied.
|
2075
|
+
* if the presence of this term is required ensure that the matching
|
2076
|
+
* documents are added to the set of required matches for this clause.
|
1835
2077
|
*
|
1836
|
-
* If the query vector for this field does not exist yet it needs
|
1837
|
-
* to be created.
|
1838
2078
|
*/
|
1839
|
-
if (
|
1840
|
-
|
2079
|
+
if (clause.presence == lunr.Query.presence.REQUIRED) {
|
2080
|
+
clauseMatches = clauseMatches.union(matchingDocumentsSet)
|
2081
|
+
|
2082
|
+
if (requiredMatches[field] === undefined) {
|
2083
|
+
requiredMatches[field] = lunr.Set.complete
|
2084
|
+
}
|
2085
|
+
}
|
2086
|
+
|
2087
|
+
/*
|
2088
|
+
* if the presence of this term is prohibited ensure that the matching
|
2089
|
+
* documents are added to the set of prohibited matches for this field,
|
2090
|
+
* creating that set if it does not yet exist.
|
2091
|
+
*/
|
2092
|
+
if (clause.presence == lunr.Query.presence.PROHIBITED) {
|
2093
|
+
if (prohibitedMatches[field] === undefined) {
|
2094
|
+
prohibitedMatches[field] = lunr.Set.empty
|
2095
|
+
}
|
2096
|
+
|
2097
|
+
prohibitedMatches[field] = prohibitedMatches[field].union(matchingDocumentsSet)
|
2098
|
+
|
2099
|
+
/*
|
2100
|
+
* Prohibited matches should not be part of the query vector used for
|
2101
|
+
* similarity scoring and no metadata should be extracted so we continue
|
2102
|
+
* to the next field
|
2103
|
+
*/
|
2104
|
+
continue
|
1841
2105
|
}
|
1842
2106
|
|
1843
2107
|
/*
|
2108
|
+
* The query field vector is populated using the termIndex found for
|
2109
|
+
* the term and a unit value with the appropriate boost applied.
|
1844
2110
|
* Using upsert because there could already be an entry in the vector
|
1845
2111
|
* for the term we are working with. In that case we just add the scores
|
1846
2112
|
* together.
|
1847
2113
|
*/
|
1848
|
-
queryVectors[field].upsert(termIndex,
|
2114
|
+
queryVectors[field].upsert(termIndex, clause.boost, function (a, b) { return a + b })
|
1849
2115
|
|
1850
2116
|
/**
|
1851
2117
|
* If we've already seen this term, field combo then we've already collected
|
@@ -1879,12 +2145,65 @@ lunr.Index.prototype.query = function (fn) {
|
|
1879
2145
|
}
|
1880
2146
|
}
|
1881
2147
|
}
|
2148
|
+
|
2149
|
+
/**
|
2150
|
+
* If the presence was required we need to update the requiredMatches field sets.
|
2151
|
+
* We do this after all fields for the term have collected their matches because
|
2152
|
+
* the clause terms presence is required in _any_ of the fields not _all_ of the
|
2153
|
+
* fields.
|
2154
|
+
*/
|
2155
|
+
if (clause.presence === lunr.Query.presence.REQUIRED) {
|
2156
|
+
for (var k = 0; k < clause.fields.length; k++) {
|
2157
|
+
var field = clause.fields[k]
|
2158
|
+
requiredMatches[field] = requiredMatches[field].intersect(clauseMatches)
|
2159
|
+
}
|
2160
|
+
}
|
2161
|
+
}
|
2162
|
+
|
2163
|
+
/**
|
2164
|
+
* Need to combine the field scoped required and prohibited
|
2165
|
+
* matching documents into a global set of required and prohibited
|
2166
|
+
* matches
|
2167
|
+
*/
|
2168
|
+
var allRequiredMatches = lunr.Set.complete,
|
2169
|
+
allProhibitedMatches = lunr.Set.empty
|
2170
|
+
|
2171
|
+
for (var i = 0; i < this.fields.length; i++) {
|
2172
|
+
var field = this.fields[i]
|
2173
|
+
|
2174
|
+
if (requiredMatches[field]) {
|
2175
|
+
allRequiredMatches = allRequiredMatches.intersect(requiredMatches[field])
|
2176
|
+
}
|
2177
|
+
|
2178
|
+
if (prohibitedMatches[field]) {
|
2179
|
+
allProhibitedMatches = allProhibitedMatches.union(prohibitedMatches[field])
|
2180
|
+
}
|
1882
2181
|
}
|
1883
2182
|
|
1884
2183
|
var matchingFieldRefs = Object.keys(matchingFields),
|
1885
2184
|
results = [],
|
1886
2185
|
matches = Object.create(null)
|
1887
2186
|
|
2187
|
+
/*
|
2188
|
+
* If the query is negated (contains only prohibited terms)
|
2189
|
+
* we need to get _all_ fieldRefs currently existing in the
|
2190
|
+
* index. This is only done when we know that the query is
|
2191
|
+
* entirely prohibited terms to avoid any cost of getting all
|
2192
|
+
* fieldRefs unnecessarily.
|
2193
|
+
*
|
2194
|
+
* Additionally, blank MatchData must be created to correctly
|
2195
|
+
* populate the results.
|
2196
|
+
*/
|
2197
|
+
if (query.isNegated()) {
|
2198
|
+
matchingFieldRefs = Object.keys(this.fieldVectors)
|
2199
|
+
|
2200
|
+
for (var i = 0; i < matchingFieldRefs.length; i++) {
|
2201
|
+
var matchingFieldRef = matchingFieldRefs[i]
|
2202
|
+
var fieldRef = lunr.FieldRef.fromString(matchingFieldRef)
|
2203
|
+
matchingFields[matchingFieldRef] = new lunr.MatchData
|
2204
|
+
}
|
2205
|
+
}
|
2206
|
+
|
1888
2207
|
for (var i = 0; i < matchingFieldRefs.length; i++) {
|
1889
2208
|
/*
|
1890
2209
|
* Currently we have document fields that match the query, but we
|
@@ -1895,8 +2214,17 @@ lunr.Index.prototype.query = function (fn) {
|
|
1895
2214
|
* above, and combined into a final document score using addition.
|
1896
2215
|
*/
|
1897
2216
|
var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]),
|
1898
|
-
docRef = fieldRef.docRef
|
1899
|
-
|
2217
|
+
docRef = fieldRef.docRef
|
2218
|
+
|
2219
|
+
if (!allRequiredMatches.contains(docRef)) {
|
2220
|
+
continue
|
2221
|
+
}
|
2222
|
+
|
2223
|
+
if (allProhibitedMatches.contains(docRef)) {
|
2224
|
+
continue
|
2225
|
+
}
|
2226
|
+
|
2227
|
+
var fieldVector = this.fieldVectors[fieldRef],
|
1900
2228
|
score = queryVectors[fieldRef.fieldName].similarity(fieldVector),
|
1901
2229
|
docMatch
|
1902
2230
|
|
@@ -2000,7 +2328,7 @@ lunr.Index.load = function (serializedIndex) {
|
|
2000
2328
|
}
|
2001
2329
|
/*!
|
2002
2330
|
* lunr.Builder
|
2003
|
-
* Copyright (C)
|
2331
|
+
* Copyright (C) 2018 Oliver Nightingale
|
2004
2332
|
*/
|
2005
2333
|
|
2006
2334
|
/**
|
@@ -2029,7 +2357,8 @@ lunr.Index.load = function (serializedIndex) {
|
|
2029
2357
|
*/
|
2030
2358
|
lunr.Builder = function () {
|
2031
2359
|
this._ref = "id"
|
2032
|
-
this._fields =
|
2360
|
+
this._fields = Object.create(null)
|
2361
|
+
this._documents = Object.create(null)
|
2033
2362
|
this.invertedIndex = Object.create(null)
|
2034
2363
|
this.fieldTermFrequencies = {}
|
2035
2364
|
this.fieldLengths = {}
|
@@ -2059,6 +2388,20 @@ lunr.Builder.prototype.ref = function (ref) {
|
|
2059
2388
|
this._ref = ref
|
2060
2389
|
}
|
2061
2390
|
|
2391
|
+
/**
|
2392
|
+
* A function that is used to extract a field from a document.
|
2393
|
+
*
|
2394
|
+
* Lunr expects a field to be at the top level of a document, if however the field
|
2395
|
+
* is deeply nested within a document an extractor function can be used to extract
|
2396
|
+
* the right field for indexing.
|
2397
|
+
*
|
2398
|
+
* @callback fieldExtractor
|
2399
|
+
* @param {object} doc - The document being added to the index.
|
2400
|
+
* @returns {?(string|object|object[])} obj - The object that will be indexed for this field.
|
2401
|
+
* @example <caption>Extracting a nested field</caption>
|
2402
|
+
* function (doc) { return doc.nested.field }
|
2403
|
+
*/
|
2404
|
+
|
2062
2405
|
/**
|
2063
2406
|
* Adds a field to the list of document fields that will be indexed. Every document being
|
2064
2407
|
* indexed should have this field. Null values for this field in indexed documents will
|
@@ -2067,10 +2410,22 @@ lunr.Builder.prototype.ref = function (ref) {
|
|
2067
2410
|
* All fields should be added before adding documents to the index. Adding fields after
|
2068
2411
|
* a document has been indexed will have no effect on already indexed documents.
|
2069
2412
|
*
|
2070
|
-
*
|
2413
|
+
* Fields can be boosted at build time. This allows terms within that field to have more
|
2414
|
+
* importance when ranking search results. Use a field boost to specify that matches within
|
2415
|
+
* one field are more important than other fields.
|
2416
|
+
*
|
2417
|
+
* @param {string} fieldName - The name of a field to index in all documents.
|
2418
|
+
* @param {object} attributes - Optional attributes associated with this field.
|
2419
|
+
* @param {number} [attributes.boost=1] - Boost applied to all terms within this field.
|
2420
|
+
* @param {fieldExtractor} [attributes.extractor] - Function to extract a field from a document.
|
2421
|
+
* @throws {RangeError} fieldName cannot contain unsupported characters '/'
|
2071
2422
|
*/
|
2072
|
-
lunr.Builder.prototype.field = function (
|
2073
|
-
|
2423
|
+
lunr.Builder.prototype.field = function (fieldName, attributes) {
|
2424
|
+
if (/\//.test(fieldName)) {
|
2425
|
+
throw new RangeError ("Field '" + fieldName + "' contains illegal character '/'")
|
2426
|
+
}
|
2427
|
+
|
2428
|
+
this._fields[fieldName] = attributes || {}
|
2074
2429
|
}
|
2075
2430
|
|
2076
2431
|
/**
|
@@ -2112,17 +2467,27 @@ lunr.Builder.prototype.k1 = function (number) {
|
|
2112
2467
|
* it should have all fields defined for indexing, though null or undefined values will not
|
2113
2468
|
* cause errors.
|
2114
2469
|
*
|
2470
|
+
* Entire documents can be boosted at build time. Applying a boost to a document indicates that
|
2471
|
+
* this document should rank higher in search results than other documents.
|
2472
|
+
*
|
2115
2473
|
* @param {object} doc - The document to add to the index.
|
2474
|
+
* @param {object} attributes - Optional attributes associated with this document.
|
2475
|
+
* @param {number} [attributes.boost=1] - Boost applied to all terms within this document.
|
2116
2476
|
*/
|
2117
|
-
lunr.Builder.prototype.add = function (doc) {
|
2118
|
-
var docRef = doc[this._ref]
|
2477
|
+
lunr.Builder.prototype.add = function (doc, attributes) {
|
2478
|
+
var docRef = doc[this._ref],
|
2479
|
+
fields = Object.keys(this._fields)
|
2119
2480
|
|
2481
|
+
this._documents[docRef] = attributes || {}
|
2120
2482
|
this.documentCount += 1
|
2121
2483
|
|
2122
|
-
for (var i = 0; i <
|
2123
|
-
var fieldName =
|
2124
|
-
|
2125
|
-
|
2484
|
+
for (var i = 0; i < fields.length; i++) {
|
2485
|
+
var fieldName = fields[i],
|
2486
|
+
extractor = this._fields[fieldName].extractor,
|
2487
|
+
field = extractor ? extractor(doc) : doc[fieldName],
|
2488
|
+
tokens = this.tokenizer(field, {
|
2489
|
+
fields: [fieldName]
|
2490
|
+
}),
|
2126
2491
|
terms = this.pipeline.run(tokens),
|
2127
2492
|
fieldRef = new lunr.FieldRef (docRef, fieldName),
|
2128
2493
|
fieldTerms = Object.create(null)
|
@@ -2150,8 +2515,8 @@ lunr.Builder.prototype.add = function (doc) {
|
|
2150
2515
|
posting["_index"] = this.termIndex
|
2151
2516
|
this.termIndex += 1
|
2152
2517
|
|
2153
|
-
for (var k = 0; k <
|
2154
|
-
posting[
|
2518
|
+
for (var k = 0; k < fields.length; k++) {
|
2519
|
+
posting[fields[k]] = Object.create(null)
|
2155
2520
|
}
|
2156
2521
|
|
2157
2522
|
this.invertedIndex[term] = posting
|
@@ -2202,9 +2567,11 @@ lunr.Builder.prototype.calculateAverageFieldLengths = function () {
|
|
2202
2567
|
accumulator[field] += this.fieldLengths[fieldRef]
|
2203
2568
|
}
|
2204
2569
|
|
2205
|
-
|
2206
|
-
|
2207
|
-
|
2570
|
+
var fields = Object.keys(this._fields)
|
2571
|
+
|
2572
|
+
for (var i = 0; i < fields.length; i++) {
|
2573
|
+
var fieldName = fields[i]
|
2574
|
+
accumulator[fieldName] = accumulator[fieldName] / documentsWithField[fieldName]
|
2208
2575
|
}
|
2209
2576
|
|
2210
2577
|
this.averageFieldLength = accumulator
|
@@ -2223,13 +2590,17 @@ lunr.Builder.prototype.createFieldVectors = function () {
|
|
2223
2590
|
|
2224
2591
|
for (var i = 0; i < fieldRefsLength; i++) {
|
2225
2592
|
var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]),
|
2226
|
-
|
2593
|
+
fieldName = fieldRef.fieldName,
|
2227
2594
|
fieldLength = this.fieldLengths[fieldRef],
|
2228
2595
|
fieldVector = new lunr.Vector,
|
2229
2596
|
termFrequencies = this.fieldTermFrequencies[fieldRef],
|
2230
2597
|
terms = Object.keys(termFrequencies),
|
2231
2598
|
termsLength = terms.length
|
2232
2599
|
|
2600
|
+
|
2601
|
+
var fieldBoost = this._fields[fieldName].boost || 1,
|
2602
|
+
docBoost = this._documents[fieldRef.docRef].boost || 1
|
2603
|
+
|
2233
2604
|
for (var j = 0; j < termsLength; j++) {
|
2234
2605
|
var term = terms[j],
|
2235
2606
|
tf = termFrequencies[term],
|
@@ -2243,7 +2614,9 @@ lunr.Builder.prototype.createFieldVectors = function () {
|
|
2243
2614
|
idf = termIdfCache[term]
|
2244
2615
|
}
|
2245
2616
|
|
2246
|
-
score = idf * ((this._k1 + 1) * tf) / (this._k1 * (1 - this._b + this._b * (fieldLength / this.averageFieldLength[
|
2617
|
+
score = idf * ((this._k1 + 1) * tf) / (this._k1 * (1 - this._b + this._b * (fieldLength / this.averageFieldLength[fieldName])) + tf)
|
2618
|
+
score *= fieldBoost
|
2619
|
+
score *= docBoost
|
2247
2620
|
scoreWithPrecision = Math.round(score * 1000) / 1000
|
2248
2621
|
// Converts 1.23456789 to 1.234.
|
2249
2622
|
// Reducing the precision so that the vectors take up less
|
@@ -2289,7 +2662,7 @@ lunr.Builder.prototype.build = function () {
|
|
2289
2662
|
invertedIndex: this.invertedIndex,
|
2290
2663
|
fieldVectors: this.fieldVectors,
|
2291
2664
|
tokenSet: this.tokenSet,
|
2292
|
-
fields: this._fields,
|
2665
|
+
fields: Object.keys(this._fields),
|
2293
2666
|
pipeline: this.searchPipeline
|
2294
2667
|
})
|
2295
2668
|
}
|
@@ -2327,7 +2700,7 @@ lunr.Builder.prototype.use = function (fn) {
|
|
2327
2700
|
*/
|
2328
2701
|
lunr.MatchData = function (term, field, metadata) {
|
2329
2702
|
var clonedMetadata = Object.create(null),
|
2330
|
-
metadataKeys = Object.keys(metadata)
|
2703
|
+
metadataKeys = Object.keys(metadata || {})
|
2331
2704
|
|
2332
2705
|
// Cloning the metadata to prevent the original
|
2333
2706
|
// being mutated during match data combination.
|
@@ -2340,8 +2713,11 @@ lunr.MatchData = function (term, field, metadata) {
|
|
2340
2713
|
}
|
2341
2714
|
|
2342
2715
|
this.metadata = Object.create(null)
|
2343
|
-
|
2344
|
-
|
2716
|
+
|
2717
|
+
if (term !== undefined) {
|
2718
|
+
this.metadata[term] = Object.create(null)
|
2719
|
+
this.metadata[term][field] = clonedMetadata
|
2720
|
+
}
|
2345
2721
|
}
|
2346
2722
|
|
2347
2723
|
/**
|
@@ -2456,11 +2832,42 @@ lunr.Query = function (allFields) {
|
|
2456
2832
|
* wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING
|
2457
2833
|
* })
|
2458
2834
|
*/
|
2835
|
+
|
2459
2836
|
lunr.Query.wildcard = new String ("*")
|
2460
2837
|
lunr.Query.wildcard.NONE = 0
|
2461
2838
|
lunr.Query.wildcard.LEADING = 1
|
2462
2839
|
lunr.Query.wildcard.TRAILING = 2
|
2463
2840
|
|
2841
|
+
/**
|
2842
|
+
* Constants for indicating what kind of presence a term must have in matching documents.
|
2843
|
+
*
|
2844
|
+
* @constant
|
2845
|
+
* @enum {number}
|
2846
|
+
* @see lunr.Query~Clause
|
2847
|
+
* @see lunr.Query#clause
|
2848
|
+
* @see lunr.Query#term
|
2849
|
+
* @example <caption>query term with required presence</caption>
|
2850
|
+
* query.term('foo', { presence: lunr.Query.presence.REQUIRED })
|
2851
|
+
*/
|
2852
|
+
lunr.Query.presence = {
|
2853
|
+
/**
|
2854
|
+
* Term's presence in a document is optional, this is the default value.
|
2855
|
+
*/
|
2856
|
+
OPTIONAL: 1,
|
2857
|
+
|
2858
|
+
/**
|
2859
|
+
* Term's presence in a document is required, documents that do not contain
|
2860
|
+
* this term will not be returned.
|
2861
|
+
*/
|
2862
|
+
REQUIRED: 2,
|
2863
|
+
|
2864
|
+
/**
|
2865
|
+
* Term's presence in a document is prohibited, documents that do contain
|
2866
|
+
* this term will not be returned.
|
2867
|
+
*/
|
2868
|
+
PROHIBITED: 3
|
2869
|
+
}
|
2870
|
+
|
2464
2871
|
/**
|
2465
2872
|
* A single clause in a {@link lunr.Query} contains a term and details on how to
|
2466
2873
|
* match that term against a {@link lunr.Index}.
|
@@ -2470,7 +2877,8 @@ lunr.Query.wildcard.TRAILING = 2
|
|
2470
2877
|
* @property {number} [boost=1] - Any boost that should be applied when matching this clause.
|
2471
2878
|
* @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be.
|
2472
2879
|
* @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline.
|
2473
|
-
* @property {number} [wildcard=
|
2880
|
+
* @property {number} [wildcard=lunr.Query.wildcard.NONE] - Whether the term should have wildcards appended or prepended.
|
2881
|
+
* @property {number} [presence=lunr.Query.presence.OPTIONAL] - The terms presence in any matching documents.
|
2474
2882
|
*/
|
2475
2883
|
|
2476
2884
|
/**
|
@@ -2508,17 +2916,44 @@ lunr.Query.prototype.clause = function (clause) {
|
|
2508
2916
|
clause.term = "" + clause.term + "*"
|
2509
2917
|
}
|
2510
2918
|
|
2919
|
+
if (!('presence' in clause)) {
|
2920
|
+
clause.presence = lunr.Query.presence.OPTIONAL
|
2921
|
+
}
|
2922
|
+
|
2511
2923
|
this.clauses.push(clause)
|
2512
2924
|
|
2513
2925
|
return this
|
2514
2926
|
}
|
2515
2927
|
|
2928
|
+
/**
|
2929
|
+
* A negated query is one in which every clause has a presence of
|
2930
|
+
* prohibited. These queries require some special processing to return
|
2931
|
+
* the expected results.
|
2932
|
+
*
|
2933
|
+
* @returns boolean
|
2934
|
+
*/
|
2935
|
+
lunr.Query.prototype.isNegated = function () {
|
2936
|
+
for (var i = 0; i < this.clauses.length; i++) {
|
2937
|
+
if (this.clauses[i].presence != lunr.Query.presence.PROHIBITED) {
|
2938
|
+
return false
|
2939
|
+
}
|
2940
|
+
}
|
2941
|
+
|
2942
|
+
return true
|
2943
|
+
}
|
2944
|
+
|
2516
2945
|
/**
|
2517
2946
|
* Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause}
|
2518
2947
|
* to the list of clauses that make up this query.
|
2519
2948
|
*
|
2520
|
-
*
|
2521
|
-
*
|
2949
|
+
* The term is used as is, i.e. no tokenization will be performed by this method. Instead conversion
|
2950
|
+
* to a token or token-like string should be done before calling this method.
|
2951
|
+
*
|
2952
|
+
* The term will be converted to a string by calling `toString`. Multiple terms can be passed as an
|
2953
|
+
* array, each term in the array will share the same options.
|
2954
|
+
*
|
2955
|
+
* @param {object|object[]} term - The term(s) to add to the query.
|
2956
|
+
* @param {object} [options] - Any additional properties to add to the query clause.
|
2522
2957
|
* @returns {lunr.Query}
|
2523
2958
|
* @see lunr.Query#clause
|
2524
2959
|
* @see lunr.Query~Clause
|
@@ -2530,10 +2965,17 @@ lunr.Query.prototype.clause = function (clause) {
|
|
2530
2965
|
* boost: 10,
|
2531
2966
|
* wildcard: lunr.Query.wildcard.TRAILING
|
2532
2967
|
* })
|
2968
|
+
* @example <caption>using lunr.tokenizer to convert a string to tokens before using them as terms</caption>
|
2969
|
+
* query.term(lunr.tokenizer("foo bar"))
|
2533
2970
|
*/
|
2534
2971
|
lunr.Query.prototype.term = function (term, options) {
|
2972
|
+
if (Array.isArray(term)) {
|
2973
|
+
term.forEach(function (t) { this.term(t, lunr.utils.clone(options)) }, this)
|
2974
|
+
return this
|
2975
|
+
}
|
2976
|
+
|
2535
2977
|
var clause = options || {}
|
2536
|
-
clause.term = term
|
2978
|
+
clause.term = term.toString()
|
2537
2979
|
|
2538
2980
|
this.clause(clause)
|
2539
2981
|
|
@@ -2645,6 +3087,7 @@ lunr.QueryLexer.FIELD = 'FIELD'
|
|
2645
3087
|
lunr.QueryLexer.TERM = 'TERM'
|
2646
3088
|
lunr.QueryLexer.EDIT_DISTANCE = 'EDIT_DISTANCE'
|
2647
3089
|
lunr.QueryLexer.BOOST = 'BOOST'
|
3090
|
+
lunr.QueryLexer.PRESENCE = 'PRESENCE'
|
2648
3091
|
|
2649
3092
|
lunr.QueryLexer.lexField = function (lexer) {
|
2650
3093
|
lexer.backup()
|
@@ -2733,6 +3176,22 @@ lunr.QueryLexer.lexText = function (lexer) {
|
|
2733
3176
|
return lunr.QueryLexer.lexBoost
|
2734
3177
|
}
|
2735
3178
|
|
3179
|
+
// "+" indicates term presence is required
|
3180
|
+
// checking for length to ensure that only
|
3181
|
+
// leading "+" are considered
|
3182
|
+
if (char == "+" && lexer.width() === 1) {
|
3183
|
+
lexer.emit(lunr.QueryLexer.PRESENCE)
|
3184
|
+
return lunr.QueryLexer.lexText
|
3185
|
+
}
|
3186
|
+
|
3187
|
+
// "-" indicates term presence is prohibited
|
3188
|
+
// checking for length to ensure that only
|
3189
|
+
// leading "-" are considered
|
3190
|
+
if (char == "-" && lexer.width() === 1) {
|
3191
|
+
lexer.emit(lunr.QueryLexer.PRESENCE)
|
3192
|
+
return lunr.QueryLexer.lexText
|
3193
|
+
}
|
3194
|
+
|
2736
3195
|
if (char.match(lunr.QueryLexer.termSeparator)) {
|
2737
3196
|
return lunr.QueryLexer.lexTerm
|
2738
3197
|
}
|
@@ -2750,7 +3209,7 @@ lunr.QueryParser.prototype.parse = function () {
|
|
2750
3209
|
this.lexer.run()
|
2751
3210
|
this.lexemes = this.lexer.lexemes
|
2752
3211
|
|
2753
|
-
var state = lunr.QueryParser.
|
3212
|
+
var state = lunr.QueryParser.parseClause
|
2754
3213
|
|
2755
3214
|
while (state) {
|
2756
3215
|
state = state(this)
|
@@ -2775,7 +3234,7 @@ lunr.QueryParser.prototype.nextClause = function () {
|
|
2775
3234
|
this.currentClause = {}
|
2776
3235
|
}
|
2777
3236
|
|
2778
|
-
lunr.QueryParser.
|
3237
|
+
lunr.QueryParser.parseClause = function (parser) {
|
2779
3238
|
var lexeme = parser.peekLexeme()
|
2780
3239
|
|
2781
3240
|
if (lexeme == undefined) {
|
@@ -2783,6 +3242,8 @@ lunr.QueryParser.parseFieldOrTerm = function (parser) {
|
|
2783
3242
|
}
|
2784
3243
|
|
2785
3244
|
switch (lexeme.type) {
|
3245
|
+
case lunr.QueryLexer.PRESENCE:
|
3246
|
+
return lunr.QueryParser.parsePresence
|
2786
3247
|
case lunr.QueryLexer.FIELD:
|
2787
3248
|
return lunr.QueryParser.parseField
|
2788
3249
|
case lunr.QueryLexer.TERM:
|
@@ -2798,6 +3259,43 @@ lunr.QueryParser.parseFieldOrTerm = function (parser) {
|
|
2798
3259
|
}
|
2799
3260
|
}
|
2800
3261
|
|
3262
|
+
lunr.QueryParser.parsePresence = function (parser) {
|
3263
|
+
var lexeme = parser.consumeLexeme()
|
3264
|
+
|
3265
|
+
if (lexeme == undefined) {
|
3266
|
+
return
|
3267
|
+
}
|
3268
|
+
|
3269
|
+
switch (lexeme.str) {
|
3270
|
+
case "-":
|
3271
|
+
parser.currentClause.presence = lunr.Query.presence.PROHIBITED
|
3272
|
+
break
|
3273
|
+
case "+":
|
3274
|
+
parser.currentClause.presence = lunr.Query.presence.REQUIRED
|
3275
|
+
break
|
3276
|
+
default:
|
3277
|
+
var errorMessage = "unrecognised presence operator'" + lexeme.str + "'"
|
3278
|
+
throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)
|
3279
|
+
}
|
3280
|
+
|
3281
|
+
var nextLexeme = parser.peekLexeme()
|
3282
|
+
|
3283
|
+
if (nextLexeme == undefined) {
|
3284
|
+
var errorMessage = "expecting term or field, found nothing"
|
3285
|
+
throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)
|
3286
|
+
}
|
3287
|
+
|
3288
|
+
switch (nextLexeme.type) {
|
3289
|
+
case lunr.QueryLexer.FIELD:
|
3290
|
+
return lunr.QueryParser.parseField
|
3291
|
+
case lunr.QueryLexer.TERM:
|
3292
|
+
return lunr.QueryParser.parseTerm
|
3293
|
+
default:
|
3294
|
+
var errorMessage = "expecting term or field, found '" + nextLexeme.type + "'"
|
3295
|
+
throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
|
3296
|
+
}
|
3297
|
+
}
|
3298
|
+
|
2801
3299
|
lunr.QueryParser.parseField = function (parser) {
|
2802
3300
|
var lexeme = parser.consumeLexeme()
|
2803
3301
|
|
@@ -2861,6 +3359,9 @@ lunr.QueryParser.parseTerm = function (parser) {
|
|
2861
3359
|
return lunr.QueryParser.parseEditDistance
|
2862
3360
|
case lunr.QueryLexer.BOOST:
|
2863
3361
|
return lunr.QueryParser.parseBoost
|
3362
|
+
case lunr.QueryLexer.PRESENCE:
|
3363
|
+
parser.nextClause()
|
3364
|
+
return lunr.QueryParser.parsePresence
|
2864
3365
|
default:
|
2865
3366
|
var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'"
|
2866
3367
|
throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
|
@@ -2901,6 +3402,9 @@ lunr.QueryParser.parseEditDistance = function (parser) {
|
|
2901
3402
|
return lunr.QueryParser.parseEditDistance
|
2902
3403
|
case lunr.QueryLexer.BOOST:
|
2903
3404
|
return lunr.QueryParser.parseBoost
|
3405
|
+
case lunr.QueryLexer.PRESENCE:
|
3406
|
+
parser.nextClause()
|
3407
|
+
return lunr.QueryParser.parsePresence
|
2904
3408
|
default:
|
2905
3409
|
var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'"
|
2906
3410
|
throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
|
@@ -2941,6 +3445,9 @@ lunr.QueryParser.parseBoost = function (parser) {
|
|
2941
3445
|
return lunr.QueryParser.parseEditDistance
|
2942
3446
|
case lunr.QueryLexer.BOOST:
|
2943
3447
|
return lunr.QueryParser.parseBoost
|
3448
|
+
case lunr.QueryLexer.PRESENCE:
|
3449
|
+
parser.nextClause()
|
3450
|
+
return lunr.QueryParser.parsePresence
|
2944
3451
|
default:
|
2945
3452
|
var errorMessage = "Unexpected lexeme type '" + nextLexeme.type + "'"
|
2946
3453
|
throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)
|
@@ -2974,4 +3481,4 @@ lunr.QueryParser.parseBoost = function (parser) {
|
|
2974
3481
|
*/
|
2975
3482
|
return lunr
|
2976
3483
|
}))
|
2977
|
-
})();
|
3484
|
+
})();
|