minimal-mistakes-jekyll 4.8.0 → 4.8.1

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