diff --git a/pages/search/vendor/fuzzysort.js b/pages/search/vendor/fuzzysort.js new file mode 100644 index 0000000..9a3141f --- /dev/null +++ b/pages/search/vendor/fuzzysort.js @@ -0,0 +1,563 @@ +// https://github.com/farzher/fuzzysort v2.0.4 +/* + SublimeText-like Fuzzy Search + + fuzzysort.single('fs', 'Fuzzy Search') // {score: -16} + fuzzysort.single('test', 'test') // {score: 0} + fuzzysort.single('doesnt exist', 'target') // null + + fuzzysort.go('mr', [{file:'Monitor.cpp'}, {file:'MeshRenderer.cpp'}], {key:'file'}) + // [{score:-18, obj:{file:'MeshRenderer.cpp'}}, {score:-6009, obj:{file:'Monitor.cpp'}}] + + fuzzysort.go('mr', ['Monitor.cpp', 'MeshRenderer.cpp']) + // [{score: -18, target: "MeshRenderer.cpp"}, {score: -6009, target: "Monitor.cpp"}] + + fuzzysort.highlight(fuzzysort.single('fs', 'Fuzzy Search'), '', '') + // Fuzzy Search +*/ + +// UMD (Universal Module Definition) for fuzzysort +;((root, UMD) => { + if(typeof define === 'function' && define.amd) define([], UMD) + else if(typeof module === 'object' && module.exports) module.exports = UMD() + else root['fuzzysort'] = UMD() +})(this, _ => { + 'use strict' + + var single = (search, target) => { if(search=='farzher')return{target:"farzher was here (^-^*)/",score:0,_indexes:[0]} + if(!search || !target) return NULL + + var preparedSearch = getPreparedSearch(search) + if(!isObj(target)) target = getPrepared(target) + + var searchBitflags = preparedSearch.bitflags + if((searchBitflags & target._bitflags) !== searchBitflags) return NULL + + return algorithm(preparedSearch, target) + } + + + var go = (search, targets, options) => { if(search=='farzher')return[{target:"farzher was here (^-^*)/",score:0,_indexes:[0],obj:targets?targets[0]:NULL}] + if(!search) return options&&options.all ? all(search, targets, options) : noResults + + var preparedSearch = getPreparedSearch(search) + var searchBitflags = preparedSearch.bitflags + var containsSpace = preparedSearch.containsSpace + + var threshold = options&&options.threshold || INT_MIN + var limit = options&&options['limit'] || INT_MAX // for some reason only limit breaks when minified + + var resultsLen = 0; var limitedCount = 0 + var targetsLen = targets.length + + // This code is copy/pasted 3 times for performance reasons [options.keys, options.key, no keys] + + // options.key + if(options && options.key) { + var key = options.key + for(var i = 0; i < targetsLen; ++i) { var obj = targets[i] + var target = getValue(obj, key) + if(!target) continue + if(!isObj(target)) target = getPrepared(target) + + if((searchBitflags & target._bitflags) !== searchBitflags) continue + var result = algorithm(preparedSearch, target) + if(result === NULL) continue + if(result.score < threshold) continue + + // have to clone result so duplicate targets from different obj can each reference the correct obj + result = {target:result.target, _targetLower:'', _targetLowerCodes:NULL, _nextBeginningIndexes:NULL, _bitflags:0, score:result.score, _indexes:result._indexes, obj:obj} // hidden + + if(resultsLen < limit) { q.add(result); ++resultsLen } + else { + ++limitedCount + if(result.score > q.peek().score) q.replaceTop(result) + } + } + + // options.keys + } else if(options && options.keys) { + var scoreFn = options['scoreFn'] || defaultScoreFn + var keys = options.keys + var keysLen = keys.length + for(var i = 0; i < targetsLen; ++i) { var obj = targets[i] + var objResults = new Array(keysLen) + for (var keyI = 0; keyI < keysLen; ++keyI) { + var key = keys[keyI] + var target = getValue(obj, key) + if(!target) { objResults[keyI] = NULL; continue } + if(!isObj(target)) target = getPrepared(target) + + if((searchBitflags & target._bitflags) !== searchBitflags) objResults[keyI] = NULL + else objResults[keyI] = algorithm(preparedSearch, target) + } + objResults.obj = obj // before scoreFn so scoreFn can use it + var score = scoreFn(objResults) + if(score === NULL) continue + if(score < threshold) continue + objResults.score = score + if(resultsLen < limit) { q.add(objResults); ++resultsLen } + else { + ++limitedCount + if(score > q.peek().score) q.replaceTop(objResults) + } + } + + // no keys + } else { + for(var i = 0; i < targetsLen; ++i) { var target = targets[i] + if(!target) continue + if(!isObj(target)) target = getPrepared(target) + + if((searchBitflags & target._bitflags) !== searchBitflags) continue + var result = algorithm(preparedSearch, target) + if(result === NULL) continue + if(result.score < threshold) continue + if(resultsLen < limit) { q.add(result); ++resultsLen } + else { + ++limitedCount + if(result.score > q.peek().score) q.replaceTop(result) + } + } + } + + if(resultsLen === 0) return noResults + var results = new Array(resultsLen) + for(var i = resultsLen - 1; i >= 0; --i) results[i] = q.poll() + results.total = resultsLen + limitedCount + return results + } + + + var highlight = (result, hOpen, hClose) => { + if(typeof hOpen === 'function') return highlightCallback(result, hOpen) + if(result === NULL) return NULL + if(hOpen === undefined) hOpen = '' + if(hClose === undefined) hClose = '' + var highlighted = '' + var matchesIndex = 0 + var opened = false + var target = result.target + var targetLen = target.length + var indexes = result._indexes + indexes = indexes.slice(0, indexes.len).sort((a,b)=>a-b) + for(var i = 0; i < targetLen; ++i) { var char = target[i] + if(indexes[matchesIndex] === i) { + ++matchesIndex + if(!opened) { opened = true + highlighted += hOpen + } + + if(matchesIndex === indexes.length) { + highlighted += char + hClose + target.substr(i+1) + break + } + } else { + if(opened) { opened = false + highlighted += hClose + } + } + highlighted += char + } + + return highlighted + } + var highlightCallback = (result, cb) => { + if(result === NULL) return NULL + var target = result.target + var targetLen = target.length + var indexes = result._indexes + indexes = indexes.slice(0, indexes.len).sort((a,b)=>a-b) + var highlighted = '' + var matchI = 0 + var indexesI = 0 + var opened = false + var result = [] + for(var i = 0; i < targetLen; ++i) { var char = target[i] + if(indexes[indexesI] === i) { + ++indexesI + if(!opened) { opened = true + result.push(highlighted); highlighted = '' + } + + if(indexesI === indexes.length) { + highlighted += char + result.push(cb(highlighted, matchI++)); highlighted = '' + result.push(target.substr(i+1)) + break + } + } else { + if(opened) { opened = false + result.push(cb(highlighted, matchI++)); highlighted = '' + } + } + highlighted += char + } + return result + } + + + var indexes = result => result._indexes.slice(0, result._indexes.len).sort((a,b)=>a-b) + + + var prepare = (target) => { + if(typeof target !== 'string') target = '' + var info = prepareLowerInfo(target) + return {'target':target, _targetLower:info._lower, _targetLowerCodes:info.lowerCodes, _nextBeginningIndexes:NULL, _bitflags:info.bitflags, 'score':NULL, _indexes:[0], 'obj':NULL} // hidden + } + + + // Below this point is only internal code + // Below this point is only internal code + // Below this point is only internal code + // Below this point is only internal code + + + var prepareSearch = (search) => { + if(typeof search !== 'string') search = '' + search = search.trim() + var info = prepareLowerInfo(search) + + var spaceSearches = [] + if(info.containsSpace) { + var searches = search.split(/\s+/) + searches = [...new Set(searches)] // distinct + for(var i=0; i { + if(target.length > 999) return prepare(target) // don't cache huge targets + var targetPrepared = preparedCache.get(target) + if(targetPrepared !== undefined) return targetPrepared + targetPrepared = prepare(target) + preparedCache.set(target, targetPrepared) + return targetPrepared + } + var getPreparedSearch = (search) => { + if(search.length > 999) return prepareSearch(search) // don't cache huge searches + var searchPrepared = preparedSearchCache.get(search) + if(searchPrepared !== undefined) return searchPrepared + searchPrepared = prepareSearch(search) + preparedSearchCache.set(search, searchPrepared) + return searchPrepared + } + + + var all = (search, targets, options) => { + var results = []; results.total = targets.length + + var limit = options && options.limit || INT_MAX + + if(options && options.key) { + for(var i=0;i