mirror of https://github.com/prometheus/prometheus
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
183 lines
5.5 KiB
183 lines
5.5 KiB
/*
|
|
* Fuzzy
|
|
* https://github.com/myork/fuzzy
|
|
*
|
|
* Copyright (c) 2012 Matt York
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use,
|
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following
|
|
* conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* A slightly modified version of https://github.com/mattyork/fuzzy/blob/3613086aa40c180ca722aeaf48cef575dc57eb5d/lib/fuzzy.js
|
|
*/
|
|
|
|
(function() {
|
|
|
|
var root = this;
|
|
|
|
var fuzzy = {};
|
|
|
|
// Use in node or in browser
|
|
if (typeof exports !== 'undefined') {
|
|
module.exports = fuzzy;
|
|
} else {
|
|
root.fuzzy = fuzzy;
|
|
}
|
|
|
|
// Return all elements of `array` that have a fuzzy
|
|
// match against `pattern`.
|
|
fuzzy.simpleFilter = function(pattern, array) {
|
|
return array.filter(function(str) {
|
|
return fuzzy.test(pattern, str);
|
|
});
|
|
};
|
|
|
|
// Does `pattern` fuzzy match `str`?
|
|
fuzzy.test = function(pattern, str) {
|
|
return fuzzy.match(pattern, str) !== null;
|
|
};
|
|
|
|
// If `pattern` matches `str`, wrap each matching character
|
|
// in `opts.pre` and `opts.post`. If no match, return null
|
|
fuzzy.match = function(pattern, str, opts, _fromIndex) {
|
|
opts = opts || {};
|
|
var patternIdx = 0
|
|
, result = []
|
|
, len = str.length
|
|
, fromIndex = _fromIndex || 0
|
|
, totalScore = 0
|
|
, currScore = 0
|
|
// prefix
|
|
, pre = opts.pre || ''
|
|
// suffix
|
|
, post = opts.post || ''
|
|
// String to compare against. This might be a lowercase version of the
|
|
// raw string
|
|
, compareString = opts.caseSensitive && str || str.toLowerCase()
|
|
, ch;
|
|
|
|
pattern = opts.caseSensitive && pattern || pattern.toLowerCase();
|
|
|
|
// If there's an exact match, add pre/post, max out score and skip the lookup
|
|
if (compareString === pattern) {
|
|
return {
|
|
rendered: pre + compareString.split('').join(post+pre) + post,
|
|
score: Infinity
|
|
};
|
|
}
|
|
|
|
// For each character in the string, either add it to the result
|
|
// or wrap in template if it's the next string in the pattern
|
|
for(var idx = 0; idx < len; idx++) {
|
|
ch = str[idx];
|
|
if(idx >= fromIndex && compareString[idx] === pattern[patternIdx]) {
|
|
ch = pre + ch + post;
|
|
patternIdx += 1;
|
|
|
|
// consecutive characters should increase the score more than linearly
|
|
currScore += 1 + currScore;
|
|
} else {
|
|
currScore = 0;
|
|
}
|
|
totalScore += currScore;
|
|
result[result.length] = ch;
|
|
}
|
|
|
|
// return rendered string if we have a match for every char
|
|
if(patternIdx === pattern.length) {
|
|
var nextPossible = str.indexOf(pattern[0], str.indexOf(pattern[0], fromIndex) + 1)
|
|
, candidate;
|
|
|
|
// If possible, try to find a better match at the rest of the string
|
|
if (nextPossible > -1 && str.length - nextPossible >= pattern.length) {
|
|
var candidate = fuzzy.match(pattern, str, opts, nextPossible);
|
|
}
|
|
|
|
return candidate && candidate.score > totalScore ? candidate : {
|
|
rendered: result.join(''), score: totalScore
|
|
};
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
// The normal entry point. Filters `arr` for matches against `pattern`.
|
|
// It returns an array with matching values of the type:
|
|
//
|
|
// [{
|
|
// string: '<b>lah' // The rendered string
|
|
// , index: 2 // The index of the element in `arr`
|
|
// , original: 'blah' // The original element in `arr`
|
|
// }]
|
|
//
|
|
// `opts` is an optional argument bag. Details:
|
|
//
|
|
// opts = {
|
|
// // string to put before a matching character
|
|
// pre: '<b>'
|
|
//
|
|
// // string to put after matching character
|
|
// , post: '</b>'
|
|
//
|
|
// // Optional function. Input is an entry in the given arr`,
|
|
// // output should be the string to test `pattern` against.
|
|
// // In this example, if `arr = [{crying: 'koala'}]` we would return
|
|
// // 'koala'.
|
|
// , extract: function(arg) { return arg.crying; }
|
|
// }
|
|
fuzzy.filter = function(pattern, arr, opts) {
|
|
if(!arr || arr.length === 0) {
|
|
return [];
|
|
}
|
|
if (typeof pattern !== 'string' || pattern === '') {
|
|
return arr;
|
|
}
|
|
opts = opts || {};
|
|
return arr
|
|
.reduce(function(prev, element, idx, arr) {
|
|
var str = element;
|
|
if(opts.extract) {
|
|
str = opts.extract(element);
|
|
}
|
|
var rendered = fuzzy.match(pattern, str, opts);
|
|
if(rendered != null) {
|
|
prev[prev.length] = {
|
|
string: rendered.rendered
|
|
, score: rendered.score
|
|
, index: idx
|
|
, original: element
|
|
};
|
|
}
|
|
return prev;
|
|
}, [])
|
|
|
|
// Sort by score. Browsers are inconsistent wrt stable/unstable
|
|
// sorting, so force stable by using the index in the case of tie.
|
|
// See http://ofb.net/~sethml/is-sort-stable.html
|
|
.sort(function(a,b) {
|
|
var compare = b.score - a.score;
|
|
if(compare) return compare;
|
|
return a.index - b.index;
|
|
});
|
|
};
|
|
|
|
|
|
}());
|