Commit 452876af authored by Tariq Islam's avatar Tariq Islam

Merge pull request #239 from tripit/update

Bugfixes merged.
parents 7452f6b1 16dca4d0
FROM ubuntu:trusty FROM ubuntu:trusty
RUN apt-get update RUN apt-get update
RUN apt-get install -yq ruby ruby-dev build-essential RUN apt-get install -yq ruby ruby-dev build-essential git
RUN gem install --no-ri --no-rdoc bundler RUN gem install --no-ri --no-rdoc bundler
ADD Gemfile /app/Gemfile ADD Gemfile /app/Gemfile
ADD Gemfile.lock /app/Gemfile.lock ADD Gemfile.lock /app/Gemfile.lock
...@@ -9,4 +9,4 @@ RUN cd /app; bundle install ...@@ -9,4 +9,4 @@ RUN cd /app; bundle install
ADD . /app ADD . /app
EXPOSE 4567 EXPOSE 4567
WORKDIR /app WORKDIR /app
CMD ["bundle", "exec", "middleman", "server"] CMD ["bundle", "exec", "middleman", "server"]
\ No newline at end of file
# If you have OpenSSL installed, we recommend updating
# the following line to use "https"
source 'http://rubygems.org' source 'http://rubygems.org'
gem "rouge", "1.7.2" # Middleman
gem 'middleman', '~>3.3.10'
gem 'middleman-gh-pages', '~> 0.0.3'
gem 'middleman-syntax', '~> 2.0.0'
gem 'rouge', '~> 1.8.0'
gem 'redcarpet', '~> 3.2.2'
gem "middleman", "~>3.3.0" gem 'rake', '~> 10.4.2'
gem 'therubyracer', '~> 0.12.1', platforms: :ruby
# For syntax highlighting
gem "middleman-syntax"
# Plugin for middleman to generate Github pages
gem 'middleman-gh-pages'
# Live-reloading plugin
gem "middleman-livereload", "~> 3.3.0"
gem 'redcarpet', '~> 3.2.1'
# For faster file watcher updates on Windows:
gem "wdm", "~> 0.1.0", :platforms => [:mswin, :mingw]
# Cross-templating language block fix for Ruby 1.8
platforms :mri_18 do
gem "ruby18_source_location"
end
gem "rake", "~> 10.4.0"
gem 'therubyracer', :platforms => :ruby
\ No newline at end of file
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
activesupport (4.1.9) activesupport (4.1.10)
i18n (~> 0.6, >= 0.6.9) i18n (~> 0.6, >= 0.6.9)
json (~> 1.7, >= 1.7.7) json (~> 1.7, >= 1.7.7)
minitest (~> 5.1) minitest (~> 5.1)
...@@ -9,62 +9,57 @@ GEM ...@@ -9,62 +9,57 @@ GEM
tzinfo (~> 1.1) tzinfo (~> 1.1)
celluloid (0.16.0) celluloid (0.16.0)
timers (~> 4.0.0) timers (~> 4.0.0)
chunky_png (1.3.3) chunky_png (1.3.4)
coffee-script (2.3.0) coffee-script (2.4.1)
coffee-script-source coffee-script-source
execjs execjs
coffee-script-source (1.8.0) coffee-script-source (1.9.1.1)
compass (1.0.1) compass (1.0.3)
chunky_png (~> 1.2) chunky_png (~> 1.2)
compass-core (~> 1.0.1) compass-core (~> 1.0.2)
compass-import-once (~> 1.0.5) compass-import-once (~> 1.0.5)
rb-fsevent (>= 0.9.3) rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9) rb-inotify (>= 0.9)
sass (>= 3.3.13, < 3.5) sass (>= 3.3.13, < 3.5)
compass-core (1.0.1) compass-core (1.0.3)
multi_json (~> 1.0) multi_json (~> 1.0)
sass (>= 3.3.0, < 3.5) sass (>= 3.3.0, < 3.5)
compass-import-once (1.0.5) compass-import-once (1.0.5)
sass (>= 3.2, < 3.5) sass (>= 3.2, < 3.5)
em-websocket (0.5.1)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
erubis (2.7.0) erubis (2.7.0)
eventmachine (1.0.4) execjs (2.5.2)
execjs (2.2.2) ffi (1.9.8)
ffi (1.9.6)
haml (4.0.6) haml (4.0.6)
tilt tilt
hike (1.2.3) hike (1.2.3)
hitimes (1.2.2) hitimes (1.2.2)
hooks (0.4.0) hooks (0.4.0)
uber (~> 0.0.4) uber (~> 0.0.4)
http_parser.rb (0.6.0) i18n (0.7.0)
i18n (0.6.11)
json (1.8.2) json (1.8.2)
kramdown (1.5.0) kramdown (1.7.0)
libv8 (3.16.14.7) libv8 (3.16.14.7)
listen (2.8.5) listen (2.10.0)
celluloid (>= 0.15.2) celluloid (~> 0.16.0)
rb-fsevent (>= 0.9.3) rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9) rb-inotify (>= 0.9)
middleman (3.3.7) middleman (3.3.11)
coffee-script (~> 2.2) coffee-script (~> 2.2)
compass (>= 1.0.0, < 2.0.0) compass (>= 1.0.0, < 2.0.0)
compass-import-once (= 1.0.5) compass-import-once (= 1.0.5)
execjs (~> 2.0) execjs (~> 2.0)
haml (>= 4.0.5) haml (>= 4.0.5)
kramdown (~> 1.2) kramdown (~> 1.2)
middleman-core (= 3.3.7) middleman-core (= 3.3.11)
middleman-sprockets (>= 3.1.2) middleman-sprockets (>= 3.1.2)
sass (>= 3.4.0, < 4.0) sass (>= 3.4.0, < 4.0)
uglifier (~> 2.5) uglifier (~> 2.5)
middleman-core (3.3.7) middleman-core (3.3.11)
activesupport (~> 4.1.0) activesupport (~> 4.1.0)
bundler (~> 1.1) bundler (~> 1.1)
erubis erubis
hooks (~> 0.3) hooks (~> 0.3)
i18n (~> 0.6.9) i18n (~> 0.7.0)
listen (>= 2.7.9, < 3.0) listen (>= 2.7.9, < 3.0)
padrino-helpers (~> 0.12.3) padrino-helpers (~> 0.12.3)
rack (>= 1.4.5, < 2.0) rack (>= 1.4.5, < 2.0)
...@@ -73,11 +68,7 @@ GEM ...@@ -73,11 +68,7 @@ GEM
tilt (~> 1.4.1, < 2.0) tilt (~> 1.4.1, < 2.0)
middleman-gh-pages (0.0.3) middleman-gh-pages (0.0.3)
rake (> 0.9.3) rake (> 0.9.3)
middleman-livereload (3.3.4) middleman-sprockets (3.4.2)
em-websocket (~> 0.5.1)
middleman-core (~> 3.2)
rack-livereload (~> 0.3.15)
middleman-sprockets (3.4.1)
middleman-core (>= 3.3) middleman-core (>= 3.3)
sprockets (~> 2.12.1) sprockets (~> 2.12.1)
sprockets-helpers (~> 1.1.0) sprockets-helpers (~> 1.1.0)
...@@ -85,28 +76,25 @@ GEM ...@@ -85,28 +76,25 @@ GEM
middleman-syntax (2.0.0) middleman-syntax (2.0.0)
middleman-core (~> 3.2) middleman-core (~> 3.2)
rouge (~> 1.0) rouge (~> 1.0)
minitest (5.5.1) minitest (5.6.1)
multi_json (1.10.1) multi_json (1.11.0)
padrino-helpers (0.12.4) padrino-helpers (0.12.5)
i18n (~> 0.6, >= 0.6.7) i18n (~> 0.6, >= 0.6.7)
padrino-support (= 0.12.4) padrino-support (= 0.12.5)
tilt (~> 1.4.1) tilt (~> 1.4.1)
padrino-support (0.12.4) padrino-support (0.12.5)
activesupport (>= 3.1) activesupport (>= 3.1)
rack (1.6.0) rack (1.6.0)
rack-livereload (0.3.15)
rack
rack-test (0.6.3) rack-test (0.6.3)
rack (>= 1.0) rack (>= 1.0)
rake (10.4.2) rake (10.4.2)
rb-fsevent (0.9.4) rb-fsevent (0.9.4)
rb-inotify (0.9.5) rb-inotify (0.9.5)
ffi (>= 0.5.0) ffi (>= 0.5.0)
redcarpet (3.2.2) redcarpet (3.2.3)
ref (1.0.5) ref (1.0.5)
rouge (1.7.2) rouge (1.8.0)
ruby18_source_location (0.2) sass (3.4.13)
sass (3.4.9)
sprockets (2.12.3) sprockets (2.12.3)
hike (~> 1.2) hike (~> 1.2)
multi_json (~> 1.0) multi_json (~> 1.0)
...@@ -117,18 +105,18 @@ GEM ...@@ -117,18 +105,18 @@ GEM
sprockets-sass (1.3.1) sprockets-sass (1.3.1)
sprockets (~> 2.0) sprockets (~> 2.0)
tilt (~> 1.1) tilt (~> 1.1)
therubyracer (0.12.1) therubyracer (0.12.2)
libv8 (~> 3.16.14.0) libv8 (~> 3.16.14.0)
ref ref
thor (0.19.1) thor (0.19.1)
thread_safe (0.3.4) thread_safe (0.3.5)
tilt (1.4.1) tilt (1.4.1)
timers (4.0.1) timers (4.0.1)
hitimes hitimes
tzinfo (1.2.2) tzinfo (1.2.2)
thread_safe (~> 0.1) thread_safe (~> 0.1)
uber (0.0.13) uber (0.0.13)
uglifier (2.7.0) uglifier (2.7.1)
execjs (>= 0.3.0) execjs (>= 0.3.0)
json (>= 1.8.0) json (>= 1.8.0)
...@@ -136,13 +124,10 @@ PLATFORMS ...@@ -136,13 +124,10 @@ PLATFORMS
ruby ruby
DEPENDENCIES DEPENDENCIES
middleman (~> 3.3.0) middleman (~> 3.3.10)
middleman-gh-pages middleman-gh-pages (~> 0.0.3)
middleman-livereload (~> 3.3.0) middleman-syntax (~> 2.0.0)
middleman-syntax rake (~> 10.4.2)
rake (~> 10.4.0) redcarpet (~> 3.2.2)
redcarpet (~> 3.2.1) rouge (~> 1.8.0)
rouge (= 1.7.2) therubyracer (~> 0.12.1)
ruby18_source_location
therubyracer
wdm (~> 0.1.0)
...@@ -84,8 +84,10 @@ Examples of Slate in the Wild ...@@ -84,8 +84,10 @@ Examples of Slate in the Wild
* [Switch Payments Documentation](http://switchpayments.com/docs/) & [API](http://switchpayments.com/developers/) * [Switch Payments Documentation](http://switchpayments.com/docs/) & [API](http://switchpayments.com/developers/)
* [Coinbase API Reference](https://developers.coinbase.com/api) * [Coinbase API Reference](https://developers.coinbase.com/api)
* [Whispir.io API](https://whispir.github.io/api) * [Whispir.io API](https://whispir.github.io/api)
* [Nasa API](https://data.nasa.gov/developer/external/planetary/) * [NASA API](https://data.nasa.gov/developer/external/planetary/)
* [CardPay API](https://developers.cardpay.com/) * [CardPay API](https://developers.cardpay.com/)
* [IBM Cloudant](https://docs-testb.cloudant.com/content-review/_design/couchapp/index.html)
* [Bitrix basis components](http://bbc.bitrix.expert/)
(Feel free to add your site to this list in a pull request!) (Feel free to add your site to this list in a pull request!)
......
# Markdown
set :markdown_engine, :redcarpet
set :markdown,
fenced_code_blocks: true,
smartypants: true,
disable_indented_code_blocks: true,
prettify: true,
tables: true,
with_toc_data: true,
no_intra_emphasis: true
# Assets
set :css_dir, 'stylesheets' set :css_dir, 'stylesheets'
set :js_dir, 'javascripts' set :js_dir, 'javascripts'
set :images_dir, 'images' set :images_dir, 'images'
set :fonts_dir, 'fonts' set :fonts_dir, 'fonts'
set :markdown_engine, :redcarpet
set :markdown, :fenced_code_blocks => true, :smartypants => true, :disable_indented_code_blocks => true, :prettify => true, :tables => true, :with_toc_data => true, :no_intra_emphasis => true
# Activate the syntax highlighter # Activate the syntax highlighter
activate :syntax activate :syntax
# This is needed for Github pages, since they're hosted on a subdomain # Github pages require relative links
activate :relative_assets activate :relative_assets
set :relative_links, true set :relative_links, true
# Build-specific configuration # Build Configuration
configure :build do configure :build do
# For example, change the Compass output style for deployment
activate :minify_css activate :minify_css
# Minify Javascript on build
activate :minify_javascript activate :minify_javascript
# Enable cache buster
# activate :asset_hash
# Use relative URLs
# activate :relative_assets # activate :relative_assets
# activate :asset_hash
# Or use a different image path # activate :gzip
# set :http_prefix, "/Content/images/"
end end
source/images/logo.png

4.68 KB | W: | H:

source/images/logo.png

3.42 KB | W: | H:

source/images/logo.png
source/images/logo.png
source/images/logo.png
source/images/logo.png
  • 2-up
  • Swipe
  • Onion skin
source/images/navbar.png

2.72 KB | W: | H:

source/images/navbar.png

96 Bytes | W: | H:

source/images/navbar.png
source/images/navbar.png
source/images/navbar.png
source/images/navbar.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -55,7 +55,7 @@ Kittn expects for the API key to be included in all API requests to the server i ...@@ -55,7 +55,7 @@ Kittn expects for the API key to be included in all API requests to the server i
`Authorization: meowmeowmeow` `Authorization: meowmeowmeow`
<aside class="notice"> <aside class="notice">
You must replace `meowmeowmeow` with your personal API key. You must replace <code>meowmeowmeow</code> with your personal API key.
</aside> </aside>
# Kittens # Kittens
......
...@@ -14,6 +14,8 @@ License for the specific language governing permissions and limitations ...@@ -14,6 +14,8 @@ License for the specific language governing permissions and limitations
under the License. under the License.
*/ */
(function (global) { (function (global) {
'use strict';
var languages = []; var languages = [];
global.setupLanguages = setupLanguages; global.setupLanguages = setupLanguages;
...@@ -26,9 +28,9 @@ under the License. ...@@ -26,9 +28,9 @@ under the License.
$(".lang-selector a").removeClass('active'); $(".lang-selector a").removeClass('active');
$(".lang-selector a[data-language-name='" + language + "']").addClass('active'); $(".lang-selector a[data-language-name='" + language + "']").addClass('active');
for (var i=0; i < languages.length; i++) { for (var i=0; i < languages.length; i++) {
$(".highlight." + languages[i]).parent().hide(); $(".highlight." + languages[i]).hide();
} }
$(".highlight." + language).parent().show(); $(".highlight." + language).show();
global.toc.calculateHeights(); global.toc.calculateHeights();
......
(function (global) { (function (global) {
'use strict';
var $global = $(global); var $global = $(global);
var content, darkBox, searchResults; var content, darkBox, searchResults;
...@@ -49,7 +50,8 @@ ...@@ -49,7 +50,8 @@
if (results.length) { if (results.length) {
searchResults.empty(); searchResults.empty();
$.each(results, function (index, result) { $.each(results, function (index, result) {
searchResults.append("<li><a href='#" + result.ref + "'>" + $('#'+result.ref).text() + "</a></li>"); var elem = document.getElementById(result.ref);
searchResults.append("<li><a href='#" + result.ref + "'>" + $(elem).text() + "</a></li>");
}); });
highlight.call(this); highlight.call(this);
} else { } else {
......
(function (global) { (function (global) {
'use strict';
var closeToc = function() { var closeToc = function() {
$(".tocify-wrapper").removeClass('open'); $(".tocify-wrapper").removeClass('open');
......
/*! jQuery UI - v1.10.3 - 2013-09-16 /*! jQuery UI - v1.11.3 - 2015-02-12
* http://jqueryui.com * http://jqueryui.com
* Includes: jquery.ui.widget.js * Includes: widget.js
* Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */ * Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */
(function(e,t){var i=0,s=Array.prototype.slice,a=e.cleanData;e.cleanData=function(t){for(var i,s=0;null!=(i=t[s]);s++)try{e(i).triggerHandler("remove")}catch(n){}a(t)},e.widget=function(i,s,a){var n,r,o,h,l={},u=i.split(".")[0];i=i.split(".")[1],n=u+"-"+i,a||(a=s,s=e.Widget),e.expr[":"][n.toLowerCase()]=function(t){return!!e.data(t,n)},e[u]=e[u]||{},r=e[u][i],o=e[u][i]=function(e,i){return this._createWidget?(arguments.length&&this._createWidget(e,i),t):new o(e,i)},e.extend(o,r,{version:a.version,_proto:e.extend({},a),_childConstructors:[]}),h=new s,h.options=e.widget.extend({},h.options),e.each(a,function(i,a){return e.isFunction(a)?(l[i]=function(){var e=function(){return s.prototype[i].apply(this,arguments)},t=function(e){return s.prototype[i].apply(this,e)};return function(){var i,s=this._super,n=this._superApply;return this._super=e,this._superApply=t,i=a.apply(this,arguments),this._super=s,this._superApply=n,i}}(),t):(l[i]=a,t)}),o.prototype=e.widget.extend(h,{widgetEventPrefix:r?h.widgetEventPrefix:i},l,{constructor:o,namespace:u,widgetName:i,widgetFullName:n}),r?(e.each(r._childConstructors,function(t,i){var s=i.prototype;e.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete r._childConstructors):s._childConstructors.push(o),e.widget.bridge(i,o)},e.widget.extend=function(i){for(var a,n,r=s.call(arguments,1),o=0,h=r.length;h>o;o++)for(a in r[o])n=r[o][a],r[o].hasOwnProperty(a)&&n!==t&&(i[a]=e.isPlainObject(n)?e.isPlainObject(i[a])?e.widget.extend({},i[a],n):e.widget.extend({},n):n);return i},e.widget.bridge=function(i,a){var n=a.prototype.widgetFullName||i;e.fn[i]=function(r){var o="string"==typeof r,h=s.call(arguments,1),l=this;return r=!o&&h.length?e.widget.extend.apply(null,[r].concat(h)):r,o?this.each(function(){var s,a=e.data(this,n);return a?e.isFunction(a[r])&&"_"!==r.charAt(0)?(s=a[r].apply(a,h),s!==a&&s!==t?(l=s&&s.jquery?l.pushStack(s.get()):s,!1):t):e.error("no such method '"+r+"' for "+i+" widget instance"):e.error("cannot call methods on "+i+" prior to initialization; "+"attempted to call method '"+r+"'")}):this.each(function(){var t=e.data(this,n);t?t.option(r||{})._init():e.data(this,n,new a(r,this))}),l}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{disabled:!1,create:null},_createWidget:function(t,s){s=e(s||this.defaultElement||this)[0],this.element=e(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),s!==this&&(e.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===s&&this.destroy()}}),this.document=e(s.style?s.ownerDocument:s.document||s),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(i,s){var a,n,r,o=i;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof i)if(o={},a=i.split("."),i=a.shift(),a.length){for(n=o[i]=e.widget.extend({},this.options[i]),r=0;a.length-1>r;r++)n[a[r]]=n[a[r]]||{},n=n[a[r]];if(i=a.pop(),s===t)return n[i]===t?null:n[i];n[i]=s}else{if(s===t)return this.options[i]===t?null:this.options[i];o[i]=s}return this._setOptions(o),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!t).attr("aria-disabled",t),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(i,s,a){var n,r=this;"boolean"!=typeof i&&(a=s,s=i,i=!1),a?(s=n=e(s),this.bindings=this.bindings.add(s)):(a=s,s=this.element,n=this.widget()),e.each(a,function(a,o){function h(){return i||r.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof o?r[o]:o).apply(r,arguments):t}"string"!=typeof o&&(h.guid=o.guid=o.guid||h.guid||e.guid++);var l=a.match(/^(\w+)\s*(.*)$/),u=l[1]+r.eventNamespace,c=l[2];c?n.delegate(c,u,h):s.bind(u,h)})},_off:function(e,t){t=(t||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function i(){return("string"==typeof e?s[e]:e).apply(s,arguments)}var s=this;return setTimeout(i,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,i,s){var a,n,r=this.options[t];if(s=s||{},i=e.Event(i),i.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),i.target=this.element[0],n=i.originalEvent)for(a in n)a in i||(i[a]=n[a]);return this.element.trigger(i,s),!(e.isFunction(r)&&r.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,i){e.Widget.prototype["_"+t]=function(s,a,n){"string"==typeof a&&(a={effect:a});var r,o=a?a===!0||"number"==typeof a?i:a.effect||i:t;a=a||{},"number"==typeof a&&(a={duration:a}),r=!e.isEmptyObject(a),a.complete=n,a.delay&&s.delay(a.delay),r&&e.effects&&e.effects.effect[o]?s[t](a):o!==t&&s[o]?s[o](a.duration,a.easing,n):s.queue(function(i){e(this)[t](),n&&n.call(s[0]),i()})}})})(jQuery); (function( factory ) {
\ No newline at end of file if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define([ "jquery" ], factory );
} else {
// Browser globals
factory( jQuery );
}
}(function( $ ) {
/*!
* jQuery UI Widget 1.11.3
* http://jqueryui.com
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/jQuery.widget/
*/
var widget_uuid = 0,
widget_slice = Array.prototype.slice;
$.cleanData = (function( orig ) {
return function( elems ) {
var events, elem, i;
for ( i = 0; (elem = elems[i]) != null; i++ ) {
try {
// Only trigger remove when necessary to save time
events = $._data( elem, "events" );
if ( events && events.remove ) {
$( elem ).triggerHandler( "remove" );
}
// http://bugs.jquery.com/ticket/8235
} catch ( e ) {}
}
orig( elems );
};
})( $.cleanData );
$.widget = function( name, base, prototype ) {
var fullName, existingConstructor, constructor, basePrototype,
// proxiedPrototype allows the provided prototype to remain unmodified
// so that it can be used as a mixin for multiple widgets (#8876)
proxiedPrototype = {},
namespace = name.split( "." )[ 0 ];
name = name.split( "." )[ 1 ];
fullName = namespace + "-" + name;
if ( !prototype ) {
prototype = base;
base = $.Widget;
}
// create selector for plugin
$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
return !!$.data( elem, fullName );
};
$[ namespace ] = $[ namespace ] || {};
existingConstructor = $[ namespace ][ name ];
constructor = $[ namespace ][ name ] = function( options, element ) {
// allow instantiation without "new" keyword
if ( !this._createWidget ) {
return new constructor( options, element );
}
// allow instantiation without initializing for simple inheritance
// must use "new" keyword (the code above always passes args)
if ( arguments.length ) {
this._createWidget( options, element );
}
};
// extend with the existing constructor to carry over any static properties
$.extend( constructor, existingConstructor, {
version: prototype.version,
// copy the object used to create the prototype in case we need to
// redefine the widget later
_proto: $.extend( {}, prototype ),
// track widgets that inherit from this widget in case this widget is
// redefined after a widget inherits from it
_childConstructors: []
});
basePrototype = new base();
// we need to make the options hash a property directly on the new instance
// otherwise we'll modify the options hash on the prototype that we're
// inheriting from
basePrototype.options = $.widget.extend( {}, basePrototype.options );
$.each( prototype, function( prop, value ) {
if ( !$.isFunction( value ) ) {
proxiedPrototype[ prop ] = value;
return;
}
proxiedPrototype[ prop ] = (function() {
var _super = function() {
return base.prototype[ prop ].apply( this, arguments );
},
_superApply = function( args ) {
return base.prototype[ prop ].apply( this, args );
};
return function() {
var __super = this._super,
__superApply = this._superApply,
returnValue;
this._super = _super;
this._superApply = _superApply;
returnValue = value.apply( this, arguments );
this._super = __super;
this._superApply = __superApply;
return returnValue;
};
})();
});
constructor.prototype = $.widget.extend( basePrototype, {
// TODO: remove support for widgetEventPrefix
// always use the name + a colon as the prefix, e.g., draggable:start
// don't prefix for widgets that aren't DOM-based
widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
}, proxiedPrototype, {
constructor: constructor,
namespace: namespace,
widgetName: name,
widgetFullName: fullName
});
// If this widget is being redefined then we need to find all widgets that
// are inheriting from it and redefine all of them so that they inherit from
// the new version of this widget. We're essentially trying to replace one
// level in the prototype chain.
if ( existingConstructor ) {
$.each( existingConstructor._childConstructors, function( i, child ) {
var childPrototype = child.prototype;
// redefine the child widget using the same prototype that was
// originally used, but inherit from the new version of the base
$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
});
// remove the list of existing child constructors from the old constructor
// so the old child constructors can be garbage collected
delete existingConstructor._childConstructors;
} else {
base._childConstructors.push( constructor );
}
$.widget.bridge( name, constructor );
return constructor;
};
$.widget.extend = function( target ) {
var input = widget_slice.call( arguments, 1 ),
inputIndex = 0,
inputLength = input.length,
key,
value;
for ( ; inputIndex < inputLength; inputIndex++ ) {
for ( key in input[ inputIndex ] ) {
value = input[ inputIndex ][ key ];
if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
// Clone objects
if ( $.isPlainObject( value ) ) {
target[ key ] = $.isPlainObject( target[ key ] ) ?
$.widget.extend( {}, target[ key ], value ) :
// Don't extend strings, arrays, etc. with objects
$.widget.extend( {}, value );
// Copy everything else by reference
} else {
target[ key ] = value;
}
}
}
}
return target;
};
$.widget.bridge = function( name, object ) {
var fullName = object.prototype.widgetFullName || name;
$.fn[ name ] = function( options ) {
var isMethodCall = typeof options === "string",
args = widget_slice.call( arguments, 1 ),
returnValue = this;
if ( isMethodCall ) {
this.each(function() {
var methodValue,
instance = $.data( this, fullName );
if ( options === "instance" ) {
returnValue = instance;
return false;
}
if ( !instance ) {
return $.error( "cannot call methods on " + name + " prior to initialization; " +
"attempted to call method '" + options + "'" );
}
if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
return $.error( "no such method '" + options + "' for " + name + " widget instance" );
}
methodValue = instance[ options ].apply( instance, args );
if ( methodValue !== instance && methodValue !== undefined ) {
returnValue = methodValue && methodValue.jquery ?
returnValue.pushStack( methodValue.get() ) :
methodValue;
return false;
}
});
} else {
// Allow multiple hashes to be passed on init
if ( args.length ) {
options = $.widget.extend.apply( null, [ options ].concat(args) );
}
this.each(function() {
var instance = $.data( this, fullName );
if ( instance ) {
instance.option( options || {} );
if ( instance._init ) {
instance._init();
}
} else {
$.data( this, fullName, new object( options, this ) );
}
});
}
return returnValue;
};
};
$.Widget = function( /* options, element */ ) {};
$.Widget._childConstructors = [];
$.Widget.prototype = {
widgetName: "widget",
widgetEventPrefix: "",
defaultElement: "<div>",
options: {
disabled: false,
// callbacks
create: null
},
_createWidget: function( options, element ) {
element = $( element || this.defaultElement || this )[ 0 ];
this.element = $( element );
this.uuid = widget_uuid++;
this.eventNamespace = "." + this.widgetName + this.uuid;
this.bindings = $();
this.hoverable = $();
this.focusable = $();
if ( element !== this ) {
$.data( element, this.widgetFullName, this );
this._on( true, this.element, {
remove: function( event ) {
if ( event.target === element ) {
this.destroy();
}
}
});
this.document = $( element.style ?
// element within the document
element.ownerDocument :
// element is window or document
element.document || element );
this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
}
this.options = $.widget.extend( {},
this.options,
this._getCreateOptions(),
options );
this._create();
this._trigger( "create", null, this._getCreateEventData() );
this._init();
},
_getCreateOptions: $.noop,
_getCreateEventData: $.noop,
_create: $.noop,
_init: $.noop,
destroy: function() {
this._destroy();
// we can probably remove the unbind calls in 2.0
// all event bindings should go through this._on()
this.element
.unbind( this.eventNamespace )
.removeData( this.widgetFullName )
// support: jquery <1.6.3
// http://bugs.jquery.com/ticket/9413
.removeData( $.camelCase( this.widgetFullName ) );
this.widget()
.unbind( this.eventNamespace )
.removeAttr( "aria-disabled" )
.removeClass(
this.widgetFullName + "-disabled " +
"ui-state-disabled" );
// clean up events and states
this.bindings.unbind( this.eventNamespace );
this.hoverable.removeClass( "ui-state-hover" );
this.focusable.removeClass( "ui-state-focus" );
},
_destroy: $.noop,
widget: function() {
return this.element;
},
option: function( key, value ) {
var options = key,
parts,
curOption,
i;
if ( arguments.length === 0 ) {
// don't return a reference to the internal hash
return $.widget.extend( {}, this.options );
}
if ( typeof key === "string" ) {
// handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
options = {};
parts = key.split( "." );
key = parts.shift();
if ( parts.length ) {
curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
for ( i = 0; i < parts.length - 1; i++ ) {
curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
curOption = curOption[ parts[ i ] ];
}
key = parts.pop();
if ( arguments.length === 1 ) {
return curOption[ key ] === undefined ? null : curOption[ key ];
}
curOption[ key ] = value;
} else {
if ( arguments.length === 1 ) {
return this.options[ key ] === undefined ? null : this.options[ key ];
}
options[ key ] = value;
}
}
this._setOptions( options );
return this;
},
_setOptions: function( options ) {
var key;
for ( key in options ) {
this._setOption( key, options[ key ] );
}
return this;
},
_setOption: function( key, value ) {
this.options[ key ] = value;
if ( key === "disabled" ) {
this.widget()
.toggleClass( this.widgetFullName + "-disabled", !!value );
// If the widget is becoming disabled, then nothing is interactive
if ( value ) {
this.hoverable.removeClass( "ui-state-hover" );
this.focusable.removeClass( "ui-state-focus" );
}
}
return this;
},
enable: function() {
return this._setOptions({ disabled: false });
},
disable: function() {
return this._setOptions({ disabled: true });
},
_on: function( suppressDisabledCheck, element, handlers ) {
var delegateElement,
instance = this;
// no suppressDisabledCheck flag, shuffle arguments
if ( typeof suppressDisabledCheck !== "boolean" ) {
handlers = element;
element = suppressDisabledCheck;
suppressDisabledCheck = false;
}
// no element argument, shuffle and use this.element
if ( !handlers ) {
handlers = element;
element = this.element;
delegateElement = this.widget();
} else {
element = delegateElement = $( element );
this.bindings = this.bindings.add( element );
}
$.each( handlers, function( event, handler ) {
function handlerProxy() {
// allow widgets to customize the disabled handling
// - disabled as an array instead of boolean
// - disabled class as method for disabling individual parts
if ( !suppressDisabledCheck &&
( instance.options.disabled === true ||
$( this ).hasClass( "ui-state-disabled" ) ) ) {
return;
}
return ( typeof handler === "string" ? instance[ handler ] : handler )
.apply( instance, arguments );
}
// copy the guid so direct unbinding works
if ( typeof handler !== "string" ) {
handlerProxy.guid = handler.guid =
handler.guid || handlerProxy.guid || $.guid++;
}
var match = event.match( /^([\w:-]*)\s*(.*)$/ ),
eventName = match[1] + instance.eventNamespace,
selector = match[2];
if ( selector ) {
delegateElement.delegate( selector, eventName, handlerProxy );
} else {
element.bind( eventName, handlerProxy );
}
});
},
_off: function( element, eventName ) {
eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) +
this.eventNamespace;
element.unbind( eventName ).undelegate( eventName );
// Clear the stack to avoid memory leaks (#10056)
this.bindings = $( this.bindings.not( element ).get() );
this.focusable = $( this.focusable.not( element ).get() );
this.hoverable = $( this.hoverable.not( element ).get() );
},
_delay: function( handler, delay ) {
function handlerProxy() {
return ( typeof handler === "string" ? instance[ handler ] : handler )
.apply( instance, arguments );
}
var instance = this;
return setTimeout( handlerProxy, delay || 0 );
},
_hoverable: function( element ) {
this.hoverable = this.hoverable.add( element );
this._on( element, {
mouseenter: function( event ) {
$( event.currentTarget ).addClass( "ui-state-hover" );
},
mouseleave: function( event ) {
$( event.currentTarget ).removeClass( "ui-state-hover" );
}
});
},
_focusable: function( element ) {
this.focusable = this.focusable.add( element );
this._on( element, {
focusin: function( event ) {
$( event.currentTarget ).addClass( "ui-state-focus" );
},
focusout: function( event ) {
$( event.currentTarget ).removeClass( "ui-state-focus" );
}
});
},
_trigger: function( type, event, data ) {
var prop, orig,
callback = this.options[ type ];
data = data || {};
event = $.Event( event );
event.type = ( type === this.widgetEventPrefix ?
type :
this.widgetEventPrefix + type ).toLowerCase();
// the original event may come from any element
// so we need to reset the target on the new event
event.target = this.element[ 0 ];
// copy original event properties over to the new event
orig = event.originalEvent;
if ( orig ) {
for ( prop in orig ) {
if ( !( prop in event ) ) {
event[ prop ] = orig[ prop ];
}
}
}
this.element.trigger( event, data );
return !( $.isFunction( callback ) &&
callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
event.isDefaultPrevented() );
}
};
$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
if ( typeof options === "string" ) {
options = { effect: options };
}
var hasOptions,
effectName = !options ?
method :
options === true || typeof options === "number" ?
defaultEffect :
options.effect || defaultEffect;
options = options || {};
if ( typeof options === "number" ) {
options = { duration: options };
}
hasOptions = !$.isEmptyObject( options );
options.complete = callback;
if ( options.delay ) {
element.delay( options.delay );
}
if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
element[ method ]( options );
} else if ( effectName !== method && element[ effectName ] ) {
element[ effectName ]( options.duration, options.easing, callback );
} else {
element.queue(function( next ) {
$( this )[ method ]();
if ( callback ) {
callback.call( element[ 0 ] );
}
next();
});
}
};
});
var widget = $.widget;
}));
/** /**
* lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.5.2 * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.5.7
* Copyright (C) 2014 Oliver Nightingale * Copyright (C) 2014 Oliver Nightingale
* MIT Licensed * MIT Licensed
* @license * @license
...@@ -7,1851 +7,1881 @@ ...@@ -7,1851 +7,1881 @@
(function(){ (function(){
/** /**
* Convenience function for instantiating a new lunr index and configuring it * Convenience function for instantiating a new lunr index and configuring it
* with the default pipeline functions and the passed config function. * with the default pipeline functions and the passed config function.
* *
* When using this convenience function a new index will be created with the * When using this convenience function a new index will be created with the
* following functions already in the pipeline: * following functions already in the pipeline:
* *
* lunr.StopWordFilter - filters out any stop words before they enter the * lunr.StopWordFilter - filters out any stop words before they enter the
* index * index
* *
* lunr.stemmer - stems the tokens before entering the index. * lunr.stemmer - stems the tokens before entering the index.
* *
* Example: * Example:
* *
* var idx = lunr(function () { * var idx = lunr(function () {
* this.field('title', 10) * this.field('title', 10)
* this.field('tags', 100) * this.field('tags', 100)
* this.field('body') * this.field('body')
* *
* this.ref('cid') * this.ref('cid')
* *
* this.pipeline.add(function () { * this.pipeline.add(function () {
* // some custom pipeline function * // some custom pipeline function
* }) * })
*
* })
*
* @param {Function} config A function that will be called with the new instance
* of the lunr.Index as both its context and first parameter. It can be used to
* customize the instance of new lunr.Index.
* @namespace
* @module
* @returns {lunr.Index}
* *
*/ * })
var lunr = function (config) { *
var idx = new lunr.Index * @param {Function} config A function that will be called with the new instance
* of the lunr.Index as both its context and first parameter. It can be used to
* customize the instance of new lunr.Index.
* @namespace
* @module
* @returns {lunr.Index}
*
*/
var lunr = function (config) {
var idx = new lunr.Index
idx.pipeline.add( idx.pipeline.add(
lunr.trimmer, lunr.trimmer,
lunr.stopWordFilter, lunr.stopWordFilter,
lunr.stemmer lunr.stemmer
) )
if (config) config.call(idx, idx) if (config) config.call(idx, idx)
return idx return idx
} }
lunr.version = "0.5.2" lunr.version = "0.5.7"
/*! /*!
* lunr.utils * lunr.utils
* Copyright (C) 2014 Oliver Nightingale * Copyright (C) 2014 Oliver Nightingale
*/ */
/** /**
* A namespace containing utils for the rest of the lunr library * A namespace containing utils for the rest of the lunr library
*/ */
lunr.utils = {} lunr.utils = {}
/** /**
* Print a warning message to the console. * Print a warning message to the console.
* *
* @param {String} message The message to be printed. * @param {String} message The message to be printed.
* @memberOf Utils * @memberOf Utils
*/ */
lunr.utils.warn = (function (global) { lunr.utils.warn = (function (global) {
return function (message) { return function (message) {
if (global.console && console.warn) { if (global.console && console.warn) {
console.warn(message) console.warn(message)
}
} }
} })(this)
})(this)
/*! /*!
* lunr.EventEmitter * lunr.EventEmitter
* Copyright (C) 2014 Oliver Nightingale * Copyright (C) 2014 Oliver Nightingale
*/ */
/** /**
* lunr.EventEmitter is an event emitter for lunr. It manages adding and removing event handlers and triggering events and their handlers. * lunr.EventEmitter is an event emitter for lunr. It manages adding and removing event handlers and triggering events and their handlers.
* *
* @constructor * @constructor
*/ */
lunr.EventEmitter = function () { lunr.EventEmitter = function () {
this.events = {} this.events = {}
} }
/** /**
* Binds a handler function to a specific event(s). * Binds a handler function to a specific event(s).
* *
* Can bind a single function to many different events in one call. * Can bind a single function to many different events in one call.
* *
* @param {String} [eventName] The name(s) of events to bind this function to. * @param {String} [eventName] The name(s) of events to bind this function to.
* @param {Function} handler The function to call when an event is fired. * @param {Function} handler The function to call when an event is fired.
* @memberOf EventEmitter * @memberOf EventEmitter
*/ */
lunr.EventEmitter.prototype.addListener = function () { lunr.EventEmitter.prototype.addListener = function () {
var args = Array.prototype.slice.call(arguments), var args = Array.prototype.slice.call(arguments),
fn = args.pop(), fn = args.pop(),
names = args names = args
if (typeof fn !== "function") throw new TypeError ("last argument must be a function") if (typeof fn !== "function") throw new TypeError ("last argument must be a function")
names.forEach(function (name) { names.forEach(function (name) {
if (!this.hasHandler(name)) this.events[name] = [] if (!this.hasHandler(name)) this.events[name] = []
this.events[name].push(fn) this.events[name].push(fn)
}, this) }, this)
} }
/** /**
* Removes a handler function from a specific event. * Removes a handler function from a specific event.
* *
* @param {String} eventName The name of the event to remove this function from. * @param {String} eventName The name of the event to remove this function from.
* @param {Function} handler The function to remove from an event. * @param {Function} handler The function to remove from an event.
* @memberOf EventEmitter * @memberOf EventEmitter
*/ */
lunr.EventEmitter.prototype.removeListener = function (name, fn) { lunr.EventEmitter.prototype.removeListener = function (name, fn) {
if (!this.hasHandler(name)) return if (!this.hasHandler(name)) return
var fnIndex = this.events[name].indexOf(fn) var fnIndex = this.events[name].indexOf(fn)
this.events[name].splice(fnIndex, 1) this.events[name].splice(fnIndex, 1)
if (!this.events[name].length) delete this.events[name] if (!this.events[name].length) delete this.events[name]
} }
/** /**
* Calls all functions bound to the given event. * Calls all functions bound to the given event.
* *
* Additional data can be passed to the event handler as arguments to `emit` * Additional data can be passed to the event handler as arguments to `emit`
* after the event name. * after the event name.
* *
* @param {String} eventName The name of the event to emit. * @param {String} eventName The name of the event to emit.
* @memberOf EventEmitter * @memberOf EventEmitter
*/ */
lunr.EventEmitter.prototype.emit = function (name) { lunr.EventEmitter.prototype.emit = function (name) {
if (!this.hasHandler(name)) return if (!this.hasHandler(name)) return
var args = Array.prototype.slice.call(arguments, 1) var args = Array.prototype.slice.call(arguments, 1)
this.events[name].forEach(function (fn) { this.events[name].forEach(function (fn) {
fn.apply(undefined, args) fn.apply(undefined, args)
}) })
} }
/** /**
* Checks whether a handler has ever been stored against an event. * Checks whether a handler has ever been stored against an event.
* *
* @param {String} eventName The name of the event to check. * @param {String} eventName The name of the event to check.
* @private * @private
* @memberOf EventEmitter * @memberOf EventEmitter
*/ */
lunr.EventEmitter.prototype.hasHandler = function (name) { lunr.EventEmitter.prototype.hasHandler = function (name) {
return name in this.events return name in this.events
} }
/*! /*!
* lunr.tokenizer * lunr.tokenizer
* Copyright (C) 2014 Oliver Nightingale * Copyright (C) 2014 Oliver Nightingale
*/ */
/** /**
* A function for splitting a string into tokens ready to be inserted into * A function for splitting a string into tokens ready to be inserted into
* the search index. * the search index.
* *
* @module * @module
* @param {String} obj The string to convert into tokens * @param {String} obj The string to convert into tokens
* @returns {Array} * @returns {Array}
*/ */
lunr.tokenizer = function (obj) { lunr.tokenizer = function (obj) {
if (!arguments.length || obj == null || obj == undefined) return [] if (!arguments.length || obj == null || obj == undefined) return []
if (Array.isArray(obj)) return obj.map(function (t) { return t.toLowerCase() }) if (Array.isArray(obj)) return obj.map(function (t) { return t.toLowerCase() })
var str = obj.toString().replace(/^\s+/, '') var str = obj.toString().replace(/^\s+/, '')
for (var i = str.length - 1; i >= 0; i--) { for (var i = str.length - 1; i >= 0; i--) {
if (/\S/.test(str.charAt(i))) { if (/\S/.test(str.charAt(i))) {
str = str.substring(0, i + 1) str = str.substring(0, i + 1)
break break
}
} }
return str
.split(/(?:\s+|\-)/)
.filter(function (token) {
return !!token
})
.map(function (token) {
return token.toLowerCase()
})
} }
/*!
* lunr.Pipeline
* Copyright (C) 2014 Oliver Nightingale
*/
return str /**
.split(/\s+/) * lunr.Pipelines maintain an ordered list of functions to be applied to all
.map(function (token) { * tokens in documents entering the search index and queries being ran against
return token.toLowerCase() * the index.
}) *
} * An instance of lunr.Index created with the lunr shortcut will contain a
/*! * pipeline with a stop word filter and an English language stemmer. Extra
* lunr.Pipeline * functions can be added before or after either of these functions or these
* Copyright (C) 2014 Oliver Nightingale * default functions can be removed.
*/ *
* When run the pipeline will call each function in turn, passing a token, the
* index of that token in the original list of all tokens and finally a list of
* all the original tokens.
*
* The output of functions in the pipeline will be passed to the next function
* in the pipeline. To exclude a token from entering the index the function
* should return undefined, the rest of the pipeline will not be called with
* this token.
*
* For serialisation of pipelines to work, all functions used in an instance of
* a pipeline should be registered with lunr.Pipeline. Registered functions can
* then be loaded. If trying to load a serialised pipeline that uses functions
* that are not registered an error will be thrown.
*
* If not planning on serialising the pipeline then registering pipeline functions
* is not necessary.
*
* @constructor
*/
lunr.Pipeline = function () {
this._stack = []
}
/** lunr.Pipeline.registeredFunctions = {}
* lunr.Pipelines maintain an ordered list of functions to be applied to all
* tokens in documents entering the search index and queries being ran against
* the index.
*
* An instance of lunr.Index created with the lunr shortcut will contain a
* pipeline with a stop word filter and an English language stemmer. Extra
* functions can be added before or after either of these functions or these
* default functions can be removed.
*
* When run the pipeline will call each function in turn, passing a token, the
* index of that token in the original list of all tokens and finally a list of
* all the original tokens.
*
* The output of functions in the pipeline will be passed to the next function
* in the pipeline. To exclude a token from entering the index the function
* should return undefined, the rest of the pipeline will not be called with
* this token.
*
* For serialisation of pipelines to work, all functions used in an instance of
* a pipeline should be registered with lunr.Pipeline. Registered functions can
* then be loaded. If trying to load a serialised pipeline that uses functions
* that are not registered an error will be thrown.
*
* If not planning on serialising the pipeline then registering pipeline functions
* is not necessary.
*
* @constructor
*/
lunr.Pipeline = function () {
this._stack = []
}
lunr.Pipeline.registeredFunctions = {} /**
* Register a function with the pipeline.
*
* Functions that are used in the pipeline should be registered if the pipeline
* needs to be serialised, or a serialised pipeline needs to be loaded.
*
* Registering a function does not add it to a pipeline, functions must still be
* added to instances of the pipeline for them to be used when running a pipeline.
*
* @param {Function} fn The function to check for.
* @param {String} label The label to register this function with
* @memberOf Pipeline
*/
lunr.Pipeline.registerFunction = function (fn, label) {
if (label in this.registeredFunctions) {
lunr.utils.warn('Overwriting existing registered function: ' + label)
}
/** fn.label = label
* Register a function with the pipeline. lunr.Pipeline.registeredFunctions[fn.label] = fn
*
* Functions that are used in the pipeline should be registered if the pipeline
* needs to be serialised, or a serialised pipeline needs to be loaded.
*
* Registering a function does not add it to a pipeline, functions must still be
* added to instances of the pipeline for them to be used when running a pipeline.
*
* @param {Function} fn The function to check for.
* @param {String} label The label to register this function with
* @memberOf Pipeline
*/
lunr.Pipeline.registerFunction = function (fn, label) {
if (label in this.registeredFunctions) {
lunr.utils.warn('Overwriting existing registered function: ' + label)
} }
fn.label = label /**
lunr.Pipeline.registeredFunctions[fn.label] = fn * Warns if the function is not registered as a Pipeline function.
} *
* @param {Function} fn The function to check for.
/** * @private
* Warns if the function is not registered as a Pipeline function. * @memberOf Pipeline
* */
* @param {Function} fn The function to check for. lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {
* @private var isRegistered = fn.label && (fn.label in this.registeredFunctions)
* @memberOf Pipeline
*/
lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {
var isRegistered = fn.label && (fn.label in this.registeredFunctions)
if (!isRegistered) { if (!isRegistered) {
lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn) lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn)
}
} }
}
/** /**
* Loads a previously serialised pipeline. * Loads a previously serialised pipeline.
* *
* All functions to be loaded must already be registered with lunr.Pipeline. * All functions to be loaded must already be registered with lunr.Pipeline.
* If any function from the serialised data has not been registered then an * If any function from the serialised data has not been registered then an
* error will be thrown. * error will be thrown.
* *
* @param {Object} serialised The serialised pipeline to load. * @param {Object} serialised The serialised pipeline to load.
* @returns {lunr.Pipeline} * @returns {lunr.Pipeline}
* @memberOf Pipeline * @memberOf Pipeline
*/ */
lunr.Pipeline.load = function (serialised) { lunr.Pipeline.load = function (serialised) {
var pipeline = new lunr.Pipeline var pipeline = new lunr.Pipeline
serialised.forEach(function (fnName) { serialised.forEach(function (fnName) {
var fn = lunr.Pipeline.registeredFunctions[fnName] var fn = lunr.Pipeline.registeredFunctions[fnName]
if (fn) { if (fn) {
pipeline.add(fn) pipeline.add(fn)
} else { } else {
throw new Error ('Cannot load un-registered function: ' + fnName) throw new Error ('Cannot load un-registered function: ' + fnName)
} }
}) })
return pipeline
}
/** return pipeline
* Adds new functions to the end of the pipeline. }
*
* Logs a warning if the function has not been registered.
*
* @param {Function} functions Any number of functions to add to the pipeline.
* @memberOf Pipeline
*/
lunr.Pipeline.prototype.add = function () {
var fns = Array.prototype.slice.call(arguments)
fns.forEach(function (fn) { /**
lunr.Pipeline.warnIfFunctionNotRegistered(fn) * Adds new functions to the end of the pipeline.
this._stack.push(fn) *
}, this) * Logs a warning if the function has not been registered.
} *
* @param {Function} functions Any number of functions to add to the pipeline.
* @memberOf Pipeline
*/
lunr.Pipeline.prototype.add = function () {
var fns = Array.prototype.slice.call(arguments)
/** fns.forEach(function (fn) {
* Adds a single function after a function that already exists in the lunr.Pipeline.warnIfFunctionNotRegistered(fn)
* pipeline. this._stack.push(fn)
* }, this)
* Logs a warning if the function has not been registered. }
*
* @param {Function} existingFn A function that already exists in the pipeline.
* @param {Function} newFn The new function to add to the pipeline.
* @memberOf Pipeline
*/
lunr.Pipeline.prototype.after = function (existingFn, newFn) {
lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
var pos = this._stack.indexOf(existingFn) + 1 /**
this._stack.splice(pos, 0, newFn) * Adds a single function after a function that already exists in the
} * pipeline.
*
* Logs a warning if the function has not been registered.
*
* @param {Function} existingFn A function that already exists in the pipeline.
* @param {Function} newFn The new function to add to the pipeline.
* @memberOf Pipeline
*/
lunr.Pipeline.prototype.after = function (existingFn, newFn) {
lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
/** var pos = this._stack.indexOf(existingFn) + 1
* Adds a single function before a function that already exists in the this._stack.splice(pos, 0, newFn)
* pipeline. }
*
* Logs a warning if the function has not been registered.
*
* @param {Function} existingFn A function that already exists in the pipeline.
* @param {Function} newFn The new function to add to the pipeline.
* @memberOf Pipeline
*/
lunr.Pipeline.prototype.before = function (existingFn, newFn) {
lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
var pos = this._stack.indexOf(existingFn) /**
this._stack.splice(pos, 0, newFn) * Adds a single function before a function that already exists in the
} * pipeline.
*
* Logs a warning if the function has not been registered.
*
* @param {Function} existingFn A function that already exists in the pipeline.
* @param {Function} newFn The new function to add to the pipeline.
* @memberOf Pipeline
*/
lunr.Pipeline.prototype.before = function (existingFn, newFn) {
lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
/** var pos = this._stack.indexOf(existingFn)
* Removes a function from the pipeline. this._stack.splice(pos, 0, newFn)
* }
* @param {Function} fn The function to remove from the pipeline.
* @memberOf Pipeline
*/
lunr.Pipeline.prototype.remove = function (fn) {
var pos = this._stack.indexOf(fn)
this._stack.splice(pos, 1)
}
/** /**
* Runs the current list of functions that make up the pipeline against the * Removes a function from the pipeline.
* passed tokens. *
* * @param {Function} fn The function to remove from the pipeline.
* @param {Array} tokens The tokens to run through the pipeline. * @memberOf Pipeline
* @returns {Array} */
* @memberOf Pipeline lunr.Pipeline.prototype.remove = function (fn) {
*/ var pos = this._stack.indexOf(fn)
lunr.Pipeline.prototype.run = function (tokens) { this._stack.splice(pos, 1)
var out = [], }
tokenLength = tokens.length,
stackLength = this._stack.length
for (var i = 0; i < tokenLength; i++) { /**
var token = tokens[i] * Runs the current list of functions that make up the pipeline against the
* passed tokens.
*
* @param {Array} tokens The tokens to run through the pipeline.
* @returns {Array}
* @memberOf Pipeline
*/
lunr.Pipeline.prototype.run = function (tokens) {
var out = [],
tokenLength = tokens.length,
stackLength = this._stack.length
for (var j = 0; j < stackLength; j++) { for (var i = 0; i < tokenLength; i++) {
token = this._stack[j](token, i, tokens) var token = tokens[i]
if (token === void 0) break
};
if (token !== void 0) out.push(token) for (var j = 0; j < stackLength; j++) {
}; token = this._stack[j](token, i, tokens)
if (token === void 0) break
};
return out if (token !== void 0) out.push(token)
} };
/** return out
* Resets the pipeline by removing any existing processors. }
*
* @memberOf Pipeline
*/
lunr.Pipeline.prototype.reset = function () {
this._stack = []
}
/** /**
* Returns a representation of the pipeline ready for serialisation. * Resets the pipeline by removing any existing processors.
* *
* Logs a warning if the function has not been registered. * @memberOf Pipeline
* */
* @returns {Array} lunr.Pipeline.prototype.reset = function () {
* @memberOf Pipeline this._stack = []
*/ }
lunr.Pipeline.prototype.toJSON = function () {
return this._stack.map(function (fn) {
lunr.Pipeline.warnIfFunctionNotRegistered(fn)
return fn.label
})
}
/*!
* lunr.Vector
* Copyright (C) 2014 Oliver Nightingale
*/
/** /**
* lunr.Vectors implement vector related operations for * Returns a representation of the pipeline ready for serialisation.
* a series of elements. *
* * Logs a warning if the function has not been registered.
* @constructor *
*/ * @returns {Array}
lunr.Vector = function () { * @memberOf Pipeline
this._magnitude = null */
this.list = undefined lunr.Pipeline.prototype.toJSON = function () {
this.length = 0 return this._stack.map(function (fn) {
} lunr.Pipeline.warnIfFunctionNotRegistered(fn)
/** return fn.label
* lunr.Vector.Node is a simple struct for each node })
* in a lunr.Vector. }
* /*!
* @private * lunr.Vector
* @param {Number} The index of the node in the vector. * Copyright (C) 2014 Oliver Nightingale
* @param {Object} The data at this node in the vector. */
* @param {lunr.Vector.Node} The node directly after this node in the vector.
* @constructor
* @memberOf Vector
*/
lunr.Vector.Node = function (idx, val, next) {
this.idx = idx
this.val = val
this.next = next
}
/** /**
* Inserts a new value at a position in a vector. * lunr.Vectors implement vector related operations for
* * a series of elements.
* @param {Number} The index at which to insert a value. *
* @param {Object} The object to insert in the vector. * @constructor
* @memberOf Vector. */
*/ lunr.Vector = function () {
lunr.Vector.prototype.insert = function (idx, val) { this._magnitude = null
var list = this.list this.list = undefined
this.length = 0
}
if (!list) { /**
this.list = new lunr.Vector.Node (idx, val, list) * lunr.Vector.Node is a simple struct for each node
return this.length++ * in a lunr.Vector.
*
* @private
* @param {Number} The index of the node in the vector.
* @param {Object} The data at this node in the vector.
* @param {lunr.Vector.Node} The node directly after this node in the vector.
* @constructor
* @memberOf Vector
*/
lunr.Vector.Node = function (idx, val, next) {
this.idx = idx
this.val = val
this.next = next
} }
var prev = list, /**
next = list.next * Inserts a new value at a position in a vector.
*
* @param {Number} The index at which to insert a value.
* @param {Object} The object to insert in the vector.
* @memberOf Vector.
*/
lunr.Vector.prototype.insert = function (idx, val) {
var list = this.list
while (next != undefined) { if (!list) {
if (idx < next.idx) { this.list = new lunr.Vector.Node (idx, val, list)
prev.next = new lunr.Vector.Node (idx, val, next)
return this.length++ return this.length++
} }
prev = next, next = next.next var prev = list,
} next = list.next
prev.next = new lunr.Vector.Node (idx, val, next) while (next != undefined) {
return this.length++ if (idx < next.idx) {
} prev.next = new lunr.Vector.Node (idx, val, next)
return this.length++
}
/** prev = next, next = next.next
* Calculates the magnitude of this vector. }
*
* @returns {Number}
* @memberOf Vector
*/
lunr.Vector.prototype.magnitude = function () {
if (this._magniture) return this._magnitude
var node = this.list,
sumOfSquares = 0,
val
while (node) { prev.next = new lunr.Vector.Node (idx, val, next)
val = node.val return this.length++
sumOfSquares += val * val
node = node.next
} }
return this._magnitude = Math.sqrt(sumOfSquares) /**
} * Calculates the magnitude of this vector.
*
* @returns {Number}
* @memberOf Vector
*/
lunr.Vector.prototype.magnitude = function () {
if (this._magniture) return this._magnitude
var node = this.list,
sumOfSquares = 0,
val
while (node) {
val = node.val
sumOfSquares += val * val
node = node.next
}
/** return this._magnitude = Math.sqrt(sumOfSquares)
* Calculates the dot product of this vector and another vector. }
*
* @param {lunr.Vector} otherVector The vector to compute the dot product with.
* @returns {Number}
* @memberOf Vector
*/
lunr.Vector.prototype.dot = function (otherVector) {
var node = this.list,
otherNode = otherVector.list,
dotProduct = 0
while (node && otherNode) { /**
if (node.idx < otherNode.idx) { * Calculates the dot product of this vector and another vector.
node = node.next *
} else if (node.idx > otherNode.idx) { * @param {lunr.Vector} otherVector The vector to compute the dot product with.
otherNode = otherNode.next * @returns {Number}
} else { * @memberOf Vector
dotProduct += node.val * otherNode.val */
node = node.next lunr.Vector.prototype.dot = function (otherVector) {
otherNode = otherNode.next var node = this.list,
otherNode = otherVector.list,
dotProduct = 0
while (node && otherNode) {
if (node.idx < otherNode.idx) {
node = node.next
} else if (node.idx > otherNode.idx) {
otherNode = otherNode.next
} else {
dotProduct += node.val * otherNode.val
node = node.next
otherNode = otherNode.next
}
} }
return dotProduct
} }
return dotProduct /**
} * Calculates the cosine similarity between this vector and another
* vector.
*
* @param {lunr.Vector} otherVector The other vector to calculate the
* similarity with.
* @returns {Number}
* @memberOf Vector
*/
lunr.Vector.prototype.similarity = function (otherVector) {
return this.dot(otherVector) / (this.magnitude() * otherVector.magnitude())
}
/*!
* lunr.SortedSet
* Copyright (C) 2014 Oliver Nightingale
*/
/** /**
* Calculates the cosine similarity between this vector and another * lunr.SortedSets are used to maintain an array of uniq values in a sorted
* vector. * order.
* *
* @param {lunr.Vector} otherVector The other vector to calculate the * @constructor
* similarity with. */
* @returns {Number} lunr.SortedSet = function () {
* @memberOf Vector this.length = 0
*/ this.elements = []
lunr.Vector.prototype.similarity = function (otherVector) { }
return this.dot(otherVector) / (this.magnitude() * otherVector.magnitude())
}
/*!
* lunr.SortedSet
* Copyright (C) 2014 Oliver Nightingale
*/
/** /**
* lunr.SortedSets are used to maintain an array of uniq values in a sorted * Loads a previously serialised sorted set.
* order. *
* * @param {Array} serialisedData The serialised set to load.
* @constructor * @returns {lunr.SortedSet}
*/ * @memberOf SortedSet
lunr.SortedSet = function () { */
this.length = 0 lunr.SortedSet.load = function (serialisedData) {
this.elements = [] var set = new this
}
/** set.elements = serialisedData
* Loads a previously serialised sorted set. set.length = serialisedData.length
*
* @param {Array} serialisedData The serialised set to load.
* @returns {lunr.SortedSet}
* @memberOf SortedSet
*/
lunr.SortedSet.load = function (serialisedData) {
var set = new this
set.elements = serialisedData return set
set.length = serialisedData.length }
return set /**
} * Inserts new items into the set in the correct position to maintain the
* order.
*
* @param {Object} The objects to add to this set.
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.add = function () {
Array.prototype.slice.call(arguments).forEach(function (element) {
if (~this.indexOf(element)) return
this.elements.splice(this.locationFor(element), 0, element)
}, this)
/** this.length = this.elements.length
* Inserts new items into the set in the correct position to maintain the }
* order.
*
* @param {Object} The objects to add to this set.
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.add = function () {
Array.prototype.slice.call(arguments).forEach(function (element) {
if (~this.indexOf(element)) return
this.elements.splice(this.locationFor(element), 0, element)
}, this)
this.length = this.elements.length /**
} * Converts this sorted set into an array.
*
* @returns {Array}
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.toArray = function () {
return this.elements.slice()
}
/** /**
* Converts this sorted set into an array. * Creates a new array with the results of calling a provided function on every
* * element in this sorted set.
* @returns {Array} *
* @memberOf SortedSet * Delegates to Array.prototype.map and has the same signature.
*/ *
lunr.SortedSet.prototype.toArray = function () { * @param {Function} fn The function that is called on each element of the
return this.elements.slice() * set.
} * @param {Object} ctx An optional object that can be used as the context
* for the function fn.
* @returns {Array}
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.map = function (fn, ctx) {
return this.elements.map(fn, ctx)
}
/** /**
* Creates a new array with the results of calling a provided function on every * Executes a provided function once per sorted set element.
* element in this sorted set. *
* * Delegates to Array.prototype.forEach and has the same signature.
* Delegates to Array.prototype.map and has the same signature. *
* * @param {Function} fn The function that is called on each element of the
* @param {Function} fn The function that is called on each element of the * set.
* set. * @param {Object} ctx An optional object that can be used as the context
* @param {Object} ctx An optional object that can be used as the context * @memberOf SortedSet
* for the function fn. * for the function fn.
* @returns {Array} */
* @memberOf SortedSet lunr.SortedSet.prototype.forEach = function (fn, ctx) {
*/ return this.elements.forEach(fn, ctx)
lunr.SortedSet.prototype.map = function (fn, ctx) { }
return this.elements.map(fn, ctx)
}
/** /**
* Executes a provided function once per sorted set element. * Returns the index at which a given element can be found in the
* * sorted set, or -1 if it is not present.
* Delegates to Array.prototype.forEach and has the same signature. *
* * @param {Object} elem The object to locate in the sorted set.
* @param {Function} fn The function that is called on each element of the * @param {Number} start An optional index at which to start searching from
* set. * within the set.
* @param {Object} ctx An optional object that can be used as the context * @param {Number} end An optional index at which to stop search from within
* @memberOf SortedSet * the set.
* for the function fn. * @returns {Number}
*/ * @memberOf SortedSet
lunr.SortedSet.prototype.forEach = function (fn, ctx) { */
return this.elements.forEach(fn, ctx) lunr.SortedSet.prototype.indexOf = function (elem, start, end) {
} var start = start || 0,
end = end || this.elements.length,
sectionLength = end - start,
pivot = start + Math.floor(sectionLength / 2),
pivotElem = this.elements[pivot]
if (sectionLength <= 1) {
if (pivotElem === elem) {
return pivot
} else {
return -1
}
}
/** if (pivotElem < elem) return this.indexOf(elem, pivot, end)
* Returns the index at which a given element can be found in the if (pivotElem > elem) return this.indexOf(elem, start, pivot)
* sorted set, or -1 if it is not present. if (pivotElem === elem) return pivot
* }
* @param {Object} elem The object to locate in the sorted set.
* @param {Number} start An optional index at which to start searching from /**
* within the set. * Returns the position within the sorted set that an element should be
* @param {Number} end An optional index at which to stop search from within * inserted at to maintain the current order of the set.
* the set. *
* @returns {Number} * This function assumes that the element to search for does not already exist
* @memberOf SortedSet * in the sorted set.
*/ *
lunr.SortedSet.prototype.indexOf = function (elem, start, end) { * @param {Object} elem The elem to find the position for in the set
var start = start || 0, * @param {Number} start An optional index at which to start searching from
end = end || this.elements.length, * within the set.
sectionLength = end - start, * @param {Number} end An optional index at which to stop search from within
pivot = start + Math.floor(sectionLength / 2), * the set.
pivotElem = this.elements[pivot] * @returns {Number}
* @memberOf SortedSet
if (sectionLength <= 1) { */
if (pivotElem === elem) { lunr.SortedSet.prototype.locationFor = function (elem, start, end) {
return pivot var start = start || 0,
} else { end = end || this.elements.length,
return -1 sectionLength = end - start,
pivot = start + Math.floor(sectionLength / 2),
pivotElem = this.elements[pivot]
if (sectionLength <= 1) {
if (pivotElem > elem) return pivot
if (pivotElem < elem) return pivot + 1
} }
if (pivotElem < elem) return this.locationFor(elem, pivot, end)
if (pivotElem > elem) return this.locationFor(elem, start, pivot)
} }
if (pivotElem < elem) return this.indexOf(elem, pivot, end) /**
if (pivotElem > elem) return this.indexOf(elem, start, pivot) * Creates a new lunr.SortedSet that contains the elements in the intersection
if (pivotElem === elem) return pivot * of this set and the passed set.
} *
* @param {lunr.SortedSet} otherSet The set to intersect with this set.
* @returns {lunr.SortedSet}
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.intersect = function (otherSet) {
var intersectSet = new lunr.SortedSet,
i = 0, j = 0,
a_len = this.length, b_len = otherSet.length,
a = this.elements, b = otherSet.elements
while (true) {
if (i > a_len - 1 || j > b_len - 1) break
if (a[i] === b[j]) {
intersectSet.add(a[i])
i++, j++
continue
}
/** if (a[i] < b[j]) {
* Returns the position within the sorted set that an element should be i++
* inserted at to maintain the current order of the set. continue
* }
* This function assumes that the element to search for does not already exist
* in the sorted set.
*
* @param {Object} elem The elem to find the position for in the set
* @param {Number} start An optional index at which to start searching from
* within the set.
* @param {Number} end An optional index at which to stop search from within
* the set.
* @returns {Number}
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.locationFor = function (elem, start, end) {
var start = start || 0,
end = end || this.elements.length,
sectionLength = end - start,
pivot = start + Math.floor(sectionLength / 2),
pivotElem = this.elements[pivot]
if (sectionLength <= 1) { if (a[i] > b[j]) {
if (pivotElem > elem) return pivot j++
if (pivotElem < elem) return pivot + 1 continue
}
};
return intersectSet
} }
if (pivotElem < elem) return this.locationFor(elem, pivot, end) /**
if (pivotElem > elem) return this.locationFor(elem, start, pivot) * Makes a copy of this set
} *
* @returns {lunr.SortedSet}
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.clone = function () {
var clone = new lunr.SortedSet
/** clone.elements = this.toArray()
* Creates a new lunr.SortedSet that contains the elements in the intersection clone.length = clone.elements.length
* of this set and the passed set.
*
* @param {lunr.SortedSet} otherSet The set to intersect with this set.
* @returns {lunr.SortedSet}
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.intersect = function (otherSet) {
var intersectSet = new lunr.SortedSet,
i = 0, j = 0,
a_len = this.length, b_len = otherSet.length,
a = this.elements, b = otherSet.elements
while (true) {
if (i > a_len - 1 || j > b_len - 1) break
if (a[i] === b[j]) {
intersectSet.add(a[i])
i++, j++
continue
}
if (a[i] < b[j]) { return clone
i++ }
continue
}
if (a[i] > b[j]) { /**
j++ * Creates a new lunr.SortedSet that contains the elements in the union
continue * of this set and the passed set.
*
* @param {lunr.SortedSet} otherSet The set to union with this set.
* @returns {lunr.SortedSet}
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.union = function (otherSet) {
var longSet, shortSet, unionSet
if (this.length >= otherSet.length) {
longSet = this, shortSet = otherSet
} else {
longSet = otherSet, shortSet = this
} }
};
return intersectSet unionSet = longSet.clone()
}
/** unionSet.add.apply(unionSet, shortSet.toArray())
* Makes a copy of this set
*
* @returns {lunr.SortedSet}
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.clone = function () {
var clone = new lunr.SortedSet
clone.elements = this.toArray() return unionSet
clone.length = clone.elements.length }
return clone /**
} * Returns a representation of the sorted set ready for serialisation.
*
* @returns {Array}
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.toJSON = function () {
return this.toArray()
}
/*!
* lunr.Index
* Copyright (C) 2014 Oliver Nightingale
*/
/** /**
* Creates a new lunr.SortedSet that contains the elements in the union * lunr.Index is object that manages a search index. It contains the indexes
* of this set and the passed set. * and stores all the tokens and document lookups. It also provides the main
* * user facing API for the library.
* @param {lunr.SortedSet} otherSet The set to union with this set. *
* @returns {lunr.SortedSet} * @constructor
* @memberOf SortedSet */
*/ lunr.Index = function () {
lunr.SortedSet.prototype.union = function (otherSet) { this._fields = []
var longSet, shortSet, unionSet this._ref = 'id'
this.pipeline = new lunr.Pipeline
this.documentStore = new lunr.Store
this.tokenStore = new lunr.TokenStore
this.corpusTokens = new lunr.SortedSet
this.eventEmitter = new lunr.EventEmitter
if (this.length >= otherSet.length) { this._idfCache = {}
longSet = this, shortSet = otherSet
} else {
longSet = otherSet, shortSet = this
}
unionSet = longSet.clone() this.on('add', 'remove', 'update', (function () {
this._idfCache = {}
}).bind(this))
}
unionSet.add.apply(unionSet, shortSet.toArray()) /**
* Bind a handler to events being emitted by the index.
*
* The handler can be bound to many events at the same time.
*
* @param {String} [eventName] The name(s) of events to bind the function to.
* @param {Function} handler The serialised set to load.
* @memberOf Index
*/
lunr.Index.prototype.on = function () {
var args = Array.prototype.slice.call(arguments)
return this.eventEmitter.addListener.apply(this.eventEmitter, args)
}
return unionSet /**
} * Removes a handler from an event being emitted by the index.
*
* @param {String} eventName The name of events to remove the function from.
* @param {Function} handler The serialised set to load.
* @memberOf Index
*/
lunr.Index.prototype.off = function (name, fn) {
return this.eventEmitter.removeListener(name, fn)
}
/** /**
* Returns a representation of the sorted set ready for serialisation. * Loads a previously serialised index.
* *
* @returns {Array} * Issues a warning if the index being imported was serialised
* @memberOf SortedSet * by a different version of lunr.
*/ *
lunr.SortedSet.prototype.toJSON = function () { * @param {Object} serialisedData The serialised set to load.
return this.toArray() * @returns {lunr.Index}
} * @memberOf Index
/*! */
* lunr.Index lunr.Index.load = function (serialisedData) {
* Copyright (C) 2014 Oliver Nightingale if (serialisedData.version !== lunr.version) {
*/ lunr.utils.warn('version mismatch: current ' + lunr.version + ' importing ' + serialisedData.version)
}
/** var idx = new this
* lunr.Index is object that manages a search index. It contains the indexes
* and stores all the tokens and document lookups. It also provides the main
* user facing API for the library.
*
* @constructor
*/
lunr.Index = function () {
this._fields = []
this._ref = 'id'
this.pipeline = new lunr.Pipeline
this.documentStore = new lunr.Store
this.tokenStore = new lunr.TokenStore
this.corpusTokens = new lunr.SortedSet
this.eventEmitter = new lunr.EventEmitter
this._idfCache = {}
this.on('add', 'remove', 'update', (function () {
this._idfCache = {}
}).bind(this))
}
/** idx._fields = serialisedData.fields
* Bind a handler to events being emitted by the index. idx._ref = serialisedData.ref
*
* The handler can be bound to many events at the same time.
*
* @param {String} [eventName] The name(s) of events to bind the function to.
* @param {Function} handler The serialised set to load.
* @memberOf Index
*/
lunr.Index.prototype.on = function () {
var args = Array.prototype.slice.call(arguments)
return this.eventEmitter.addListener.apply(this.eventEmitter, args)
}
/** idx.documentStore = lunr.Store.load(serialisedData.documentStore)
* Removes a handler from an event being emitted by the index. idx.tokenStore = lunr.TokenStore.load(serialisedData.tokenStore)
* idx.corpusTokens = lunr.SortedSet.load(serialisedData.corpusTokens)
* @param {String} eventName The name of events to remove the function from. idx.pipeline = lunr.Pipeline.load(serialisedData.pipeline)
* @param {Function} handler The serialised set to load.
* @memberOf Index
*/
lunr.Index.prototype.off = function (name, fn) {
return this.eventEmitter.removeListener(name, fn)
}
/** return idx
* Loads a previously serialised index.
*
* Issues a warning if the index being imported was serialised
* by a different version of lunr.
*
* @param {Object} serialisedData The serialised set to load.
* @returns {lunr.Index}
* @memberOf Index
*/
lunr.Index.load = function (serialisedData) {
if (serialisedData.version !== lunr.version) {
lunr.utils.warn('version mismatch: current ' + lunr.version + ' importing ' + serialisedData.version)
} }
var idx = new this /**
* Adds a field to the list of fields that will be searchable within documents
idx._fields = serialisedData.fields * in the index.
idx._ref = serialisedData.ref *
* An optional boost param can be passed to affect how much tokens in this field
* rank in search results, by default the boost value is 1.
*
* Fields should be added before any documents are added to the index, fields
* that are added after documents are added to the index will only apply to new
* documents added to the index.
*
* @param {String} fieldName The name of the field within the document that
* should be indexed
* @param {Number} boost An optional boost that can be applied to terms in this
* field.
* @returns {lunr.Index}
* @memberOf Index
*/
lunr.Index.prototype.field = function (fieldName, opts) {
var opts = opts || {},
field = { name: fieldName, boost: opts.boost || 1 }
idx.documentStore = lunr.Store.load(serialisedData.documentStore) this._fields.push(field)
idx.tokenStore = lunr.TokenStore.load(serialisedData.tokenStore) return this
idx.corpusTokens = lunr.SortedSet.load(serialisedData.corpusTokens) }
idx.pipeline = lunr.Pipeline.load(serialisedData.pipeline)
return idx /**
} * Sets the property used to uniquely identify documents added to the index,
* by default this property is 'id'.
*
* This should only be changed before adding documents to the index, changing
* the ref property without resetting the index can lead to unexpected results.
*
* @param {String} refName The property to use to uniquely identify the
* documents in the index.
* @param {Boolean} emitEvent Whether to emit add events, defaults to true
* @returns {lunr.Index}
* @memberOf Index
*/
lunr.Index.prototype.ref = function (refName) {
this._ref = refName
return this
}
/** /**
* Adds a field to the list of fields that will be searchable within documents * Add a document to the index.
* in the index. *
* * This is the way new documents enter the index, this function will run the
* An optional boost param can be passed to affect how much tokens in this field * fields from the document through the index's pipeline and then add it to
* rank in search results, by default the boost value is 1. * the index, it will then show up in search results.
* *
* Fields should be added before any documents are added to the index, fields * An 'add' event is emitted with the document that has been added and the index
* that are added after documents are added to the index will only apply to new * the document has been added to. This event can be silenced by passing false
* documents added to the index. * as the second argument to add.
* *
* @param {String} fieldName The name of the field within the document that * @param {Object} doc The document to add to the index.
* should be indexed * @param {Boolean} emitEvent Whether or not to emit events, default true.
* @param {Number} boost An optional boost that can be applied to terms in this * @memberOf Index
* field. */
* @returns {lunr.Index} lunr.Index.prototype.add = function (doc, emitEvent) {
* @memberOf Index var docTokens = {},
*/ allDocumentTokens = new lunr.SortedSet,
lunr.Index.prototype.field = function (fieldName, opts) { docRef = doc[this._ref],
var opts = opts || {}, emitEvent = emitEvent === undefined ? true : emitEvent
field = { name: fieldName, boost: opts.boost || 1 }
this._fields.push(field) this._fields.forEach(function (field) {
return this var fieldTokens = this.pipeline.run(lunr.tokenizer(doc[field.name]))
}
/** docTokens[field.name] = fieldTokens
* Sets the property used to uniquely identify documents added to the index, lunr.SortedSet.prototype.add.apply(allDocumentTokens, fieldTokens)
* by default this property is 'id'. }, this)
*
* This should only be changed before adding documents to the index, changing
* the ref property without resetting the index can lead to unexpected results.
*
* @param {String} refName The property to use to uniquely identify the
* documents in the index.
* @param {Boolean} emitEvent Whether to emit add events, defaults to true
* @returns {lunr.Index}
* @memberOf Index
*/
lunr.Index.prototype.ref = function (refName) {
this._ref = refName
return this
}
/** this.documentStore.set(docRef, allDocumentTokens)
* Add a document to the index. lunr.SortedSet.prototype.add.apply(this.corpusTokens, allDocumentTokens.toArray())
*
* This is the way new documents enter the index, this function will run the
* fields from the document through the index's pipeline and then add it to
* the index, it will then show up in search results.
*
* An 'add' event is emitted with the document that has been added and the index
* the document has been added to. This event can be silenced by passing false
* as the second argument to add.
*
* @param {Object} doc The document to add to the index.
* @param {Boolean} emitEvent Whether or not to emit events, default true.
* @memberOf Index
*/
lunr.Index.prototype.add = function (doc, emitEvent) {
var docTokens = {},
allDocumentTokens = new lunr.SortedSet,
docRef = doc[this._ref],
emitEvent = emitEvent === undefined ? true : emitEvent
this._fields.forEach(function (field) { for (var i = 0; i < allDocumentTokens.length; i++) {
var fieldTokens = this.pipeline.run(lunr.tokenizer(doc[field.name])) var token = allDocumentTokens.elements[i]
var tf = this._fields.reduce(function (memo, field) {
var fieldLength = docTokens[field.name].length
docTokens[field.name] = fieldTokens if (!fieldLength) return memo
lunr.SortedSet.prototype.add.apply(allDocumentTokens, fieldTokens)
}, this)
this.documentStore.set(docRef, allDocumentTokens) var tokenCount = docTokens[field.name].filter(function (t) { return t === token }).length
lunr.SortedSet.prototype.add.apply(this.corpusTokens, allDocumentTokens.toArray())
for (var i = 0; i < allDocumentTokens.length; i++) { return memo + (tokenCount / fieldLength * field.boost)
var token = allDocumentTokens.elements[i] }, 0)
var tf = this._fields.reduce(function (memo, field) {
var fieldLength = docTokens[field.name].length
if (!fieldLength) return memo this.tokenStore.add(token, { ref: docRef, tf: tf })
};
var tokenCount = docTokens[field.name].filter(function (t) { return t === token }).length if (emitEvent) this.eventEmitter.emit('add', doc, this)
}
return memo + (tokenCount / fieldLength * field.boost) /**
}, 0) * Removes a document from the index.
*
* To make sure documents no longer show up in search results they can be
* removed from the index using this method.
*
* The document passed only needs to have the same ref property value as the
* document that was added to the index, they could be completely different
* objects.
*
* A 'remove' event is emitted with the document that has been removed and the index
* the document has been removed from. This event can be silenced by passing false
* as the second argument to remove.
*
* @param {Object} doc The document to remove from the index.
* @param {Boolean} emitEvent Whether to emit remove events, defaults to true
* @memberOf Index
*/
lunr.Index.prototype.remove = function (doc, emitEvent) {
var docRef = doc[this._ref],
emitEvent = emitEvent === undefined ? true : emitEvent
this.tokenStore.add(token, { ref: docRef, tf: tf }) if (!this.documentStore.has(docRef)) return
};
if (emitEvent) this.eventEmitter.emit('add', doc, this) var docTokens = this.documentStore.get(docRef)
}
/** this.documentStore.remove(docRef)
* Removes a document from the index.
*
* To make sure documents no longer show up in search results they can be
* removed from the index using this method.
*
* The document passed only needs to have the same ref property value as the
* document that was added to the index, they could be completely different
* objects.
*
* A 'remove' event is emitted with the document that has been removed and the index
* the document has been removed from. This event can be silenced by passing false
* as the second argument to remove.
*
* @param {Object} doc The document to remove from the index.
* @param {Boolean} emitEvent Whether to emit remove events, defaults to true
* @memberOf Index
*/
lunr.Index.prototype.remove = function (doc, emitEvent) {
var docRef = doc[this._ref],
emitEvent = emitEvent === undefined ? true : emitEvent
if (!this.documentStore.has(docRef)) return docTokens.forEach(function (token) {
this.tokenStore.remove(token, docRef)
}, this)
var docTokens = this.documentStore.get(docRef) if (emitEvent) this.eventEmitter.emit('remove', doc, this)
}
this.documentStore.remove(docRef) /**
* Updates a document in the index.
*
* When a document contained within the index gets updated, fields changed,
* added or removed, to make sure it correctly matched against search queries,
* it should be updated in the index.
*
* This method is just a wrapper around `remove` and `add`
*
* An 'update' event is emitted with the document that has been updated and the index.
* This event can be silenced by passing false as the second argument to update. Only
* an update event will be fired, the 'add' and 'remove' events of the underlying calls
* are silenced.
*
* @param {Object} doc The document to update in the index.
* @param {Boolean} emitEvent Whether to emit update events, defaults to true
* @see Index.prototype.remove
* @see Index.prototype.add
* @memberOf Index
*/
lunr.Index.prototype.update = function (doc, emitEvent) {
var emitEvent = emitEvent === undefined ? true : emitEvent
docTokens.forEach(function (token) { this.remove(doc, false)
this.tokenStore.remove(token, docRef) this.add(doc, false)
}, this)
if (emitEvent) this.eventEmitter.emit('remove', doc, this) if (emitEvent) this.eventEmitter.emit('update', doc, this)
} }
/** /**
* Updates a document in the index. * Calculates the inverse document frequency for a token within the index.
* *
* When a document contained within the index gets updated, fields changed, * @param {String} token The token to calculate the idf of.
* added or removed, to make sure it correctly matched against search queries, * @see Index.prototype.idf
* it should be updated in the index. * @private
* * @memberOf Index
* This method is just a wrapper around `remove` and `add` */
* lunr.Index.prototype.idf = function (term) {
* An 'update' event is emitted with the document that has been updated and the index. var cacheKey = "@" + term
* This event can be silenced by passing false as the second argument to update. Only if (Object.prototype.hasOwnProperty.call(this._idfCache, cacheKey)) return this._idfCache[cacheKey]
* an update event will be fired, the 'add' and 'remove' events of the underlying calls
* are silenced.
*
* @param {Object} doc The document to update in the index.
* @param {Boolean} emitEvent Whether to emit update events, defaults to true
* @see Index.prototype.remove
* @see Index.prototype.add
* @memberOf Index
*/
lunr.Index.prototype.update = function (doc, emitEvent) {
var emitEvent = emitEvent === undefined ? true : emitEvent
this.remove(doc, false) var documentFrequency = this.tokenStore.count(term),
this.add(doc, false) idf = 1
if (emitEvent) this.eventEmitter.emit('update', doc, this) if (documentFrequency > 0) {
} idf = 1 + Math.log(this.tokenStore.length / documentFrequency)
}
/** return this._idfCache[cacheKey] = idf
* Calculates the inverse document frequency for a token within the index. }
*
* @param {String} token The token to calculate the idf of.
* @see Index.prototype.idf
* @private
* @memberOf Index
*/
lunr.Index.prototype.idf = function (term) {
var cacheKey = "@" + term
if (Object.prototype.hasOwnProperty.call(this._idfCache, cacheKey)) return this._idfCache[cacheKey]
var documentFrequency = this.tokenStore.count(term), /**
idf = 1 * Searches the index using the passed query.
*
* Queries should be a string, multiple words are allowed and will lead to an
* AND based query, e.g. `idx.search('foo bar')` will run a search for
* documents containing both 'foo' and 'bar'.
*
* All query tokens are passed through the same pipeline that document tokens
* are passed through, so any language processing involved will be run on every
* query term.
*
* Each query term is expanded, so that the term 'he' might be expanded to
* 'hello' and 'help' if those terms were already included in the index.
*
* Matching documents are returned as an array of objects, each object contains
* the matching document ref, as set for this index, and the similarity score
* for this document against the query.
*
* @param {String} query The query to search the index with.
* @returns {Object}
* @see Index.prototype.idf
* @see Index.prototype.documentVector
* @memberOf Index
*/
lunr.Index.prototype.search = function (query) {
var queryTokens = this.pipeline.run(lunr.tokenizer(query)),
queryVector = new lunr.Vector,
documentSets = [],
fieldBoosts = this._fields.reduce(function (memo, f) { return memo + f.boost }, 0)
var hasSomeToken = queryTokens.some(function (token) {
return this.tokenStore.has(token)
}, this)
if (documentFrequency > 0) { if (!hasSomeToken) return []
idf = 1 + Math.log(this.tokenStore.length / documentFrequency)
}
return this._idfCache[cacheKey] = idf queryTokens
} .forEach(function (token, i, tokens) {
var tf = 1 / tokens.length * this._fields.length * fieldBoosts,
self = this
/** var set = this.tokenStore.expand(token).reduce(function (memo, key) {
* Searches the index using the passed query. var pos = self.corpusTokens.indexOf(key),
* idf = self.idf(key),
* Queries should be a string, multiple words are allowed and will lead to an similarityBoost = 1,
* AND based query, e.g. `idx.search('foo bar')` will run a search for set = new lunr.SortedSet
* documents containing both 'foo' and 'bar'.
*
* All query tokens are passed through the same pipeline that document tokens
* are passed through, so any language processing involved will be run on every
* query term.
*
* Each query term is expanded, so that the term 'he' might be expanded to
* 'hello' and 'help' if those terms were already included in the index.
*
* Matching documents are returned as an array of objects, each object contains
* the matching document ref, as set for this index, and the similarity score
* for this document against the query.
*
* @param {String} query The query to search the index with.
* @returns {Object}
* @see Index.prototype.idf
* @see Index.prototype.documentVector
* @memberOf Index
*/
lunr.Index.prototype.search = function (query) {
var queryTokens = this.pipeline.run(lunr.tokenizer(query)),
queryVector = new lunr.Vector,
documentSets = [],
fieldBoosts = this._fields.reduce(function (memo, f) { return memo + f.boost }, 0)
var hasSomeToken = queryTokens.some(function (token) {
return this.tokenStore.has(token)
}, this)
if (!hasSomeToken) return []
queryTokens
.forEach(function (token, i, tokens) {
var tf = 1 / tokens.length * this._fields.length * fieldBoosts,
self = this
var set = this.tokenStore.expand(token).reduce(function (memo, key) {
var pos = self.corpusTokens.indexOf(key),
idf = self.idf(key),
similarityBoost = 1,
set = new lunr.SortedSet
// if the expanded key is not an exact match to the token then
// penalise the score for this key by how different the key is
// to the token.
if (key !== token) {
var diff = Math.max(3, key.length - token.length)
similarityBoost = 1 / Math.log(diff)
}
// calculate the query tf-idf score for this token // if the expanded key is not an exact match to the token then
// applying an similarityBoost to ensure exact matches // penalise the score for this key by how different the key is
// these rank higher than expanded terms // to the token.
if (pos > -1) queryVector.insert(pos, tf * idf * similarityBoost) if (key !== token) {
var diff = Math.max(3, key.length - token.length)
similarityBoost = 1 / Math.log(diff)
}
// add all the documents that have this key into a set // calculate the query tf-idf score for this token
Object.keys(self.tokenStore.get(key)).forEach(function (ref) { set.add(ref) }) // applying an similarityBoost to ensure exact matches
// these rank higher than expanded terms
if (pos > -1) queryVector.insert(pos, tf * idf * similarityBoost)
return memo.union(set) // add all the documents that have this key into a set
}, new lunr.SortedSet) Object.keys(self.tokenStore.get(key)).forEach(function (ref) { set.add(ref) })
documentSets.push(set) return memo.union(set)
}, this) }, new lunr.SortedSet)
var documentSet = documentSets.reduce(function (memo, set) { documentSets.push(set)
return memo.intersect(set) }, this)
})
return documentSet var documentSet = documentSets.reduce(function (memo, set) {
.map(function (ref) { return memo.intersect(set)
return { ref: ref, score: queryVector.similarity(this.documentVector(ref)) }
}, this)
.sort(function (a, b) {
return b.score - a.score
}) })
}
/** return documentSet
* Generates a vector containing all the tokens in the document matching the .map(function (ref) {
* passed documentRef. return { ref: ref, score: queryVector.similarity(this.documentVector(ref)) }
* }, this)
* The vector contains the tf-idf score for each token contained in the .sort(function (a, b) {
* document with the passed documentRef. The vector will contain an element return b.score - a.score
* for every token in the indexes corpus, if the document does not contain that })
* token the element will be 0. }
*
* @param {Object} documentRef The ref to find the document with.
* @returns {lunr.Vector}
* @private
* @memberOf Index
*/
lunr.Index.prototype.documentVector = function (documentRef) {
var documentTokens = this.documentStore.get(documentRef),
documentTokensLength = documentTokens.length,
documentVector = new lunr.Vector
for (var i = 0; i < documentTokensLength; i++) { /**
var token = documentTokens.elements[i], * Generates a vector containing all the tokens in the document matching the
tf = this.tokenStore.get(token)[documentRef].tf, * passed documentRef.
idf = this.idf(token) *
* The vector contains the tf-idf score for each token contained in the
* document with the passed documentRef. The vector will contain an element
* for every token in the indexes corpus, if the document does not contain that
* token the element will be 0.
*
* @param {Object} documentRef The ref to find the document with.
* @returns {lunr.Vector}
* @private
* @memberOf Index
*/
lunr.Index.prototype.documentVector = function (documentRef) {
var documentTokens = this.documentStore.get(documentRef),
documentTokensLength = documentTokens.length,
documentVector = new lunr.Vector
documentVector.insert(this.corpusTokens.indexOf(token), tf * idf) for (var i = 0; i < documentTokensLength; i++) {
}; var token = documentTokens.elements[i],
tf = this.tokenStore.get(token)[documentRef].tf,
idf = this.idf(token)
return documentVector documentVector.insert(this.corpusTokens.indexOf(token), tf * idf)
} };
/** return documentVector
* Returns a representation of the index ready for serialisation. }
*
* @returns {Object}
* @memberOf Index
*/
lunr.Index.prototype.toJSON = function () {
return {
version: lunr.version,
fields: this._fields,
ref: this._ref,
documentStore: this.documentStore.toJSON(),
tokenStore: this.tokenStore.toJSON(),
corpusTokens: this.corpusTokens.toJSON(),
pipeline: this.pipeline.toJSON()
}
}
/** /**
* Applies a plugin to the current index. * Returns a representation of the index ready for serialisation.
* *
* A plugin is a function that is called with the index as its context. * @returns {Object}
* Plugins can be used to customise or extend the behaviour the index * @memberOf Index
* in some way. A plugin is just a function, that encapsulated the custom */
* behaviour that should be applied to the index. lunr.Index.prototype.toJSON = function () {
* return {
* The plugin function will be called with the index as its argument, additional version: lunr.version,
* arguments can also be passed when calling use. The function will be called fields: this._fields,
* with the index as its context. ref: this._ref,
* documentStore: this.documentStore.toJSON(),
* Example: tokenStore: this.tokenStore.toJSON(),
* corpusTokens: this.corpusTokens.toJSON(),
* var myPlugin = function (idx, arg1, arg2) { pipeline: this.pipeline.toJSON()
}
}
/**
* Applies a plugin to the current index.
*
* A plugin is a function that is called with the index as its context.
* Plugins can be used to customise or extend the behaviour the index
* in some way. A plugin is just a function, that encapsulated the custom
* behaviour that should be applied to the index.
*
* The plugin function will be called with the index as its argument, additional
* arguments can also be passed when calling use. The function will be called
* with the index as its context.
*
* Example:
*
* var myPlugin = function (idx, arg1, arg2) {
* // `this` is the index to be extended * // `this` is the index to be extended
* // apply any extensions etc here. * // apply any extensions etc here.
* } * }
* *
* var idx = lunr(function () { * var idx = lunr(function () {
* this.use(myPlugin, 'arg1', 'arg2') * this.use(myPlugin, 'arg1', 'arg2')
* }) * })
* *
* @param {Function} plugin The plugin to apply. * @param {Function} plugin The plugin to apply.
* @memberOf Index * @memberOf Index
*/ */
lunr.Index.prototype.use = function (plugin) { lunr.Index.prototype.use = function (plugin) {
var args = Array.prototype.slice.call(arguments, 1) var args = Array.prototype.slice.call(arguments, 1)
args.unshift(this) args.unshift(this)
plugin.apply(this, args) plugin.apply(this, args)
} }
/*! /*!
* lunr.Store * lunr.Store
* Copyright (C) 2014 Oliver Nightingale * Copyright (C) 2014 Oliver Nightingale
*/ */
/** /**
* lunr.Store is a simple key-value store used for storing sets of tokens for * lunr.Store is a simple key-value store used for storing sets of tokens for
* documents stored in index. * documents stored in index.
* *
* @constructor * @constructor
* @module * @module
*/ */
lunr.Store = function () { lunr.Store = function () {
this.store = {} this.store = {}
this.length = 0 this.length = 0
} }
/** /**
* Loads a previously serialised store * Loads a previously serialised store
* *
* @param {Object} serialisedData The serialised store to load. * @param {Object} serialisedData The serialised store to load.
* @returns {lunr.Store} * @returns {lunr.Store}
* @memberOf Store * @memberOf Store
*/ */
lunr.Store.load = function (serialisedData) { lunr.Store.load = function (serialisedData) {
var store = new this var store = new this
store.length = serialisedData.length store.length = serialisedData.length
store.store = Object.keys(serialisedData.store).reduce(function (memo, key) { store.store = Object.keys(serialisedData.store).reduce(function (memo, key) {
memo[key] = lunr.SortedSet.load(serialisedData.store[key]) memo[key] = lunr.SortedSet.load(serialisedData.store[key])
return memo return memo
}, {}) }, {})
return store return store
} }
/** /**
* Stores the given tokens in the store against the given id. * Stores the given tokens in the store against the given id.
* *
* @param {Object} id The key used to store the tokens against. * @param {Object} id The key used to store the tokens against.
* @param {Object} tokens The tokens to store against the key. * @param {Object} tokens The tokens to store against the key.
* @memberOf Store * @memberOf Store
*/ */
lunr.Store.prototype.set = function (id, tokens) { lunr.Store.prototype.set = function (id, tokens) {
this.store[id] = tokens if (!this.has(id)) this.length++
this.length = Object.keys(this.store).length this.store[id] = tokens
} }
/** /**
* Retrieves the tokens from the store for a given key. * Retrieves the tokens from the store for a given key.
* *
* @param {Object} id The key to lookup and retrieve from the store. * @param {Object} id The key to lookup and retrieve from the store.
* @returns {Object} * @returns {Object}
* @memberOf Store * @memberOf Store
*/ */
lunr.Store.prototype.get = function (id) { lunr.Store.prototype.get = function (id) {
return this.store[id] return this.store[id]
} }
/** /**
* Checks whether the store contains a key. * Checks whether the store contains a key.
* *
* @param {Object} id The id to look up in the store. * @param {Object} id The id to look up in the store.
* @returns {Boolean} * @returns {Boolean}
* @memberOf Store * @memberOf Store
*/ */
lunr.Store.prototype.has = function (id) { lunr.Store.prototype.has = function (id) {
return id in this.store return id in this.store
} }
/** /**
* Removes the value for a key in the store. * Removes the value for a key in the store.
* *
* @param {Object} id The id to remove from the store. * @param {Object} id The id to remove from the store.
* @memberOf Store * @memberOf Store
*/ */
lunr.Store.prototype.remove = function (id) { lunr.Store.prototype.remove = function (id) {
if (!this.has(id)) return if (!this.has(id)) return
delete this.store[id] delete this.store[id]
this.length-- this.length--
} }
/** /**
* Returns a representation of the store ready for serialisation. * Returns a representation of the store ready for serialisation.
* *
* @returns {Object} * @returns {Object}
* @memberOf Store * @memberOf Store
*/ */
lunr.Store.prototype.toJSON = function () { lunr.Store.prototype.toJSON = function () {
return { return {
store: this.store, store: this.store,
length: this.length length: this.length
}
} }
}
/*! /*!
* lunr.stemmer * lunr.stemmer
* Copyright (C) 2014 Oliver Nightingale * Copyright (C) 2014 Oliver Nightingale
* Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
*/ */
/** /**
* lunr.stemmer is an english language stemmer, this is a JavaScript * lunr.stemmer is an english language stemmer, this is a JavaScript
* implementation of the PorterStemmer taken from http://tartaurs.org/~martin * implementation of the PorterStemmer taken from http://tartaurs.org/~martin
* *
* @module * @module
* @param {String} str The string to stem * @param {String} str The string to stem
* @returns {String} * @returns {String}
* @see lunr.Pipeline * @see lunr.Pipeline
*/ */
lunr.stemmer = (function(){ lunr.stemmer = (function(){
var step2list = { var step2list = {
"ational" : "ate", "ational" : "ate",
"tional" : "tion", "tional" : "tion",
"enci" : "ence", "enci" : "ence",
"anci" : "ance", "anci" : "ance",
"izer" : "ize", "izer" : "ize",
"bli" : "ble", "bli" : "ble",
"alli" : "al", "alli" : "al",
"entli" : "ent", "entli" : "ent",
"eli" : "e", "eli" : "e",
"ousli" : "ous", "ousli" : "ous",
"ization" : "ize", "ization" : "ize",
"ation" : "ate", "ation" : "ate",
"ator" : "ate", "ator" : "ate",
"alism" : "al", "alism" : "al",
"iveness" : "ive", "iveness" : "ive",
"fulness" : "ful", "fulness" : "ful",
"ousness" : "ous", "ousness" : "ous",
"aliti" : "al", "aliti" : "al",
"iviti" : "ive", "iviti" : "ive",
"biliti" : "ble", "biliti" : "ble",
"logi" : "log" "logi" : "log"
}, },
step3list = { step3list = {
"icate" : "ic", "icate" : "ic",
"ative" : "", "ative" : "",
"alize" : "al", "alize" : "al",
"iciti" : "ic", "iciti" : "ic",
"ical" : "ic", "ical" : "ic",
"ful" : "", "ful" : "",
"ness" : "" "ness" : ""
}, },
c = "[^aeiou]", // consonant c = "[^aeiou]", // consonant
v = "[aeiouy]", // vowel v = "[aeiouy]", // vowel
C = c + "[^aeiouy]*", // consonant sequence C = c + "[^aeiouy]*", // consonant sequence
V = v + "[aeiou]*", // vowel sequence V = v + "[aeiou]*", // vowel sequence
mgr0 = "^(" + C + ")?" + V + C, // [C]VC... is m>0 mgr0 = "^(" + C + ")?" + V + C, // [C]VC... is m>0
meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$", // [C]VC[V] is m=1 meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$", // [C]VC[V] is m=1
mgr1 = "^(" + C + ")?" + V + C + V + C, // [C]VCVC... is m>1 mgr1 = "^(" + C + ")?" + V + C + V + C, // [C]VCVC... is m>1
s_v = "^(" + C + ")?" + v; // vowel in stem s_v = "^(" + C + ")?" + v; // vowel in stem
return function (w) { var re_mgr0 = new RegExp(mgr0);
var stem, var re_mgr1 = new RegExp(mgr1);
suffix, var re_meq1 = new RegExp(meq1);
firstch, var re_s_v = new RegExp(s_v);
re,
re2, var re_1a = /^(.+?)(ss|i)es$/;
re3, var re2_1a = /^(.+?)([^s])s$/;
re4; var re_1b = /^(.+?)eed$/;
var re2_1b = /^(.+?)(ed|ing)$/;
if (w.length < 3) { return w; } var re_1b_2 = /.$/;
var re2_1b_2 = /(at|bl|iz)$/;
firstch = w.substr(0,1); var re3_1b_2 = new RegExp("([^aeiouylsz])\\1$");
if (firstch == "y") { var re4_1b_2 = new RegExp("^" + C + v + "[^aeiouwxy]$");
w = firstch.toUpperCase() + w.substr(1);
} var re_1c = /^(.+?[^aeiou])y$/;
var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
var re2_4 = /^(.+?)(s|t)(ion)$/;
var re_5 = /^(.+?)e$/;
var re_5_1 = /ll$/;
var re3_5 = new RegExp("^" + C + v + "[^aeiouwxy]$");
var porterStemmer = function porterStemmer(w) {
var stem,
suffix,
firstch,
re,
re2,
re3,
re4;
if (w.length < 3) { return w; }
firstch = w.substr(0,1);
if (firstch == "y") {
w = firstch.toUpperCase() + w.substr(1);
}
// Step 1a // Step 1a
re = /^(.+?)(ss|i)es$/; re = re_1a
re2 = /^(.+?)([^s])s$/; re2 = re2_1a;
if (re.test(w)) { w = w.replace(re,"$1$2"); } if (re.test(w)) { w = w.replace(re,"$1$2"); }
else if (re2.test(w)) { w = w.replace(re2,"$1$2"); } else if (re2.test(w)) { w = w.replace(re2,"$1$2"); }
// Step 1b // Step 1b
re = /^(.+?)eed$/; re = re_1b;
re2 = /^(.+?)(ed|ing)$/; re2 = re2_1b;
if (re.test(w)) { if (re.test(w)) {
var fp = re.exec(w); var fp = re.exec(w);
re = new RegExp(mgr0); re = re_mgr0;
if (re.test(fp[1])) { if (re.test(fp[1])) {
re = /.$/; re = re_1b_2;
w = w.replace(re,""); w = w.replace(re,"");
}
} else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = re_s_v;
if (re2.test(stem)) {
w = stem;
re2 = re2_1b_2;
re3 = re3_1b_2;
re4 = re4_1b_2;
if (re2.test(w)) { w = w + "e"; }
else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,""); }
else if (re4.test(w)) { w = w + "e"; }
}
} }
} else if (re2.test(w)) {
var fp = re2.exec(w); // 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)
stem = fp[1]; re = re_1c;
re2 = new RegExp(s_v); if (re.test(w)) {
if (re2.test(stem)) { var fp = re.exec(w);
w = stem; stem = fp[1];
re2 = /(at|bl|iz)$/; w = stem + "i";
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w)) { w = w + "e"; }
else if (re3.test(w)) { re = /.$/; w = w.replace(re,""); }
else if (re4.test(w)) { w = w + "e"; }
} }
}
// Step 1c // Step 2
re = /^(.+?)y$/; re = re_2;
if (re.test(w)) { if (re.test(w)) {
var fp = re.exec(w); var fp = re.exec(w);
stem = fp[1]; stem = fp[1];
re = new RegExp(s_v); suffix = fp[2];
if (re.test(stem)) { w = stem + "i"; } re = re_mgr0;
} if (re.test(stem)) {
w = stem + step2list[suffix];
}
}
// Step 2 // Step 3
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; re = re_3;
if (re.test(w)) { if (re.test(w)) {
var fp = re.exec(w); var fp = re.exec(w);
stem = fp[1]; stem = fp[1];
suffix = fp[2]; suffix = fp[2];
re = new RegExp(mgr0); re = re_mgr0;
if (re.test(stem)) { if (re.test(stem)) {
w = stem + step2list[suffix]; w = stem + step3list[suffix];
}
} }
}
// Step 3 // Step 4
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; re = re_4;
if (re.test(w)) { re2 = re2_4;
var fp = re.exec(w); if (re.test(w)) {
stem = fp[1]; var fp = re.exec(w);
suffix = fp[2]; stem = fp[1];
re = new RegExp(mgr0); re = re_mgr1;
if (re.test(stem)) { if (re.test(stem)) {
w = stem + step3list[suffix]; w = stem;
}
} else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = re_mgr1;
if (re2.test(stem)) {
w = stem;
}
} }
}
// Step 4 // Step 5
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; re = re_5;
re2 = /^(.+?)(s|t)(ion)$/; if (re.test(w)) {
if (re.test(w)) { var fp = re.exec(w);
var fp = re.exec(w); stem = fp[1];
stem = fp[1]; re = re_mgr1;
re = new RegExp(mgr1); re2 = re_meq1;
if (re.test(stem)) { re3 = re3_5;
w = stem; if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {
w = stem;
}
} }
} else if (re2.test(w)) {
var fp = re2.exec(w); re = re_5_1;
stem = fp[1] + fp[2]; re2 = re_mgr1;
re2 = new RegExp(mgr1); if (re.test(w) && re2.test(w)) {
if (re2.test(stem)) { re = re_1b_2;
w = stem; w = w.replace(re,"");
} }
}
// Step 5 // and turn initial Y back to y
re = /^(.+?)e$/;
if (re.test(w)) { if (firstch == "y") {
var fp = re.exec(w); w = firstch.toLowerCase() + w.substr(1);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {
w = stem;
} }
}
re = /ll$/; return w;
re2 = new RegExp(mgr1); };
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y return porterStemmer;
})();
if (firstch == "y") { lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer')
w = firstch.toLowerCase() + w.substr(1); /*!
} * lunr.stopWordFilter
* Copyright (C) 2014 Oliver Nightingale
*/
return w; /**
* lunr.stopWordFilter is an English language stop word list filter, any words
* contained in the list will not be passed through the filter.
*
* This is intended to be used in the Pipeline. If the token does not pass the
* filter then undefined will be returned.
*
* @module
* @param {String} token The token to pass through the filter
* @returns {String}
* @see lunr.Pipeline
*/
lunr.stopWordFilter = function (token) {
if (lunr.stopWordFilter.stopWords.indexOf(token) === -1) return token
} }
})();
lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer') lunr.stopWordFilter.stopWords = new lunr.SortedSet
/*! lunr.stopWordFilter.stopWords.length = 119
* lunr.stopWordFilter lunr.stopWordFilter.stopWords.elements = [
* Copyright (C) 2014 Oliver Nightingale "",
*/ "a",
"able",
"about",
"across",
"after",
"all",
"almost",
"also",
"am",
"among",
"an",
"and",
"any",
"are",
"as",
"at",
"be",
"because",
"been",
"but",
"by",
"can",
"cannot",
"could",
"dear",
"did",
"do",
"does",
"either",
"else",
"ever",
"every",
"for",
"from",
"get",
"got",
"had",
"has",
"have",
"he",
"her",
"hers",
"him",
"his",
"how",
"however",
"i",
"if",
"in",
"into",
"is",
"it",
"its",
"just",
"least",
"let",
"like",
"likely",
"may",
"me",
"might",
"most",
"must",
"my",
"neither",
"no",
"nor",
"not",
"of",
"off",
"often",
"on",
"only",
"or",
"other",
"our",
"own",
"rather",
"said",
"say",
"says",
"she",
"should",
"since",
"so",
"some",
"than",
"that",
"the",
"their",
"them",
"then",
"there",
"these",
"they",
"this",
"tis",
"to",
"too",
"twas",
"us",
"wants",
"was",
"we",
"were",
"what",
"when",
"where",
"which",
"while",
"who",
"whom",
"why",
"will",
"with",
"would",
"yet",
"you",
"your"
]
lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter')
/*!
* lunr.trimmer
* Copyright (C) 2014 Oliver Nightingale
*/
/** /**
* lunr.stopWordFilter is an English language stop word list filter, any words * lunr.trimmer is a pipeline function for trimming non word
* contained in the list will not be passed through the filter. * characters from the begining and end of tokens before they
* * enter the index.
* This is intended to be used in the Pipeline. If the token does not pass the *
* filter then undefined will be returned. * This implementation may not work correctly for non latin
* * characters and should either be removed or adapted for use
* @module * with languages with non-latin characters.
* @param {String} token The token to pass through the filter *
* @returns {String} * @module
* @see lunr.Pipeline * @param {String} token The token to pass through the filter
*/ * @returns {String}
lunr.stopWordFilter = function (token) { * @see lunr.Pipeline
if (lunr.stopWordFilter.stopWords.indexOf(token) === -1) return token */
} lunr.trimmer = function (token) {
return token
lunr.stopWordFilter.stopWords = new lunr.SortedSet .replace(/^\W+/, '')
lunr.stopWordFilter.stopWords.length = 119 .replace(/\W+$/, '')
lunr.stopWordFilter.stopWords.elements = [ }
"",
"a",
"able",
"about",
"across",
"after",
"all",
"almost",
"also",
"am",
"among",
"an",
"and",
"any",
"are",
"as",
"at",
"be",
"because",
"been",
"but",
"by",
"can",
"cannot",
"could",
"dear",
"did",
"do",
"does",
"either",
"else",
"ever",
"every",
"for",
"from",
"get",
"got",
"had",
"has",
"have",
"he",
"her",
"hers",
"him",
"his",
"how",
"however",
"i",
"if",
"in",
"into",
"is",
"it",
"its",
"just",
"least",
"let",
"like",
"likely",
"may",
"me",
"might",
"most",
"must",
"my",
"neither",
"no",
"nor",
"not",
"of",
"off",
"often",
"on",
"only",
"or",
"other",
"our",
"own",
"rather",
"said",
"say",
"says",
"she",
"should",
"since",
"so",
"some",
"than",
"that",
"the",
"their",
"them",
"then",
"there",
"these",
"they",
"this",
"tis",
"to",
"too",
"twas",
"us",
"wants",
"was",
"we",
"were",
"what",
"when",
"where",
"which",
"while",
"who",
"whom",
"why",
"will",
"with",
"would",
"yet",
"you",
"your"
]
lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter')
/*!
* lunr.trimmer
* Copyright (C) 2014 Oliver Nightingale
*/
/** lunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer')
* lunr.trimmer is a pipeline function for trimming non word /*!
* characters from the begining and end of tokens before they * lunr.stemmer
* enter the index. * Copyright (C) 2014 Oliver Nightingale
* * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
* This implementation may not work correctly for non latin */
* characters and should either be removed or adapted for use
* with languages with non-latin characters.
*
* @module
* @param {String} token The token to pass through the filter
* @returns {String}
* @see lunr.Pipeline
*/
lunr.trimmer = function (token) {
return token
.replace(/^\W+/, '')
.replace(/\W+$/, '')
}
lunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer')
/*!
* lunr.stemmer
* Copyright (C) 2014 Oliver Nightingale
* Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
*/
/** /**
* lunr.TokenStore is used for efficient storing and lookup of the reverse * lunr.TokenStore is used for efficient storing and lookup of the reverse
* index of token to document ref. * index of token to document ref.
* *
* @constructor * @constructor
*/ */
lunr.TokenStore = function () { lunr.TokenStore = function () {
this.root = { docs: {} } this.root = { docs: {} }
this.length = 0 this.length = 0
} }
/** /**
* Loads a previously serialised token store * Loads a previously serialised token store
* *
* @param {Object} serialisedData The serialised token store to load. * @param {Object} serialisedData The serialised token store to load.
* @returns {lunr.TokenStore} * @returns {lunr.TokenStore}
* @memberOf TokenStore * @memberOf TokenStore
*/ */
lunr.TokenStore.load = function (serialisedData) { lunr.TokenStore.load = function (serialisedData) {
var store = new this var store = new this
store.root = serialisedData.root store.root = serialisedData.root
store.length = serialisedData.length store.length = serialisedData.length
return store return store
} }
/** /**
* Adds a new token doc pair to the store. * Adds a new token doc pair to the store.
* *
* By default this function starts at the root of the current store, however * By default this function starts at the root of the current store, however
* it can start at any node of any token store if required. * it can start at any node of any token store if required.
* *
* @param {String} token The token to store the doc under * @param {String} token The token to store the doc under
* @param {Object} doc The doc to store against the token * @param {Object} doc The doc to store against the token
* @param {Object} root An optional node at which to start looking for the * @param {Object} root An optional node at which to start looking for the
* correct place to enter the doc, by default the root of this lunr.TokenStore * correct place to enter the doc, by default the root of this lunr.TokenStore
* is used. * is used.
* @memberOf TokenStore * @memberOf TokenStore
*/ */
lunr.TokenStore.prototype.add = function (token, doc, root) { lunr.TokenStore.prototype.add = function (token, doc, root) {
var root = root || this.root, var root = root || this.root,
key = token[0], key = token[0],
rest = token.slice(1) rest = token.slice(1)
if (!(key in root)) root[key] = {docs: {}} if (!(key in root)) root[key] = {docs: {}}
if (rest.length === 0) { if (rest.length === 0) {
root[key].docs[doc.ref] = doc root[key].docs[doc.ref] = doc
this.length += 1 this.length += 1
return return
} else { } else {
return this.add(rest, doc, root[key]) return this.add(rest, doc, root[key])
}
} }
}
/** /**
* Checks whether this key is contained within this lunr.TokenStore. * Checks whether this key is contained within this lunr.TokenStore.
* *
* By default this function starts at the root of the current store, however * By default this function starts at the root of the current store, however
* it can start at any node of any token store if required. * it can start at any node of any token store if required.
* *
* @param {String} token The token to check for * @param {String} token The token to check for
* @param {Object} root An optional node at which to start * @param {Object} root An optional node at which to start
* @memberOf TokenStore * @memberOf TokenStore
*/ */
lunr.TokenStore.prototype.has = function (token) { lunr.TokenStore.prototype.has = function (token) {
if (!token) return false if (!token) return false
var node = this.root
var node = this.root for (var i = 0; i < token.length; i++) {
if (!node[token[i]]) return false
for (var i = 0; i < token.length; i++) { node = node[token[i]]
if (!node[token[i]]) return false }
node = node[token[i]] return true
} }
return true /**
} * Retrieve a node from the token store for a given token.
*
* By default this function starts at the root of the current store, however
* it can start at any node of any token store if required.
*
* @param {String} token The token to get the node for.
* @param {Object} root An optional node at which to start.
* @returns {Object}
* @see TokenStore.prototype.get
* @memberOf TokenStore
*/
lunr.TokenStore.prototype.getNode = function (token) {
if (!token) return {}
/** var node = this.root
* Retrieve a node from the token store for a given token.
*
* By default this function starts at the root of the current store, however
* it can start at any node of any token store if required.
*
* @param {String} token The token to get the node for.
* @param {Object} root An optional node at which to start.
* @returns {Object}
* @see TokenStore.prototype.get
* @memberOf TokenStore
*/
lunr.TokenStore.prototype.getNode = function (token) {
if (!token) return {}
var node = this.root for (var i = 0; i < token.length; i++) {
if (!node[token[i]]) return {}
for (var i = 0; i < token.length; i++) { node = node[token[i]]
if (!node[token[i]]) return {} }
node = node[token[i]] return node
} }
return node /**
} * Retrieve the documents for a node for the given token.
*
* By default this function starts at the root of the current store, however
* it can start at any node of any token store if required.
*
* @param {String} token The token to get the documents for.
* @param {Object} root An optional node at which to start.
* @returns {Object}
* @memberOf TokenStore
*/
lunr.TokenStore.prototype.get = function (token, root) {
return this.getNode(token, root).docs || {}
}
/** lunr.TokenStore.prototype.count = function (token, root) {
* Retrieve the documents for a node for the given token. return Object.keys(this.get(token, root)).length
* }
* By default this function starts at the root of the current store, however
* it can start at any node of any token store if required.
*
* @param {String} token The token to get the documents for.
* @param {Object} root An optional node at which to start.
* @returns {Object}
* @memberOf TokenStore
*/
lunr.TokenStore.prototype.get = function (token, root) {
return this.getNode(token, root).docs || {}
}
lunr.TokenStore.prototype.count = function (token, root) { /**
return Object.keys(this.get(token, root)).length * Remove the document identified by ref from the token in the store.
} *
* By default this function starts at the root of the current store, however
* it can start at any node of any token store if required.
*
* @param {String} token The token to get the documents for.
* @param {String} ref The ref of the document to remove from this token.
* @param {Object} root An optional node at which to start.
* @returns {Object}
* @memberOf TokenStore
*/
lunr.TokenStore.prototype.remove = function (token, ref) {
if (!token) return
var node = this.root
/** for (var i = 0; i < token.length; i++) {
* Remove the document identified by ref from the token in the store. if (!(token[i] in node)) return
* node = node[token[i]]
* By default this function starts at the root of the current store, however }
* it can start at any node of any token store if required.
*
* @param {String} token The token to get the documents for.
* @param {String} ref The ref of the document to remove from this token.
* @param {Object} root An optional node at which to start.
* @returns {Object}
* @memberOf TokenStore
*/
lunr.TokenStore.prototype.remove = function (token, ref) {
if (!token) return
var node = this.root
for (var i = 0; i < token.length; i++) { delete node.docs[ref]
if (!(token[i] in node)) return
node = node[token[i]]
} }
delete node.docs[ref] /**
} * Find all the possible suffixes of the passed token using tokens
* currently in the store.
/** *
* Find all the possible suffixes of the passed token using tokens * @param {String} token The token to expand.
* currently in the store. * @returns {Array}
* * @memberOf TokenStore
* @param {String} token The token to expand. */
* @returns {Array} lunr.TokenStore.prototype.expand = function (token, memo) {
* @memberOf TokenStore var root = this.getNode(token),
*/ docs = root.docs || {},
lunr.TokenStore.prototype.expand = function (token, memo) { memo = memo || []
var root = this.getNode(token),
docs = root.docs || {},
memo = memo || []
if (Object.keys(docs).length) memo.push(token) if (Object.keys(docs).length) memo.push(token)
Object.keys(root) Object.keys(root)
.forEach(function (key) { .forEach(function (key) {
if (key === 'docs') return if (key === 'docs') return
memo.concat(this.expand(token + key, memo)) memo.concat(this.expand(token + key, memo))
}, this) }, this)
return memo return memo
} }
/** /**
* Returns a representation of the token store ready for serialisation. * Returns a representation of the token store ready for serialisation.
* *
* @returns {Object} * @returns {Object}
* @memberOf TokenStore * @memberOf TokenStore
*/ */
lunr.TokenStore.prototype.toJSON = function () { lunr.TokenStore.prototype.toJSON = function () {
return { return {
root: this.root, root: this.root,
length: this.length length: this.length
}
} }
}
/** /**
* export the module via AMD, CommonnJS or as a browser global * export the module via AMD, CommonJS or as a browser global
* Export code from https://github.com/umdjs/umd/blob/master/returnExports.js * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js
*/ */
;(function (root, factory) { ;(function (root, factory) {
......
...@@ -24,7 +24,7 @@ under the License. ...@@ -24,7 +24,7 @@ under the License.
<%= stylesheet_link_tag :screen, media: :screen %> <%= stylesheet_link_tag :screen, media: :screen %>
<%= stylesheet_link_tag :print, media: :print %> <%= stylesheet_link_tag :print, media: :print %>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<% if current_page.data.search %> <% if current_page.data.search %>
<%= javascript_include_tag "all" %> <%= javascript_include_tag "all" %>
<% else %> <% else %>
......
/*! normalize.css v2.0.1 | MIT License | git.io/normalize */ /*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/* ========================================================================== /**
HTML5 display definitions * 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */ ========================================================================== */
/* /**
* Corrects `block` display not defined in IE 8/9. * Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/ */
article, article,
...@@ -16,24 +38,29 @@ figure, ...@@ -16,24 +38,29 @@ figure,
footer, footer,
header, header,
hgroup, hgroup,
main,
menu,
nav, nav,
section, section,
summary { summary {
display: block; display: block;
} }
/* /**
* Corrects `inline-block` display not defined in IE 8/9. * 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/ */
audio, audio,
canvas, canvas,
progress,
video { video {
display: inline-block; display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
} }
/* /**
* Prevents modern browsers from displaying `audio` without controls. * Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices. * Remove excess height in iOS 5 devices.
*/ */
...@@ -42,52 +69,29 @@ audio:not([controls]) { ...@@ -42,52 +69,29 @@ audio:not([controls]) {
height: 0; height: 0;
} }
/* /**
* Addresses styling for `hidden` attribute not present in IE 8/9. * Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/ */
[hidden] { [hidden],
template {
display: none; display: none;
} }
/* ========================================================================== /* Links
Base
========================================================================== */ ========================================================================== */
/* /**
* 1. Sets default font family to sans-serif. * Remove the gray background color from active links in IE 10.
* 2. Prevents iOS text size adjust after orientation change, without disabling
* user zoom.
*/ */
html { a {
font-family: sans-serif; /* 1 */ background-color: transparent;
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
} }
/* /**
* Removes default margin. * Improve readability when focused and also mouse hovered in all browsers.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/*
* Addresses `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers.
*/ */
a:active, a:active,
...@@ -95,29 +99,19 @@ a:hover { ...@@ -95,29 +99,19 @@ a:hover {
outline: 0; outline: 0;
} }
/* ========================================================================== /* Text-level semantics
Typography
========================================================================== */ ========================================================================== */
/* /**
* Addresses `h1` font sizes within `section` and `article` in Firefox 4+, * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
* Safari 5, and Chrome.
*/
h1 {
font-size: 2em;
}
/*
* Addresses styling not present in IE 8/9, Safari 5, and Chrome.
*/ */
abbr[title] { abbr[title] {
border-bottom: 1px dotted; border-bottom: 1px dotted;
} }
/* /**
* Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome. * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/ */
b, b,
...@@ -125,64 +119,43 @@ strong { ...@@ -125,64 +119,43 @@ strong {
font-weight: bold; font-weight: bold;
} }
/* /**
* Addresses styling not present in Safari 5 and Chrome. * Address styling not present in Safari and Chrome.
*/ */
dfn { dfn {
font-style: italic; font-style: italic;
} }
/* /**
* Addresses styling not present in IE 8/9. * Address variable `h1` font-size and margin within `section` and `article`
*/ * contexts in Firefox 4+, Safari, and Chrome.
mark {
background: #ff0;
color: #000;
}
/*
* Corrects font family set oddly in Safari 5 and Chrome.
*/ */
code, h1 {
kbd, font-size: 2em;
pre, margin: 0.67em 0;
samp {
font-family: monospace, serif;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
} }
/* /**
* Sets consistent quote types. * Address styling not present in IE 8/9.
*/ */
q { mark {
quotes: "\201C" "\201D" "\2018" "\2019"; background: #ff0;
color: #000;
} }
/* /**
* Addresses inconsistent and variable font size in all browsers. * Address inconsistent and variable font size in all browsers.
*/ */
small { small {
font-size: 80%; font-size: 80%;
} }
/* /**
* Prevents `sub` and `sup` affecting `line-height` in all browsers. * Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/ */
sub, sub,
...@@ -201,92 +174,116 @@ sub { ...@@ -201,92 +174,116 @@ sub {
bottom: -0.25em; bottom: -0.25em;
} }
/* ========================================================================== /* Embedded content
Embedded content
========================================================================== */ ========================================================================== */
/* /**
* Removes border when inside `a` element in IE 8/9. * Remove border when inside `a` element in IE 8/9/10.
*/ */
img { img {
border: 0; border: 0;
} }
/* /**
* Corrects overflow displayed oddly in IE 9. * Correct overflow not hidden in IE 9/10/11.
*/ */
svg:not(:root) { svg:not(:root) {
overflow: hidden; overflow: hidden;
} }
/* ========================================================================== /* Grouping content
Figures
========================================================================== */ ========================================================================== */
/* /**
* Addresses margin not present in IE 8/9 and Safari 5. * Address margin not present in IE 8/9 and Safari.
*/ */
figure { figure {
margin: 0; margin: 1em 40px;
} }
/* ========================================================================== /**
Forms * Address differences between Firefox and other browsers.
========================================================================== */ */
/* hr {
* Define consistent border, margin, and padding. -moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/ */
fieldset { pre {
border: 1px solid #c0c0c0; overflow: auto;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
} }
/* /**
* 1. Corrects color not being inherited in IE 8/9. * Address odd `em`-unit font size rendering in all browsers.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/ */
legend { code,
border: 0; /* 1 */ kbd,
padding: 0; /* 2 */ pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
} }
/* /* Forms
* 1. Corrects font family not being inherited in all browsers. ========================================================================== */
* 2. Corrects font size not being inherited in all browsers.
* 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome /**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/ */
button, button,
input, input,
optgroup,
select, select,
textarea { textarea {
font-family: inherit; /* 1 */ color: inherit; /* 1 */
font-size: 100%; /* 2 */ font: inherit; /* 2 */
margin: 0; /* 3 */ margin: 0; /* 3 */
} }
/* /**
* Addresses Firefox 4+ setting `line-height` on `input` using `!important` in * Address `overflow` set to `hidden` in IE 8/9/10/11.
* the UA stylesheet. */
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/ */
button, button,
input { select {
line-height: normal; text-transform: none;
} }
/* /**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls. * and `video` controls.
* 2. Corrects inability to style clickable `input` types in iOS. * 2. Correct inability to style clickable `input` types in iOS.
* 3. Improves usability and consistency of cursor style between image-type * 3. Improve usability and consistency of cursor style between image-type
* `input` and others. * `input` and others.
*/ */
...@@ -298,18 +295,40 @@ input[type="submit"] { ...@@ -298,18 +295,40 @@ input[type="submit"] {
cursor: pointer; /* 3 */ cursor: pointer; /* 3 */
} }
/* /**
* Re-set default cursor for disabled elements. * Re-set default cursor for disabled elements.
*/ */
button[disabled], button[disabled],
input[disabled] { html input[disabled] {
cursor: default; cursor: default;
} }
/* /**
* 1. Addresses box sizing set to `content-box` in IE 8/9. * Remove inner padding and border in Firefox 4+.
* 2. Removes excess padding in IE 8/9. */
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/ */
input[type="checkbox"], input[type="checkbox"],
...@@ -318,9 +337,20 @@ input[type="radio"] { ...@@ -318,9 +337,20 @@ input[type="radio"] {
padding: 0; /* 2 */ padding: 0; /* 2 */
} }
/* /**
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. * Fix the cursor style for Chrome's increment/decrement buttons. For certain
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome * `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
* (include `-moz` to future-proof). * (include `-moz` to future-proof).
*/ */
...@@ -331,9 +361,10 @@ input[type="search"] { ...@@ -331,9 +361,10 @@ input[type="search"] {
box-sizing: content-box; box-sizing: content-box;
} }
/* /**
* Removes inner padding and search cancel button in Safari 5 and Chrome * Remove inner padding and search cancel button in Safari and Chrome on OS X.
* on OS X. * Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/ */
input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-cancel-button,
...@@ -341,35 +372,56 @@ input[type="search"]::-webkit-search-decoration { ...@@ -341,35 +372,56 @@ input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none; -webkit-appearance: none;
} }
/* /**
* Removes inner padding and border in Firefox 4+. * Define consistent border, margin, and padding.
*/ */
button::-moz-focus-inner, fieldset {
input::-moz-focus-inner { border: 1px solid #c0c0c0;
border: 0; margin: 0 2px;
padding: 0; padding: 0.35em 0.625em 0.75em;
} }
/* /**
* 1. Removes default vertical scrollbar in IE 8/9. * 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Improves readability and alignment in all browsers. * 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/ */
textarea { textarea {
overflow: auto; /* 1 */ overflow: auto;
vertical-align: top; /* 2 */
} }
/* ========================================================================== /**
Tables * Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */ ========================================================================== */
/* /**
* Remove most spacing between table cells. * Remove most spacing between table cells.
*/ */
table { table {
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0; border-spacing: 0;
} }
\ No newline at end of file
td,
th {
padding: 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment