recent-searches
Version:
A JavaScript module to help anyone easily build recent searches functionality into their search.
367 lines (299 loc) • 12.1 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define("RecentSearches", [], factory);
else if(typeof exports === 'object')
exports["RecentSearches"] = factory();
else
root["RecentSearches"] = factory();
})((typeof self !== 'undefined' ? self : this), function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
;
__webpack_require__.r(__webpack_exports__);
// CONCATENATED MODULE: ./lib/MemoryStorage.ts
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var MemoryStorage_MemoryStorage = function MemoryStorage(config) {
var _this = this;
_classCallCheck(this, MemoryStorage);
_defineProperty(this, "DATA", {});
_defineProperty(this, "KEY", void 0);
_defineProperty(this, "DEFAULT_VALUE", void 0);
_defineProperty(this, "getItem", function () {
return _this.DATA[_this.KEY] || _this.DEFAULT_VALUE;
});
_defineProperty(this, "setItem", function (data) {
_this.DATA[_this.KEY] = data;
return true;
});
var key = config.key,
defaultValue = config.defaultValue;
this.DATA = {};
this.KEY = key || DEFAULT_STORAGE_KEY;
this.DEFAULT_VALUE = defaultValue;
};
/* harmony default export */ var lib_MemoryStorage = (MemoryStorage_MemoryStorage);
// CONCATENATED MODULE: ./lib/SafeLocalStorage.ts
function SafeLocalStorage_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function SafeLocalStorage_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var DEFAULT_STORAGE_KEY = "__RECENT_SEARCHES__";
var isLocalStorageSupported = function isLocalStorageSupported() {
var key = "__TEST__KEY__";
try {
localStorage.setItem(key, "");
localStorage.removeItem(key);
return true;
} catch (error) {
return false;
}
};
var safeDataParse = function safeDataParse(data, defaultValue) {
if (!data) {
return defaultValue;
}
try {
return JSON.parse(data);
} catch (e) {
return defaultValue;
}
};
var SafeLocalStorage = function SafeLocalStorage(config) {
var _this = this;
SafeLocalStorage_classCallCheck(this, SafeLocalStorage);
SafeLocalStorage_defineProperty(this, "KEY", void 0);
SafeLocalStorage_defineProperty(this, "DEFAULT_VALUE", void 0);
SafeLocalStorage_defineProperty(this, "getItem", function () {
var data = localStorage.getItem(_this.KEY);
return safeDataParse(data, _this.DEFAULT_VALUE);
});
SafeLocalStorage_defineProperty(this, "setItem", function (items) {
try {
localStorage.setItem(_this.KEY, JSON.stringify(items));
return true;
} catch (e) {
return false;
}
});
var key = config.key,
defaultValue = config.defaultValue;
this.KEY = key || DEFAULT_STORAGE_KEY;
this.DEFAULT_VALUE = defaultValue;
};
var SafeLocalStorage_NewSafeLocalStorage = function NewSafeLocalStorage(config) {
if (!isLocalStorageSupported()) {
return new lib_MemoryStorage(config);
}
return new SafeLocalStorage(config);
};
/* harmony default export */ var lib_SafeLocalStorage = (SafeLocalStorage_NewSafeLocalStorage);
// CONCATENATED MODULE: ./lib/utils/score.ts
var computeMatchScore = function computeMatchScore(search, query, rankBy, ttl) {
var normalizedQuery = String(query);
switch (rankBy) {
case "PROXIMITY":
return search.query.indexOf(normalizedQuery);
case "TIME":
return Math.log10(new Date().getTime() - search.timestamp);
default:
var matchDistance = search.query.indexOf(normalizedQuery);
var timeDelta = Math.log2((new Date().getTime() - search.timestamp) / ttl + 1);
var proximity = Math.log2(matchDistance + 1) || 1;
if (matchDistance === -1) {
return matchDistance;
}
return (0.01 + 0.49 * proximity + 0.49 * timeDelta) / 1;
}
};
/* harmony default export */ var utils_score = (computeMatchScore);
// CONCATENATED MODULE: ./lib/utils/string.ts
var isBlank = function isBlank() {
var str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
return /^\s*$/.test(str);
};
var isValidQuery = function isValidQuery(query) {
return typeof query === "string" && !isBlank(query) || typeof query === "number";
};
/* harmony default export */ var string = (isValidQuery);
// CONCATENATED MODULE: ./lib/index.ts
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RecentSearches", function() { return lib_RecentSearches; });
function lib_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function lib_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var lib_RecentSearches = function RecentSearches() {
var _this = this;
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
lib_classCallCheck(this, RecentSearches);
lib_defineProperty(this, "TTL", void 0);
lib_defineProperty(this, "LIMIT", void 0);
lib_defineProperty(this, "STORAGE", void 0);
lib_defineProperty(this, "RANKING", void 0);
lib_defineProperty(this, "RECENT_SEARCHES", []);
lib_defineProperty(this, "getRecentSearches", function (query) {
if (!string(query)) {
return _this.RECENT_SEARCHES;
}
var matchedSearches = _this.RECENT_SEARCHES.map(function (search) {
var score = utils_score(search, query, _this.RANKING, _this.TTL);
return {
data: search.data,
query: search.query,
score: score,
timestamp: search.timestamp
};
}).filter(_this.filterScoredResults).sort(_this.sortScoredResults).map(function (search) {
return {
data: search.data,
query: search.query,
timestamp: search.timestamp
};
});
return matchedSearches;
});
lib_defineProperty(this, "setRecentSearch", function (query, data) {
if (!string(query)) {
return _this.RECENT_SEARCHES;
}
var search = {
data: data,
query: String(query),
timestamp: new Date().getTime()
};
var existingQueryIndex = _this.RECENT_SEARCHES.findIndex(function (searchEntry) {
return searchEntry.query === query;
});
if (existingQueryIndex > -1) {
_this.RECENT_SEARCHES.splice(existingQueryIndex, 1);
}
_this.RECENT_SEARCHES.unshift(search);
_this.RECENT_SEARCHES = _this.RECENT_SEARCHES.slice(0, _this.LIMIT);
_this.STORAGE.setItem(_this.RECENT_SEARCHES);
return _this.RECENT_SEARCHES;
});
lib_defineProperty(this, "filterScoredResults", function (search) {
if (_this.RANKING === "TIME") {
return true;
}
return search.score > -1;
});
lib_defineProperty(this, "sortScoredResults", function (a, b) {
return a.score - b.score;
});
lib_defineProperty(this, "initializeStorageData", function () {
var currentTimestamp = new Date().getTime();
var items = _this.STORAGE.getItem().filter(function (search) {
return search.timestamp + _this.TTL >= currentTimestamp;
}).slice(0, _this.LIMIT);
_this.STORAGE.setItem(items);
_this.RECENT_SEARCHES = items;
return items;
});
this.TTL = config.ttl || 1000 * 60 * 60 * 24;
this.LIMIT = config.limit || 50;
this.STORAGE = lib_SafeLocalStorage({
defaultValue: [],
key: config.namespace
});
this.RECENT_SEARCHES = this.initializeStorageData();
this.RANKING = config.ranking || "PROXIMITY_AND_TIME";
}
/**
* Retrieve recent searches for a given query.
* If no query is passed, returns all recent searches
*
* @param {string} query?
* @returns Search[]
*/
;
/* harmony default export */ var lib = __webpack_exports__["default"] = (lib_RecentSearches);
/***/ })
/******/ ]);
});
//# sourceMappingURL=index.js.map