elliptical
Version:
Interactive natural-language interfaces
262 lines (216 loc) • 7.29 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) {
let input = _ref.input;
let text = _ref.text;
let strategy = _ref.strategy;
const nullInput = nullMatch({ input: input, text: text });
if (nullInput) return nullInput;
const inputLower = _lodash2.default.deburr(input.toLowerCase());
const textLower = _lodash2.default.deburr(text.toLowerCase());
const beginning = beginningMatch({ input: input, text: text, inputLower: inputLower, textLower: textLower });
if (beginning) return beginning;
if (strategy === 'contain' || strategy === 'fuzzy') {
const anywhere = anywhereMatch({ input: input, text: text, inputLower: inputLower, textLower: textLower });
if (anywhere) return anywhere;
}
if (strategy === 'fuzzy') {
const fuzzy = fuzzyMatch({ input: input, text: text, inputLower: inputLower, textLower: textLower });
if (fuzzy) return fuzzy;
}
}
function nullMatch(_ref2) {
let input = _ref2.input;
let text = _ref2.text;
if (input == null) {
return {
words: [{ text: text, input: false }],
remaining: null,
score: 1
};
}
}
function partialBeginningMatch(_ref3) {
let input = _ref3.input;
let text = _ref3.text;
let inputLower = _ref3.inputLower;
let 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) {
let input = _ref4.input;
let text = _ref4.text;
let inputLower = _ref4.inputLower;
let textLower = _ref4.textLower;
if (_lodash2.default.startsWith(textLower, inputLower)) {
const 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) {
let input = _ref5.input;
let text = _ref5.text;
let inputLower = _ref5.inputLower;
let textLower = _ref5.textLower;
const partialBeginning = partialBeginningMatch({ input: input, text: text, inputLower: inputLower, textLower: textLower });
if (partialBeginning) return partialBeginning;
const fullBeginning = fullBeginningMatch({ input: input, text: text, inputLower: inputLower, textLower: textLower });
if (fullBeginning) return fullBeginning;
}
function anywhereMatch(_ref6) {
let input = _ref6.input;
let text = _ref6.text;
let inputLower = _ref6.inputLower;
let textLower = _ref6.textLower;
const index = textLower.indexOf(inputLower);
if (index > -1) {
const words = [];
const 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) {
let inputLower = _ref7.inputLower;
let text = _ref7.text;
if (_lodash2.default.includes(text, ' ')) {
const chars = regexSplit(inputLower);
const fuzzyString = chars.reduce((a, b) => `${ a }(.*(?:\\s|^))(${ b })`, '^') + '(.*)$';
const fuzzyRegex = new RegExp(fuzzyString, 'i');
const matches = text.match(fuzzyRegex);
if (matches) {
return { score: 0.5, matches: matches };
}
}
}
function capitalMatches(_ref8) {
let inputLower = _ref8.inputLower;
let text = _ref8.text;
if (/[A-Z]/.test(text)) {
const chars = regexSplit(inputLower);
const fuzzyString = chars.reduce((a, b) => {
const bUpper = b.toUpperCase();
return `${ a }([^${ bUpper }]*)(${ bUpper })`;
}, '^') + '(.*)$';
const fuzzyRegex = new RegExp(fuzzyString);
const matches = text.match(fuzzyRegex);
if (matches) {
return { score: 0.4, matches: matches };
}
}
}
function trueFuzzyMatches(_ref9) {
let inputLower = _ref9.inputLower;
let text = _ref9.text;
const chars = regexSplit(inputLower);
const fuzzyString = chars.reduce((a, b) => `${ a }([^${ b }]*)(${ b })`, '^') + '(.*)$';
const fuzzyRegex = new RegExp(fuzzyString, 'i');
const matches = text.match(fuzzyRegex);
if (matches) {
return { score: 0.3 * (1 / matches.length), matches: matches };
}
}
function fuzzyMatch(_ref10) {
let input = _ref10.input;
let text = _ref10.text;
let inputLower = _ref10.inputLower;
let textLower = _ref10.textLower;
var _ref11 = acronymMatches({ inputLower: inputLower, text: text }) || capitalMatches({ inputLower: inputLower, text: text }) || trueFuzzyMatches({ inputLower: inputLower, text: text }) || {};
const score = _ref11.score;
const matches = _ref11.matches;
if (matches) {
const words = [];
for (let 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
// }