itemsjs
Version:
Created to perform fast search on small json dataset (up to 1000 elements).
1,689 lines (1,431 loc) • 154 kB
HTML
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>ItemsJS</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="robots" content="noindex, nofollow">
<meta name="googlebot" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script
type="text/javascript"
src="/js/lib/dummy.js"
></script>
<link rel="stylesheet" type="text/css" href="/css/result-light.css">
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.0.0/css/bootstrap.min.css">
<script type="text/javascript" src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/itemsapi/itemsapi-example-data/master/jsfiddle/imdb.js"></script>
<style id="compiled-css" type="text/css">
/* EOS */
</style>
<script id="insert"></script>
</head>
<body>
<div id="el">
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#" v-on:click="reset()">ItemsJS movies</a>
</div>
<div id="navbar">
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" v-model="query" class="form-control" placeholder="Search">
</div>
</form>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container" style="margin-top: 50px;">
<h1>List of items ({{ searchResult.pagination.total }})</h1>
<p class="text-muted">Search performed in {{ searchResult.timings.search }} ms, facets in {{ searchResult.timings.facets }} ms</p>
<div class="breadcrumbs" style="margin: 0 0 10px 0; " v-if="selected_filters.length">
<span v-for="filter in selected_filters" class="label label-default" v-on:click="remove_filter(filter.facet, filter.name)" style="margin-right: 5px; cursor: pointer;">{{ filter.name }}
<span class="glyphicon glyphicon-remove"></span>
</span>
<span class="label label-default pull-right" v-on:click="reset()" style="cursor: pointer; margin-right: 0;">Clear filters
<span class="glyphicon glyphicon-remove"></span>
</span>
</div>
<div class="clearfix"></div>
<div class="" style="margin-bottom: 10px;">
<select class="form-control pull-left" style="width: auto; margin-top: 0px;" v-model="sort">
<option disabled value="">Select sort</option>
<option v-for="option in sort_keys" v-bind:value="option">
{{ option }}
</option>
</select>
<select class="form-control pull-left" style="width: auto; margin-top: 0px; margin-left: 5px;" v-model="per_page">
<option disabled value="">Select listing size</option>
<option value="10">10</option>
<option value="20">20</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
<div class="clearfix"></div>
</div>
<div class="clearfix"></div>
<div class="row">
<div class="col-md-2 col-xs-2">
<div v-for="facet in searchResult.data.aggregations">
<h5 style="margin-bottom: 5px;"><strong style="color: #337ab7;">{{ facet.title }}</strong></h5>
<ul class="browse-list list-unstyled long-list" style="margin-bottom: 0;">
<li v-for="bucket in facet.buckets">
<div class="checkbox block" style="margin-top: 0; margin-bottom: 0;">
<label>
<input class="checkbox" type="checkbox" v-model="filters[facet.name]" v-bind:value="bucket.key">
{{ bucket.key }} ({{ bucket.doc_count }})
</label>
</div>
</li>
</ul>
</div>
</div>
<div class="col-md-10 col-xs-10">
<div class="breadcrumbs"></div>
<div class="clearfix"></div>
<!--<h3>List of items ({{ searchResult.pagination.total }})</h3>-->
<table class="table table-striped">
<tbody>
<tr v-for="item of searchResult.data.items">
<td><img style="width: 100px;" v-bind:src="item.image"></td>
<td></td>
<td>
<b>{{ item.name }}</b>
<br />
{{ item.description }}
</td>
<td>{{ item.year }}</td>
<td>
<span style="font-size: 12px; display: block; float: left; background-color: #dbebf2; border-radius: 5px; padding: 1px 3px 1px 3px; margin: 2px;" v-for="tag in item.tags">{{ tag }}</span>
</td>
</tr>
</tbody>
</table>
<div class="clearfix"></div>
</div>
<div class="clearfix" style="margin-bottom: 100px;"></div>
</div>
</div>
</div>
<script type="text/javascript">//<![CDATA[
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.itemsjs = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
'use strict';
module.exports = require('./src/index');
},{"./src/index":7}],2:[function(require,module,exports){
/* FastBitSet.js : a fast bit set implementation in JavaScript.
* (c) the authors
* Licensed under the Apache License, Version 2.0.
*
* Speed-optimized BitSet implementation for modern browsers and JavaScript engines.
*
* A BitSet is an ideal data structure to implement a Set when values being stored are
* reasonably small integers. It can be orders of magnitude faster than a generic set implementation.
* The FastBitSet implementation optimizes for speed, leveraging commonly available features
* like typed arrays.
*
* Simple usage :
* // const FastBitSet = require("fastbitset");// if you use node
* const b = new FastBitSet();// initially empty
* b.add(1);// add the value "1"
* b.has(1); // check that the value is present! (will return true)
* b.add(2);
* console.log(""+b);// should display {1,2}
* b.add(10);
* b.array(); // would return [1,2,10]
*
* let c = new FastBitSet([1,2,3,10]); // create bitset initialized with values 1,2,3,10
* c.difference(b); // from c, remove elements that are in b (modifies c)
* c.difference2(b); // from c, remove elements that are in b (modifies b)
* c.change(b); // c will contain elements that are in b or in c, but not both
* const su = c.union_size(b);// compute the size of the union (bitsets are unchanged)
* c.union(b); // c will contain all elements that are in c and b
* const s1 = c.intersection_size(b);// compute the size of the intersection (bitsets are unchanged)
* c.intersection(b); // c will only contain elements that are in both c and b
* c = b.clone(); // create a (deep) copy of b and assign it to c.
* c.equals(b); // check whether c and b are equal
*
* See README.md file for a more complete description.
*
* You can install the library under node with the command line
* npm install fastbitset
*/
"use strict";
// you can provide an iterable
function FastBitSet(iterable) {
this.words = [];
if (iterable) {
if (Symbol && Symbol.iterator && iterable[Symbol.iterator] !== undefined) {
const iterator = iterable[Symbol.iterator]();
let current = iterator.next();
while (!current.done) {
this.add(current.value);
current = iterator.next();
}
} else {
for (let i = 0; i < iterable.length; i++) {
this.add(iterable[i]);
}
}
}
}
// Creates a bitmap from words
FastBitSet.prototype.fromWords = function (words) {
const bitSet = Object.create(FastBitSet.prototype);
bitSet.words = words;
return bitSet;
};
// Add the value (Set the bit at index to true)
FastBitSet.prototype.add = function (index) {
this.resize(index);
this.words[index >>> 5] |= 1 << index;
};
// If the value was not in the set, add it, otherwise remove it (flip bit at index)
FastBitSet.prototype.flip = function (index) {
this.resize(index);
this.words[index >>> 5] ^= 1 << index;
};
// Remove all values, reset memory usage
FastBitSet.prototype.clear = function () {
this.words.length = 0;
};
// Set the bit at index to false
FastBitSet.prototype.remove = function (index) {
this.resize(index);
this.words[index >>> 5] &= ~(1 << index);
};
// Return true if no bit is set
FastBitSet.prototype.isEmpty = function (index) {
const c = this.words.length;
for (let i = 0; i < c; i++) {
if (this.words[i] !== 0) return false;
}
return true;
};
// Is the value contained in the set? Is the bit at index true or false? Returns a boolean
FastBitSet.prototype.has = function (index) {
return (this.words[index >>> 5] & (1 << index)) !== 0;
};
// Tries to add the value (Set the bit at index to true), return 1 if the
// value was added, return 0 if the value was already present
FastBitSet.prototype.checkedAdd = function (index) {
this.resize(index);
const word = this.words[index >>> 5];
const newword = word | (1 << index);
this.words[index >>> 5] = newword;
return (newword ^ word) >>> index;
};
// Reduce the memory usage to a minimum
FastBitSet.prototype.trim = function (index) {
let nl = this.words.length;
while (nl > 0 && this.words[nl - 1] === 0) {
nl--;
}
this.words = this.words.slice(0, nl);
};
// Resize the bitset so that we can write a value at index
FastBitSet.prototype.resize = function (index) {
const count = (index + 32) >>> 5; // just what is needed
for (let i = this.words.length; i < count; i++) this.words[i] = 0;
};
// fast function to compute the Hamming weight of a 32-bit unsigned integer
FastBitSet.prototype.hammingWeight = function (v) {
v -= (v >>> 1) & 0x55555555; // works with signed or unsigned shifts
v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
return (((v + (v >>> 4)) & 0xf0f0f0f) * 0x1010101) >>> 24;
};
// fast function to compute the Hamming weight of four 32-bit unsigned integers
FastBitSet.prototype.hammingWeight4 = function (v1, v2, v3, v4) {
v1 -= (v1 >>> 1) & 0x55555555; // works with signed or unsigned shifts
v2 -= (v2 >>> 1) & 0x55555555; // works with signed or unsigned shifts
v3 -= (v3 >>> 1) & 0x55555555; // works with signed or unsigned shifts
v4 -= (v4 >>> 1) & 0x55555555; // works with signed or unsigned shifts
v1 = (v1 & 0x33333333) + ((v1 >>> 2) & 0x33333333);
v2 = (v2 & 0x33333333) + ((v2 >>> 2) & 0x33333333);
v3 = (v3 & 0x33333333) + ((v3 >>> 2) & 0x33333333);
v4 = (v4 & 0x33333333) + ((v4 >>> 2) & 0x33333333);
v1 = (v1 + (v1 >>> 4)) & 0xf0f0f0f;
v2 = (v2 + (v2 >>> 4)) & 0xf0f0f0f;
v3 = (v3 + (v3 >>> 4)) & 0xf0f0f0f;
v4 = (v4 + (v4 >>> 4)) & 0xf0f0f0f;
return ((v1 + v2 + v3 + v4) * 0x1010101) >>> 24;
};
// How many values stored in the set? How many set bits?
FastBitSet.prototype.size = function () {
let answer = 0;
const c = this.words.length;
const w = this.words;
for (let i = 0; i < c; i++) {
answer += this.hammingWeight(w[i]);
}
return answer;
};
// Return an array with the set bit locations (values)
FastBitSet.prototype.array = function () {
const answer = new Array(this.size());
let pos = 0 | 0;
const c = this.words.length;
for (let k = 0; k < c; ++k) {
let w = this.words[k];
while (w != 0) {
const t = w & -w;
answer[pos++] = (k << 5) + this.hammingWeight((t - 1) | 0);
w ^= t;
}
}
return answer;
};
// Return an array with the set bit locations (values)
FastBitSet.prototype.forEach = function (fnc) {
const c = this.words.length;
for (let k = 0; k < c; ++k) {
let w = this.words[k];
while (w != 0) {
const t = w & -w;
fnc((k << 5) + this.hammingWeight((t - 1) | 0));
w ^= t;
}
}
};
// Returns an iterator of set bit locations (values)
FastBitSet.prototype[Symbol.iterator] = function* () {
const c = this.words.length;
for (let k = 0; k < c; ++k) {
let w = this.words[k];
while (w != 0) {
const t = w & -w;
yield (k << 5) + this.hammingWeight((t - 1) | 0);
w ^= t;
}
}
};
// Creates a copy of this bitmap
FastBitSet.prototype.clone = function () {
const clone = Object.create(FastBitSet.prototype);
clone.words = this.words.slice();
return clone;
};
// Check if this bitset intersects with another one,
// no bitmap is modified
FastBitSet.prototype.intersects = function (otherbitmap) {
const newcount = Math.min(this.words.length, otherbitmap.words.length);
for (let k = 0 | 0; k < newcount; ++k) {
if ((this.words[k] & otherbitmap.words[k]) !== 0) return true;
}
return false;
};
// Computes the intersection between this bitset and another one,
// the current bitmap is modified (and returned by the function)
FastBitSet.prototype.intersection = function (otherbitmap) {
const newcount = Math.min(this.words.length, otherbitmap.words.length);
let k = 0 | 0;
for (; k + 7 < newcount; k += 8) {
this.words[k] &= otherbitmap.words[k];
this.words[k + 1] &= otherbitmap.words[k + 1];
this.words[k + 2] &= otherbitmap.words[k + 2];
this.words[k + 3] &= otherbitmap.words[k + 3];
this.words[k + 4] &= otherbitmap.words[k + 4];
this.words[k + 5] &= otherbitmap.words[k + 5];
this.words[k + 6] &= otherbitmap.words[k + 6];
this.words[k + 7] &= otherbitmap.words[k + 7];
}
for (; k < newcount; ++k) {
this.words[k] &= otherbitmap.words[k];
}
const c = this.words.length;
for (k = newcount; k < c; ++k) {
this.words[k] = 0;
}
return this;
};
// Computes the size of the intersection between this bitset and another one
FastBitSet.prototype.intersection_size = function (otherbitmap) {
const newcount = Math.min(this.words.length, otherbitmap.words.length);
let answer = 0 | 0;
for (let k = 0 | 0; k < newcount; ++k) {
answer += this.hammingWeight(this.words[k] & otherbitmap.words[k]);
}
return answer;
};
// Computes the intersection between this bitset and another one,
// a new bitmap is generated
FastBitSet.prototype.new_intersection = function (otherbitmap) {
const answer = Object.create(FastBitSet.prototype);
const count = Math.min(this.words.length, otherbitmap.words.length);
answer.words = new Array(count);
let k = 0 | 0;
for (; k + 7 < count; k += 8) {
answer.words[k] = this.words[k] & otherbitmap.words[k];
answer.words[k + 1] = this.words[k + 1] & otherbitmap.words[k + 1];
answer.words[k + 2] = this.words[k + 2] & otherbitmap.words[k + 2];
answer.words[k + 3] = this.words[k + 3] & otherbitmap.words[k + 3];
answer.words[k + 4] = this.words[k + 4] & otherbitmap.words[k + 4];
answer.words[k + 5] = this.words[k + 5] & otherbitmap.words[k + 5];
answer.words[k + 6] = this.words[k + 6] & otherbitmap.words[k + 6];
answer.words[k + 7] = this.words[k + 7] & otherbitmap.words[k + 7];
}
for (; k < count; ++k) {
answer.words[k] = this.words[k] & otherbitmap.words[k];
}
return answer;
};
// Computes the intersection between this bitset and another one,
// the current bitmap is modified
FastBitSet.prototype.equals = function (otherbitmap) {
const mcount = Math.min(this.words.length, otherbitmap.words.length);
for (let k = 0 | 0; k < mcount; ++k) {
if (this.words[k] != otherbitmap.words[k]) return false;
}
if (this.words.length < otherbitmap.words.length) {
const c = otherbitmap.words.length;
for (let k = this.words.length; k < c; ++k) {
if (otherbitmap.words[k] != 0) return false;
}
} else if (otherbitmap.words.length < this.words.length) {
const c = this.words.length;
for (let k = otherbitmap.words.length; k < c; ++k) {
if (this.words[k] != 0) return false;
}
}
return true;
};
// Computes the difference between this bitset and another one,
// the current bitset is modified (and returned by the function)
// (for this set A and other set B,
// this computes A = A - B and returns A)
FastBitSet.prototype.difference = function (otherbitmap) {
const newcount = Math.min(this.words.length, otherbitmap.words.length);
let k = 0 | 0;
for (; k + 7 < newcount; k += 8) {
this.words[k] &= ~otherbitmap.words[k];
this.words[k + 1] &= ~otherbitmap.words[k + 1];
this.words[k + 2] &= ~otherbitmap.words[k + 2];
this.words[k + 3] &= ~otherbitmap.words[k + 3];
this.words[k + 4] &= ~otherbitmap.words[k + 4];
this.words[k + 5] &= ~otherbitmap.words[k + 5];
this.words[k + 6] &= ~otherbitmap.words[k + 6];
this.words[k + 7] &= ~otherbitmap.words[k + 7];
}
for (; k < newcount; ++k) {
this.words[k] &= ~otherbitmap.words[k];
}
return this;
};
// Computes the difference between this bitset and another one,
// the other bitset is modified (and returned by the function)
// (for this set A and other set B,
// this computes B = A - B and returns B)
FastBitSet.prototype.difference2 = function (otherbitmap) {
const mincount = Math.min(this.words.length, otherbitmap.words.length);
let k = 0 | 0;
for (; k + 7 < mincount; k += 8) {
otherbitmap.words[k] = this.words[k] & ~otherbitmap.words[k];
otherbitmap.words[k + 1] = this.words[k + 1] & ~otherbitmap.words[k + 1];
otherbitmap.words[k + 2] = this.words[k + 2] & ~otherbitmap.words[k + 2];
otherbitmap.words[k + 3] = this.words[k + 3] & ~otherbitmap.words[k + 3];
otherbitmap.words[k + 4] = this.words[k + 4] & ~otherbitmap.words[k + 4];
otherbitmap.words[k + 5] = this.words[k + 5] & ~otherbitmap.words[k + 5];
otherbitmap.words[k + 6] = this.words[k + 6] & ~otherbitmap.words[k + 6];
otherbitmap.words[k + 7] = this.words[k + 7] & ~otherbitmap.words[k + 7];
}
for (; k < mincount; ++k) {
otherbitmap.words[k] = this.words[k] & ~otherbitmap.words[k];
}
// remaining words are all part of difference
for (k = this.words.length - 1; k >= mincount; --k) {
otherbitmap.words[k] = this.words[k];
}
otherbitmap.words = otherbitmap.words.slice(0, this.words.length);
return otherbitmap;
};
// Computes the difference between this bitset and another one,
// a new bitmap is generated
FastBitSet.prototype.new_difference = function (otherbitmap) {
return this.clone().difference(otherbitmap); // should be fast enough
};
// Computes the size of the difference between this bitset and another one
FastBitSet.prototype.difference_size = function (otherbitmap) {
const newcount = Math.min(this.words.length, otherbitmap.words.length);
let answer = 0 | 0;
let k = 0 | 0;
for (; k < newcount; ++k) {
answer += this.hammingWeight(this.words[k] & ~otherbitmap.words[k]);
}
const c = this.words.length;
for (; k < c; ++k) {
answer += this.hammingWeight(this.words[k]);
}
return answer;
};
// Computes the changed elements (XOR) between this bitset and another one,
// the current bitset is modified (and returned by the function)
FastBitSet.prototype.change = function (otherbitmap) {
const mincount = Math.min(this.words.length, otherbitmap.words.length);
let k = 0 | 0;
for (; k + 7 < mincount; k += 8) {
this.words[k] ^= otherbitmap.words[k];
this.words[k + 1] ^= otherbitmap.words[k + 1];
this.words[k + 2] ^= otherbitmap.words[k + 2];
this.words[k + 3] ^= otherbitmap.words[k + 3];
this.words[k + 4] ^= otherbitmap.words[k + 4];
this.words[k + 5] ^= otherbitmap.words[k + 5];
this.words[k + 6] ^= otherbitmap.words[k + 6];
this.words[k + 7] ^= otherbitmap.words[k + 7];
}
for (; k < mincount; ++k) {
this.words[k] ^= otherbitmap.words[k];
}
// remaining words are all part of change
for (k = otherbitmap.words.length - 1; k >= mincount; --k) {
this.words[k] = otherbitmap.words[k];
}
return this;
};
// Computes the change between this bitset and another one,
// a new bitmap is generated
FastBitSet.prototype.new_change = function (otherbitmap) {
const answer = Object.create(FastBitSet.prototype);
const count = Math.max(this.words.length, otherbitmap.words.length);
answer.words = new Array(count);
const mcount = Math.min(this.words.length, otherbitmap.words.length);
let k = 0;
for (; k + 7 < mcount; k += 8) {
answer.words[k] = this.words[k] ^ otherbitmap.words[k];
answer.words[k + 1] = this.words[k + 1] ^ otherbitmap.words[k + 1];
answer.words[k + 2] = this.words[k + 2] ^ otherbitmap.words[k + 2];
answer.words[k + 3] = this.words[k + 3] ^ otherbitmap.words[k + 3];
answer.words[k + 4] = this.words[k + 4] ^ otherbitmap.words[k + 4];
answer.words[k + 5] = this.words[k + 5] ^ otherbitmap.words[k + 5];
answer.words[k + 6] = this.words[k + 6] ^ otherbitmap.words[k + 6];
answer.words[k + 7] = this.words[k + 7] ^ otherbitmap.words[k + 7];
}
for (; k < mcount; ++k) {
answer.words[k] = this.words[k] ^ otherbitmap.words[k];
}
const c = this.words.length;
for (k = mcount; k < c; ++k) {
answer.words[k] = this.words[k];
}
const c2 = otherbitmap.words.length;
for (k = mcount; k < c2; ++k) {
answer.words[k] = otherbitmap.words[k];
}
return answer;
};
// Computes the number of changed elements between this bitset and another one
FastBitSet.prototype.change_size = function (otherbitmap) {
const mincount = Math.min(this.words.length, otherbitmap.words.length);
let answer = 0 | 0;
let k = 0 | 0;
for (; k < mincount; ++k) {
answer += this.hammingWeight(this.words[k] ^ otherbitmap.words[k]);
}
const longer =
this.words.length > otherbitmap.words.length ? this : otherbitmap;
const c = longer.words.length;
for (; k < c; ++k) {
answer += this.hammingWeight(longer.words[k]);
}
return answer;
};
// Returns a string representation
FastBitSet.prototype.toString = function () {
return "{" + this.array().join(",") + "}";
};
// Computes the union between this bitset and another one,
// the current bitset is modified (and returned by the function)
FastBitSet.prototype.union = function (otherbitmap) {
const mcount = Math.min(this.words.length, otherbitmap.words.length);
let k = 0 | 0;
for (; k + 7 < mcount; k += 8) {
this.words[k] |= otherbitmap.words[k];
this.words[k + 1] |= otherbitmap.words[k + 1];
this.words[k + 2] |= otherbitmap.words[k + 2];
this.words[k + 3] |= otherbitmap.words[k + 3];
this.words[k + 4] |= otherbitmap.words[k + 4];
this.words[k + 5] |= otherbitmap.words[k + 5];
this.words[k + 6] |= otherbitmap.words[k + 6];
this.words[k + 7] |= otherbitmap.words[k + 7];
}
for (; k < mcount; ++k) {
this.words[k] |= otherbitmap.words[k];
}
if (this.words.length < otherbitmap.words.length) {
this.resize((otherbitmap.words.length << 5) - 1);
const c = otherbitmap.words.length;
for (let k = mcount; k < c; ++k) {
this.words[k] = otherbitmap.words[k];
}
}
return this;
};
FastBitSet.prototype.new_union = function (otherbitmap) {
const answer = Object.create(FastBitSet.prototype);
const count = Math.max(this.words.length, otherbitmap.words.length);
answer.words = new Array(count);
const mcount = Math.min(this.words.length, otherbitmap.words.length);
let k = 0;
for (; k + 7 < mcount; k += 8) {
answer.words[k] = this.words[k] | otherbitmap.words[k];
answer.words[k + 1] = this.words[k + 1] | otherbitmap.words[k + 1];
answer.words[k + 2] = this.words[k + 2] | otherbitmap.words[k + 2];
answer.words[k + 3] = this.words[k + 3] | otherbitmap.words[k + 3];
answer.words[k + 4] = this.words[k + 4] | otherbitmap.words[k + 4];
answer.words[k + 5] = this.words[k + 5] | otherbitmap.words[k + 5];
answer.words[k + 6] = this.words[k + 6] | otherbitmap.words[k + 6];
answer.words[k + 7] = this.words[k + 7] | otherbitmap.words[k + 7];
}
for (; k < mcount; ++k) {
answer.words[k] = this.words[k] | otherbitmap.words[k];
}
const c = this.words.length;
for (k = mcount; k < c; ++k) {
answer.words[k] = this.words[k];
}
const c2 = otherbitmap.words.length;
for (k = mcount; k < c2; ++k) {
answer.words[k] = otherbitmap.words[k];
}
return answer;
};
// Computes the size union between this bitset and another one
FastBitSet.prototype.union_size = function (otherbitmap) {
const mcount = Math.min(this.words.length, otherbitmap.words.length);
let answer = 0 | 0;
for (let k = 0 | 0; k < mcount; ++k) {
answer += this.hammingWeight(this.words[k] | otherbitmap.words[k]);
}
if (this.words.length < otherbitmap.words.length) {
const c = otherbitmap.words.length;
for (let k = this.words.length; k < c; ++k) {
answer += this.hammingWeight(otherbitmap.words[k] | 0);
}
} else {
const c = this.words.length;
for (let k = otherbitmap.words.length; k < c; ++k) {
answer += this.hammingWeight(this.words[k] | 0);
}
}
return answer;
};
///////////////
module.exports = FastBitSet;
},{}],3:[function(require,module,exports){
/**
* lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 1.0.0
* Copyright (C) 2017 Oliver Nightingale
* @license MIT
*/
;(function(){
/**
* Convenience function for instantiating a new lunr index and configuring it
* with the default pipeline functions and the passed config function.
*
* When using this convenience function a new index will be created with the
* following functions already in the pipeline:
*
* lunr.StopWordFilter - filters out any stop words before they enter the
* index
*
* lunr.stemmer - stems the tokens before entering the index.
*
* Example:
*
* var idx = lunr(function () {
* this.field('title', 10)
* this.field('tags', 100)
* this.field('body')
*
* this.ref('cid')
*
* this.pipeline.add(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
idx.pipeline.add(
lunr.trimmer,
lunr.stopWordFilter,
lunr.stemmer
)
if (config) config.call(idx, idx)
return idx
}
lunr.version = "1.0.0"
/*!
* lunr.utils
* Copyright (C) 2017 Oliver Nightingale
*/
/**
* A namespace containing utils for the rest of the lunr library
*/
lunr.utils = {}
/**
* Print a warning message to the console.
*
* @param {String} message The message to be printed.
* @memberOf Utils
*/
lunr.utils.warn = (function (global) {
return function (message) {
if (global.console && console.warn) {
console.warn(message)
}
}
})(this)
/**
* Convert an object to a string.
*
* In the case of `null` and `undefined` the function returns
* the empty string, in all other cases the result of calling
* `toString` on the passed object is returned.
*
* @param {Any} obj The object to convert to a string.
* @return {String} string representation of the passed object.
* @memberOf Utils
*/
lunr.utils.asString = function (obj) {
if (obj === void 0 || obj === null) {
return ""
} else {
return obj.toString()
}
}
/*!
* lunr.EventEmitter
* Copyright (C) 2017 Oliver Nightingale
*/
/**
* lunr.EventEmitter is an event emitter for lunr. It manages adding and removing event handlers and triggering events and their handlers.
*
* @constructor
*/
lunr.EventEmitter = function () {
this.events = {}
}
/**
* Binds a handler function to a specific event(s).
*
* 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 {Function} fn The function to call when an event is fired.
* @memberOf EventEmitter
*/
lunr.EventEmitter.prototype.addListener = function () {
var args = Array.prototype.slice.call(arguments),
fn = args.pop(),
names = args
if (typeof fn !== "function") throw new TypeError ("last argument must be a function")
names.forEach(function (name) {
if (!this.hasHandler(name)) this.events[name] = []
this.events[name].push(fn)
}, this)
}
/**
* Removes a handler function from a specific event.
*
* @param {String} eventName The name of the event to remove this function from.
* @param {Function} fn The function to remove from an event.
* @memberOf EventEmitter
*/
lunr.EventEmitter.prototype.removeListener = function (name, fn) {
if (!this.hasHandler(name)) return
var fnIndex = this.events[name].indexOf(fn)
this.events[name].splice(fnIndex, 1)
if (!this.events[name].length) delete this.events[name]
}
/**
* Calls all functions bound to the given event.
*
* Additional data can be passed to the event handler as arguments to `emit`
* after the event name.
*
* @param {String} eventName The name of the event to emit.
* @memberOf EventEmitter
*/
lunr.EventEmitter.prototype.emit = function (name) {
if (!this.hasHandler(name)) return
var args = Array.prototype.slice.call(arguments, 1)
this.events[name].forEach(function (fn) {
fn.apply(undefined, args)
})
}
/**
* Checks whether a handler has ever been stored against an event.
*
* @param {String} eventName The name of the event to check.
* @private
* @memberOf EventEmitter
*/
lunr.EventEmitter.prototype.hasHandler = function (name) {
return name in this.events
}
/*!
* lunr.tokenizer
* Copyright (C) 2017 Oliver Nightingale
*/
/**
* A function for splitting a string into tokens ready to be inserted into
* the search index. Uses `lunr.tokenizer.separator` to split strings, change
* the value of this property to change how strings are split into tokens.
*
* @module
* @param {String} obj The string to convert into tokens
* @see lunr.tokenizer.separator
* @returns {Array}
*/
lunr.tokenizer = function (obj) {
if (!arguments.length || obj == null || obj == undefined) return []
if (Array.isArray(obj)) return obj.map(function (t) { return lunr.utils.asString(t).toLowerCase() })
return obj.toString().trim().toLowerCase().split(lunr.tokenizer.separator)
}
/**
* The sperator used to split a string into tokens. Override this property to change the behaviour of
* `lunr.tokenizer` behaviour when tokenizing strings. By default this splits on whitespace and hyphens.
*
* @static
* @see lunr.tokenizer
*/
lunr.tokenizer.separator = /[\s\-]+/
/**
* Loads a previously serialised tokenizer.
*
* A tokenizer function to be loaded must already be registered with lunr.tokenizer.
* If the serialised tokenizer has not been registered then an error will be thrown.
*
* @param {String} label The label of the serialised tokenizer.
* @returns {Function}
* @memberOf tokenizer
*/
lunr.tokenizer.load = function (label) {
var fn = this.registeredFunctions[label]
if (!fn) {
throw new Error('Cannot load un-registered function: ' + label)
}
return fn
}
lunr.tokenizer.label = 'default'
lunr.tokenizer.registeredFunctions = {
'default': lunr.tokenizer
}
/**
* Register a tokenizer function.
*
* Functions that are used as tokenizers should be registered if they are to be used with a serialised index.
*
* Registering a function does not add it to an index, functions must still be associated with a specific index for them to be used when indexing and searching documents.
*
* @param {Function} fn The function to register.
* @param {String} label The label to register this function with
* @memberOf tokenizer
*/
lunr.tokenizer.registerFunction = function (fn, label) {
if (label in this.registeredFunctions) {
lunr.utils.warn('Overwriting existing tokenizer: ' + label)
}
fn.label = label
this.registeredFunctions[label] = fn
}
/*!
* lunr.Pipeline
* Copyright (C) 2017 Oliver Nightingale
*/
/**
* 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
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
* @memberOf Pipeline
*/
lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {
var isRegistered = fn.label && (fn.label in this.registeredFunctions)
if (!isRegistered) {
lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn)
}
}
/**
* Loads a previously serialised 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
* error will be thrown.
*
* @param {Object} serialised The serialised pipeline to load.
* @returns {lunr.Pipeline}
* @memberOf Pipeline
*/
lunr.Pipeline.load = function (serialised) {
var pipeline = new lunr.Pipeline
serialised.forEach(function (fnName) {
var fn = lunr.Pipeline.registeredFunctions[fnName]
if (fn) {
pipeline.add(fn)
} else {
throw new Error('Cannot load un-registered function: ' + fnName)
}
})
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)
this._stack.push(fn)
}, this)
}
/**
* 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)
if (pos == -1) {
throw new Error('Cannot find existingFn')
}
pos = pos + 1
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)
if (pos == -1) {
throw new Error('Cannot find existingFn')
}
this._stack.splice(pos, 0, newFn)
}
/**
* Removes a function from the pipeline.
*
* @param {Function} fn The function to remove from the pipeline.
* @memberOf Pipeline
*/
lunr.Pipeline.prototype.remove = function (fn) {
var pos = this._stack.indexOf(fn)
if (pos == -1) {
return
}
this._stack.splice(pos, 1)
}
/**
* 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 i = 0; i < tokenLength; i++) {
var token = tokens[i]
for (var j = 0; j < stackLength; j++) {
token = this._stack[j](token, i, tokens)
if (token === void 0 || token === '') break
};
if (token !== void 0 && token !== '') 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.
*
* Logs a warning if the function has not been registered.
*
* @returns {Array}
* @memberOf Pipeline
*/
lunr.Pipeline.prototype.toJSON = function () {
return this._stack.map(function (fn) {
lunr.Pipeline.warnIfFunctionNotRegistered(fn)
return fn.label
})
}
/*!
* lunr.Vector
* Copyright (C) 2017 Oliver Nightingale
*/
/**
* lunr.Vectors implement vector related operations for
* a series of elements.
*
* @constructor
*/
lunr.Vector = function () {
this._magnitude = null
this.list = undefined
this.length = 0
}
/**
* lunr.Vector.Node is a simple struct for each node
* 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
}
/**
* 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) {
this._magnitude = undefined;
var list = this.list
if (!list) {
this.list = new lunr.Vector.Node (idx, val, list)
return this.length++
}
if (idx < list.idx) {
this.list = new lunr.Vector.Node (idx, val, list)
return this.length++
}
var prev = list,
next = list.next
while (next != undefined) {
if (idx < next.idx) {
prev.next = new lunr.Vector.Node (idx, val, next)
return this.length++
}
prev = next, next = next.next
}
prev.next = new lunr.Vector.Node (idx, val, next)
return this.length++
}
/**
* Calculates the magnitude of this vector.
*
* @returns {Number}
* @memberOf Vector
*/
lunr.Vector.prototype.magnitude = function () {
if (this._magnitude) 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) {
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
}
/**
* 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) 2017 Oliver Nightingale
*/
/**
* lunr.SortedSets are used to maintain an array of uniq values in a sorted
* order.
*
* @constructor
*/
lunr.SortedSet = function () {
this.length = 0
this.elements = []
}
/**
* Loads a previously serialised sorted set.
*
* @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
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 () {
var i, element
for (i = 0; i < arguments.length; i++) {
element = arguments[i]
if (~this.indexOf(element)) continue
this.elements.splice(this.locationFor(element), 0, element)
}
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()
}
/**
* Creates a new array with the results of calling a provided function on every
* element in this sorted set.
*
* Delegates to Array.prototype.map and has the same signature.
*
* @param {Function} fn The function that is called on each element of the
* 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)
}
/**
* Executes a provided function once per sorted set element.
*
* Delegates to Array.prototype.forEach and has the same signature.
*
* @param {Function} fn The function that is called on each element of the
* set.
* @param {Object} ctx An optional object that can be used as the context
* @memberOf SortedSet
* for the function fn.
*/
lunr.SortedSet.prototype.forEach = function (fn, ctx) {
return this.elements.forEach(fn, ctx)
}
/**
* Returns the index at which a given element can be found in the
* sorted set, or -1 if it is not present.
*
* @param {Object} elem The object to locate in the sorted set.
* @returns {Number}
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.indexOf = function (elem) {
var start = 0,
end = this.elements.length,
sectionLength = end - start,
pivot = start + Math.floor(sectionLength / 2),
pivotElem = this.elements[pivot]
while (sectionLength > 1) {
if (pivotElem === elem) return pivot
if (pivotElem < elem) start = pivot
if (pivotElem > elem) end = pivot
sectionLength = end - start
pivot = start + Math.floor(sectionLength / 2)
pivotElem = this.elements[pivot]
}
if (pivotElem === elem) return pivot
return -1
}
/**
* Returns the position within the sorted set that an element should be
* inserted at to maintain the current order of the set.
*
* 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
* @returns {Number}
* @memberOf SortedSet
*/
lunr.SortedSet.prototype.locationFor = function (elem) {
var start = 0,
end = this.elements.length,
sectionLength = end - start,
pivot = start + Math.floor(sectionLength / 2),
pivotElem = this.elements[pivot]
while (sectionLength > 1) {
if (pivotElem < elem) start = pivot
if (pivotElem > elem) end = pivot
sectionLength = end - start
pivot = start + Math.floor(sectionLength / 2)
pivotElem = this.elements[pivot]
}
if (pivotElem > elem) return pivot
if (pivotElem < elem) return pivot + 1
}
/**
* Creates a new lunr.SortedSet that contains the elements in the intersection
* 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]) {
i++
continue
}
if (a[i] > b[j]) {
j++
continue
}
};
return intersectSet
}
/**
* 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()
clone.length = clone.elements.length
return clone
}
/**
* Creates a new lunr.SortedSet that contains the elements in the union
* 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
}
unionSet = longSet.clone()
for(var i = 0, shortSetElements = shortSet.toArray(); i < shortSetElements.length; i++){
unionSet.add(shortSetElements[i])
}
return unionSet
}
/**
* 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) 2017 Oliver Nightingale
*/
/**
* 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.tokenizerFn = lunr.tokenizer
this._idfCache = {}
this.on('add', 'remove', 'update', (function () {
this._idfCache = {}
}).bind(this))
}
/**
* 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} fn 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)
}
/**
* 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} fn The serialised set to load.
* @memberOf Index
*/
lunr.Index.prototype.off = function (name, fn) {
return this.eventEmitter.removeListener(name, fn)
}
/**
* 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
idx._fields = serialisedData.fields
idx._ref = serialisedData.ref
idx.tokenizer(lunr.tokenizer.load(serialisedData.tokeniz