|
|
@ -1,16 +1,3 @@
|
|
|
|
// TODO: This file was created by bulk-decaffeinate.
|
|
|
|
|
|
|
|
// Sanity-check the conversion and remove this comment.
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
* decaffeinate suggestions:
|
|
|
|
|
|
|
|
* DS101: Remove unnecessary use of Array.from
|
|
|
|
|
|
|
|
* DS102: Remove unnecessary code created because of implicit returns
|
|
|
|
|
|
|
|
* DS104: Avoid inline assignments
|
|
|
|
|
|
|
|
* DS202: Simplify dynamic range loops
|
|
|
|
|
|
|
|
* DS206: Consider reworking classes to avoid initClass
|
|
|
|
|
|
|
|
* DS207: Consider shorter variations of null checks
|
|
|
|
|
|
|
|
* DS209: Avoid top-level return
|
|
|
|
|
|
|
|
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// Match functions
|
|
|
|
// Match functions
|
|
|
|
//
|
|
|
|
//
|
|
|
@ -158,249 +145,226 @@ function scoreFuzzyMatch() {
|
|
|
|
// Searchers
|
|
|
|
// Searchers
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
(function () {
|
|
|
|
app.Searcher = class Searcher extends Events {
|
|
|
|
let CHUNK_SIZE = undefined;
|
|
|
|
static CHUNK_SIZE = 20000;
|
|
|
|
let DEFAULTS = undefined;
|
|
|
|
|
|
|
|
let SEPARATORS_REGEXP = undefined;
|
|
|
|
|
|
|
|
let EOS_SEPARATORS_REGEXP = undefined;
|
|
|
|
|
|
|
|
let INFO_PARANTHESES_REGEXP = undefined;
|
|
|
|
|
|
|
|
let EMPTY_PARANTHESES_REGEXP = undefined;
|
|
|
|
|
|
|
|
let EVENT_REGEXP = undefined;
|
|
|
|
|
|
|
|
let DOT_REGEXP = undefined;
|
|
|
|
|
|
|
|
let WHITESPACE_REGEXP = undefined;
|
|
|
|
|
|
|
|
let EMPTY_STRING = undefined;
|
|
|
|
|
|
|
|
let ELLIPSIS = undefined;
|
|
|
|
|
|
|
|
let STRING = undefined;
|
|
|
|
|
|
|
|
app.Searcher = class Searcher extends Events {
|
|
|
|
|
|
|
|
static initClass() {
|
|
|
|
|
|
|
|
CHUNK_SIZE = 20000;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEFAULTS = {
|
|
|
|
|
|
|
|
max_results: app.config.max_results,
|
|
|
|
|
|
|
|
fuzzy_min_length: 3,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SEPARATORS_REGEXP =
|
|
|
|
|
|
|
|
/#|::|:-|->|\$(?=\w)|\-(?=\w)|\:(?=\w)|\ [\/\-&]\ |:\ |\ /g;
|
|
|
|
|
|
|
|
EOS_SEPARATORS_REGEXP = /(\w)[\-:]$/;
|
|
|
|
|
|
|
|
INFO_PARANTHESES_REGEXP = /\ \(\w+?\)$/;
|
|
|
|
|
|
|
|
EMPTY_PARANTHESES_REGEXP = /\(\)/;
|
|
|
|
|
|
|
|
EVENT_REGEXP = /\ event$/;
|
|
|
|
|
|
|
|
DOT_REGEXP = /\.+/g;
|
|
|
|
|
|
|
|
WHITESPACE_REGEXP = /\s/g;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EMPTY_STRING = "";
|
|
|
|
|
|
|
|
ELLIPSIS = "...";
|
|
|
|
|
|
|
|
STRING = "string";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static normalizeString(string) {
|
|
|
|
static DEFAULTS = {
|
|
|
|
return string
|
|
|
|
max_results: app.config.max_results,
|
|
|
|
.toLowerCase()
|
|
|
|
fuzzy_min_length: 3,
|
|
|
|
.replace(ELLIPSIS, EMPTY_STRING)
|
|
|
|
};
|
|
|
|
.replace(EVENT_REGEXP, EMPTY_STRING)
|
|
|
|
|
|
|
|
.replace(INFO_PARANTHESES_REGEXP, EMPTY_STRING)
|
|
|
|
|
|
|
|
.replace(SEPARATORS_REGEXP, SEPARATOR)
|
|
|
|
|
|
|
|
.replace(DOT_REGEXP, SEPARATOR)
|
|
|
|
|
|
|
|
.replace(EMPTY_PARANTHESES_REGEXP, EMPTY_STRING)
|
|
|
|
|
|
|
|
.replace(WHITESPACE_REGEXP, EMPTY_STRING);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static normalizeQuery(string) {
|
|
|
|
static SEPARATORS_REGEXP =
|
|
|
|
string = this.normalizeString(string);
|
|
|
|
/#|::|:-|->|\$(?=\w)|\-(?=\w)|\:(?=\w)|\ [\/\-&]\ |:\ |\ /g;
|
|
|
|
return string.replace(EOS_SEPARATORS_REGEXP, "$1.");
|
|
|
|
static EOS_SEPARATORS_REGEXP = /(\w)[\-:]$/;
|
|
|
|
}
|
|
|
|
static INFO_PARANTHESES_REGEXP = /\ \(\w+?\)$/;
|
|
|
|
|
|
|
|
static EMPTY_PARANTHESES_REGEXP = /\(\)/;
|
|
|
|
|
|
|
|
static EVENT_REGEXP = /\ event$/;
|
|
|
|
|
|
|
|
static DOT_REGEXP = /\.+/g;
|
|
|
|
|
|
|
|
static WHITESPACE_REGEXP = /\s/g;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static EMPTY_STRING = "";
|
|
|
|
|
|
|
|
static ELLIPSIS = "...";
|
|
|
|
|
|
|
|
static STRING = "string";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static normalizeString(string) {
|
|
|
|
|
|
|
|
return string
|
|
|
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
|
|
|
.replace(Searcher.ELLIPSIS, Searcher.EMPTY_STRING)
|
|
|
|
|
|
|
|
.replace(Searcher.EVENT_REGEXP, Searcher.EMPTY_STRING)
|
|
|
|
|
|
|
|
.replace(Searcher.INFO_PARANTHESES_REGEXP, Searcher.EMPTY_STRING)
|
|
|
|
|
|
|
|
.replace(Searcher.SEPARATORS_REGEXP, SEPARATOR)
|
|
|
|
|
|
|
|
.replace(Searcher.DOT_REGEXP, SEPARATOR)
|
|
|
|
|
|
|
|
.replace(Searcher.EMPTY_PARANTHESES_REGEXP, Searcher.EMPTY_STRING)
|
|
|
|
|
|
|
|
.replace(Searcher.WHITESPACE_REGEXP, Searcher.EMPTY_STRING);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
constructor(options) {
|
|
|
|
static normalizeQuery(string) {
|
|
|
|
super();
|
|
|
|
string = this.normalizeString(string);
|
|
|
|
this.options = $.extend({}, DEFAULTS, options || {});
|
|
|
|
return string.replace(Searcher.EOS_SEPARATORS_REGEXP, "$1.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
find(data, attr, q) {
|
|
|
|
constructor(options) {
|
|
|
|
this.kill();
|
|
|
|
super();
|
|
|
|
|
|
|
|
this.options = $.extend({}, Searcher.DEFAULTS, options || {});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.data = data;
|
|
|
|
find(data, attr, q) {
|
|
|
|
this.attr = attr;
|
|
|
|
this.kill();
|
|
|
|
this.query = q;
|
|
|
|
|
|
|
|
this.setup();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.isValid()) {
|
|
|
|
this.data = data;
|
|
|
|
this.match();
|
|
|
|
this.attr = attr;
|
|
|
|
} else {
|
|
|
|
this.query = q;
|
|
|
|
this.end();
|
|
|
|
this.setup();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setup() {
|
|
|
|
if (this.isValid()) {
|
|
|
|
query = this.query = this.constructor.normalizeQuery(this.query);
|
|
|
|
this.match();
|
|
|
|
queryLength = query.length;
|
|
|
|
} else {
|
|
|
|
this.dataLength = this.data.length;
|
|
|
|
this.end();
|
|
|
|
this.matchers = [exactMatch];
|
|
|
|
|
|
|
|
this.totalResults = 0;
|
|
|
|
|
|
|
|
this.setupFuzzy();
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setupFuzzy() {
|
|
|
|
setup() {
|
|
|
|
if (queryLength >= this.options.fuzzy_min_length) {
|
|
|
|
query = this.query = this.constructor.normalizeQuery(this.query);
|
|
|
|
fuzzyRegexp = this.queryToFuzzyRegexp(query);
|
|
|
|
queryLength = query.length;
|
|
|
|
this.matchers.push(fuzzyMatch);
|
|
|
|
this.dataLength = this.data.length;
|
|
|
|
} else {
|
|
|
|
this.matchers = [exactMatch];
|
|
|
|
fuzzyRegexp = null;
|
|
|
|
this.totalResults = 0;
|
|
|
|
}
|
|
|
|
this.setupFuzzy();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
isValid() {
|
|
|
|
setupFuzzy() {
|
|
|
|
return queryLength > 0 && query !== SEPARATOR;
|
|
|
|
if (queryLength >= this.options.fuzzy_min_length) {
|
|
|
|
|
|
|
|
fuzzyRegexp = this.queryToFuzzyRegexp(query);
|
|
|
|
|
|
|
|
this.matchers.push(fuzzyMatch);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
fuzzyRegexp = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
end() {
|
|
|
|
isValid() {
|
|
|
|
if (!this.totalResults) {
|
|
|
|
return queryLength > 0 && query !== SEPARATOR;
|
|
|
|
this.triggerResults([]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.trigger("end");
|
|
|
|
|
|
|
|
this.free();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
kill() {
|
|
|
|
end() {
|
|
|
|
if (this.timeout) {
|
|
|
|
if (!this.totalResults) {
|
|
|
|
clearTimeout(this.timeout);
|
|
|
|
this.triggerResults([]);
|
|
|
|
this.free();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.trigger("end");
|
|
|
|
|
|
|
|
this.free();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
free() {
|
|
|
|
kill() {
|
|
|
|
this.data =
|
|
|
|
if (this.timeout) {
|
|
|
|
this.attr =
|
|
|
|
clearTimeout(this.timeout);
|
|
|
|
this.dataLength =
|
|
|
|
this.free();
|
|
|
|
this.matchers =
|
|
|
|
|
|
|
|
this.matcher =
|
|
|
|
|
|
|
|
this.query =
|
|
|
|
|
|
|
|
this.totalResults =
|
|
|
|
|
|
|
|
this.scoreMap =
|
|
|
|
|
|
|
|
this.cursor =
|
|
|
|
|
|
|
|
this.timeout =
|
|
|
|
|
|
|
|
null;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
match() {
|
|
|
|
free() {
|
|
|
|
if (!this.foundEnough() && (this.matcher = this.matchers.shift())) {
|
|
|
|
this.data = null;
|
|
|
|
this.setupMatcher();
|
|
|
|
this.attr = null;
|
|
|
|
this.matchChunks();
|
|
|
|
this.dataLength = null;
|
|
|
|
} else {
|
|
|
|
this.matchers = null;
|
|
|
|
this.end();
|
|
|
|
this.matcher = null;
|
|
|
|
}
|
|
|
|
this.query = null;
|
|
|
|
}
|
|
|
|
this.totalResults = null;
|
|
|
|
|
|
|
|
this.scoreMap = null;
|
|
|
|
|
|
|
|
this.cursor = null;
|
|
|
|
|
|
|
|
this.timeout = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
setupMatcher() {
|
|
|
|
match() {
|
|
|
|
this.cursor = 0;
|
|
|
|
if (!this.foundEnough() && (this.matcher = this.matchers.shift())) {
|
|
|
|
this.scoreMap = new Array(101);
|
|
|
|
this.setupMatcher();
|
|
|
|
|
|
|
|
this.matchChunks();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
this.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
matchChunks() {
|
|
|
|
setupMatcher() {
|
|
|
|
this.matchChunk();
|
|
|
|
this.cursor = 0;
|
|
|
|
|
|
|
|
this.scoreMap = new Array(101);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (this.cursor === this.dataLength || this.scoredEnough()) {
|
|
|
|
matchChunks() {
|
|
|
|
this.delay(() => this.match());
|
|
|
|
this.matchChunk();
|
|
|
|
this.sendResults();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (this.cursor === this.dataLength || this.scoredEnough()) {
|
|
|
|
this.delay(() => this.matchChunks());
|
|
|
|
this.delay(() => this.match());
|
|
|
|
}
|
|
|
|
this.sendResults();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
this.delay(() => this.matchChunks());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
matchChunk() {
|
|
|
|
matchChunk() {
|
|
|
|
({ matcher } = this);
|
|
|
|
({ matcher } = this);
|
|
|
|
for (
|
|
|
|
for (let j = 0, end = this.chunkSize(); j < end; j++) {
|
|
|
|
let j = 0, end = this.chunkSize(), asc = 0 <= end;
|
|
|
|
value = this.data[this.cursor][this.attr];
|
|
|
|
asc ? j < end : j > end;
|
|
|
|
if (value.split) {
|
|
|
|
asc ? j++ : j--
|
|
|
|
// string
|
|
|
|
) {
|
|
|
|
valueLength = value.length;
|
|
|
|
value = this.data[this.cursor][this.attr];
|
|
|
|
if ((score = matcher())) {
|
|
|
|
if (value.split) {
|
|
|
|
this.addResult(this.data[this.cursor], score);
|
|
|
|
// string
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// array
|
|
|
|
|
|
|
|
score = 0;
|
|
|
|
|
|
|
|
for (value of Array.from(this.data[this.cursor][this.attr])) {
|
|
|
|
valueLength = value.length;
|
|
|
|
valueLength = value.length;
|
|
|
|
if ((score = matcher())) {
|
|
|
|
score = Math.max(score, matcher() || 0);
|
|
|
|
this.addResult(this.data[this.cursor], score);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (score > 0) {
|
|
|
|
} else {
|
|
|
|
this.addResult(this.data[this.cursor], score);
|
|
|
|
// array
|
|
|
|
|
|
|
|
score = 0;
|
|
|
|
|
|
|
|
for (value of Array.from(this.data[this.cursor][this.attr])) {
|
|
|
|
|
|
|
|
valueLength = value.length;
|
|
|
|
|
|
|
|
score = Math.max(score, matcher() || 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (score > 0) {
|
|
|
|
|
|
|
|
this.addResult(this.data[this.cursor], score);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.cursor++;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.cursor++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
chunkSize() {
|
|
|
|
chunkSize() {
|
|
|
|
if (this.cursor + CHUNK_SIZE > this.dataLength) {
|
|
|
|
if (this.cursor + Searcher.CHUNK_SIZE > this.dataLength) {
|
|
|
|
return this.dataLength % CHUNK_SIZE;
|
|
|
|
return this.dataLength % Searcher.CHUNK_SIZE;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
return CHUNK_SIZE;
|
|
|
|
return Searcher.CHUNK_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
scoredEnough() {
|
|
|
|
scoredEnough() {
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
(this.scoreMap[100] != null ? this.scoreMap[100].length : undefined) >=
|
|
|
|
(this.scoreMap[100] != null ? this.scoreMap[100].length : undefined) >=
|
|
|
|
this.options.max_results
|
|
|
|
this.options.max_results
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foundEnough() {
|
|
|
|
foundEnough() {
|
|
|
|
return this.totalResults >= this.options.max_results;
|
|
|
|
return this.totalResults >= this.options.max_results;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addResult(object, score) {
|
|
|
|
addResult(object, score) {
|
|
|
|
let name;
|
|
|
|
let name;
|
|
|
|
(
|
|
|
|
(
|
|
|
|
this.scoreMap[(name = Math.round(score))] || (this.scoreMap[name] = [])
|
|
|
|
this.scoreMap[(name = Math.round(score))] || (this.scoreMap[name] = [])
|
|
|
|
).push(object);
|
|
|
|
).push(object);
|
|
|
|
this.totalResults++;
|
|
|
|
this.totalResults++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getResults() {
|
|
|
|
getResults() {
|
|
|
|
const results = [];
|
|
|
|
const results = [];
|
|
|
|
for (let j = this.scoreMap.length - 1; j >= 0; j--) {
|
|
|
|
for (let j = this.scoreMap.length - 1; j >= 0; j--) {
|
|
|
|
var objects = this.scoreMap[j];
|
|
|
|
var objects = this.scoreMap[j];
|
|
|
|
if (objects) {
|
|
|
|
if (objects) {
|
|
|
|
results.push.apply(results, objects);
|
|
|
|
results.push.apply(results, objects);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return results.slice(0, this.options.max_results);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results.slice(0, this.options.max_results);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sendResults() {
|
|
|
|
sendResults() {
|
|
|
|
const results = this.getResults();
|
|
|
|
const results = this.getResults();
|
|
|
|
if (results.length) {
|
|
|
|
if (results.length) {
|
|
|
|
this.triggerResults(results);
|
|
|
|
this.triggerResults(results);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
triggerResults(results) {
|
|
|
|
triggerResults(results) {
|
|
|
|
this.trigger("results", results);
|
|
|
|
this.trigger("results", results);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
delay(fn) {
|
|
|
|
delay(fn) {
|
|
|
|
return (this.timeout = setTimeout(fn, 1));
|
|
|
|
return (this.timeout = setTimeout(fn, 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
queryToFuzzyRegexp(string) {
|
|
|
|
queryToFuzzyRegexp(string) {
|
|
|
|
const chars = string.split("");
|
|
|
|
const chars = string.split("");
|
|
|
|
for (i = 0; i < chars.length; i++) {
|
|
|
|
for (i = 0; i < chars.length; i++) {
|
|
|
|
var char = chars[i];
|
|
|
|
var char = chars[i];
|
|
|
|
chars[i] = $.escapeRegexp(char);
|
|
|
|
chars[i] = $.escapeRegexp(char);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new RegExp(chars.join(".*?")); // abc -> /a.*?b.*?c.*?/
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return new RegExp(chars.join(".*?")); // abc -> /a.*?b.*?c.*?/
|
|
|
|
app.Searcher.initClass();
|
|
|
|
}
|
|
|
|
return app.Searcher;
|
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.SynchronousSearcher = class SynchronousSearcher extends app.Searcher {
|
|
|
|
app.SynchronousSearcher = class SynchronousSearcher extends app.Searcher {
|
|
|
|
match() {
|
|
|
|
match() {
|
|
|
|