elliptical
Version:
Interactive natural-language interfaces
266 lines (220 loc) • 7.3 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.match = match;
exports.nullMatch = nullMatch;
exports.beginningMatch = beginningMatch;
exports.anywhereMatch = anywhereMatch;
exports.fuzzyMatch = fuzzyMatch;
var _lodash = require('lodash');
var _lodash2 = _interopRequireDefault(_lodash);
var _escapeStringRegexp = require('escape-string-regexp');
var _escapeStringRegexp2 = _interopRequireDefault(_escapeStringRegexp);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// returns a `words` object if its a match, else null
function match(_ref) {
var input = _ref.input;
var text = _ref.text;
var strategy = _ref.strategy;
var nullInput = nullMatch({ input: input, text: text });
if (nullInput) return nullInput;
var inputLower = _lodash2.default.deburr(input.toLowerCase());
var textLower = _lodash2.default.deburr(text.toLowerCase());
var beginning = beginningMatch({ input: input, text: text, inputLower: inputLower, textLower: textLower });
if (beginning) return beginning;
if (strategy === 'contain' || strategy === 'fuzzy') {
var anywhere = anywhereMatch({ input: input, text: text, inputLower: inputLower, textLower: textLower });
if (anywhere) return anywhere;
}
if (strategy === 'fuzzy') {
var fuzzy = fuzzyMatch({ input: input, text: text, inputLower: inputLower, textLower: textLower });
if (fuzzy) return fuzzy;
}
}
function nullMatch(_ref2) {
var input = _ref2.input;
var text = _ref2.text;
if (input == null) {
return {
words: [{ text: text, input: false }],
remaining: null,
score: 1
};
}
}
function partialBeginningMatch(_ref3) {
var input = _ref3.input;
var text = _ref3.text;
var inputLower = _ref3.inputLower;
var textLower = _ref3.textLower;
if (_lodash2.default.startsWith(inputLower, textLower)) {
return {
words: [{ text: text, input: true }],
remaining: input.substring(text.length),
score: 1
};
}
}
function fullBeginningMatch(_ref4) {
var input = _ref4.input;
var text = _ref4.text;
var inputLower = _ref4.inputLower;
var textLower = _ref4.textLower;
if (_lodash2.default.startsWith(textLower, inputLower)) {
var words = [];
if (input.length > 0) {
words.push({ text: text.substring(0, input.length), input: true });
}
if (text.length > input.length) {
words.push({ text: text.substring(input.length), input: false });
}
return {
words: words,
remaining: null,
score: 1
};
}
}
function beginningMatch(_ref5) {
var input = _ref5.input;
var text = _ref5.text;
var inputLower = _ref5.inputLower;
var textLower = _ref5.textLower;
var partialBeginning = partialBeginningMatch({ input: input, text: text, inputLower: inputLower, textLower: textLower });
if (partialBeginning) return partialBeginning;
var fullBeginning = fullBeginningMatch({ input: input, text: text, inputLower: inputLower, textLower: textLower });
if (fullBeginning) return fullBeginning;
}
function anywhereMatch(_ref6) {
var input = _ref6.input;
var text = _ref6.text;
var inputLower = _ref6.inputLower;
var textLower = _ref6.textLower;
var index = textLower.indexOf(inputLower);
if (index > -1) {
var words = [];
var endIndex = index + input.length;
if (index > 0) {
words.push({ text: text.slice(0, index), input: false });
}
words.push({ text: text.slice(index, endIndex), input: true });
if (endIndex <= text.length - 1) {
words.push({ text: text.slice(endIndex), input: false });
}
return {
words: words,
remaining: null,
score: 1 - index / (2 * text.length)
};
}
return null;
}
function regexSplit(text) {
return _lodash2.default.map(text.split(''), _escapeStringRegexp2.default);
}
function acronymMatches(_ref7) {
var inputLower = _ref7.inputLower;
var text = _ref7.text;
if (_lodash2.default.includes(text, ' ')) {
var chars = regexSplit(inputLower);
var fuzzyString = chars.reduce(function (a, b) {
return a + '(.*(?:\\s|^))(' + b + ')';
}, '^') + '(.*)$';
var fuzzyRegex = new RegExp(fuzzyString, 'i');
var matches = text.match(fuzzyRegex);
if (matches) {
return { score: 0.5, matches: matches };
}
}
}
function capitalMatches(_ref8) {
var inputLower = _ref8.inputLower;
var text = _ref8.text;
if (/[A-Z]/.test(text)) {
var chars = regexSplit(inputLower);
var fuzzyString = chars.reduce(function (a, b) {
var bUpper = b.toUpperCase();
return a + '([^' + bUpper + ']*)(' + bUpper + ')';
}, '^') + '(.*)$';
var fuzzyRegex = new RegExp(fuzzyString);
var matches = text.match(fuzzyRegex);
if (matches) {
return { score: 0.4, matches: matches };
}
}
}
function trueFuzzyMatches(_ref9) {
var inputLower = _ref9.inputLower;
var text = _ref9.text;
var chars = regexSplit(inputLower);
var fuzzyString = chars.reduce(function (a, b) {
return a + '([^' + b + ']*)(' + b + ')';
}, '^') + '(.*)$';
var fuzzyRegex = new RegExp(fuzzyString, 'i');
var matches = text.match(fuzzyRegex);
if (matches) {
return { score: 0.3 * (1 / matches.length), matches: matches };
}
}
function fuzzyMatch(_ref10) {
var input = _ref10.input;
var text = _ref10.text;
var inputLower = _ref10.inputLower;
var textLower = _ref10.textLower;
var _ref11 = acronymMatches({ inputLower: inputLower, text: text }) || capitalMatches({ inputLower: inputLower, text: text }) || trueFuzzyMatches({ inputLower: inputLower, text: text }) || {};
var score = _ref11.score;
var matches = _ref11.matches;
if (matches) {
var words = [];
for (var i = 1, l = matches.length; i < l; i++) {
if (matches[i].length > 0) {
words.push({
text: matches[i],
input: i % 2 === 0
});
}
}
return {
words: words,
remaining: null,
score: score
};
}
return null;
}
// export function * sort (input, items) {
// let itemSet = _.map(items, item => ({item, matched: false}))
//
// for (let [func, score] of [[beginningMatch, 1], [anywhereMatch, 0.5]]) {
// yield * sortFunction({input, itemSet, func, score})
// }
// }
//
// function * sortFunction ({input, itemSet, func, score}) {
// for (let obj of itemSet) {
// if (!obj.matched) {
// const words = func({input, text: obj.item.text, qualifier: obj.item.qualifier})
// if (words) {
// obj.matched = true
// _.forEach(words, word => word.descriptor = obj.item.descriptor)
// yield {words, result: obj.item.value, score}
// }
// }
// }
// }
//
// escape special characters, and wrap in parens (for matching)
// function regexEscape (str) {
// return `(${str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/, '\\$&')})`
// }
// function beginningMatch ({input, text, qualifier}) {
// if (_.startsWith(text.toLowerCase(), input.toLowerCase())) {
// const matches = [{text: text.slice(0, input.length), input: true, qualifier}]
// if (input.length < text.length) {
// matches.push({text: text.slice(input.length), input: false, qualifier})
// }
// return matches
// }
// return null
// }