UNPKG

elliptical

Version:

Interactive natural-language interfaces

262 lines (216 loc) 7.29 kB
'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 // }