@hal313/settings-manager-chrome-extension-adapter
Version:
A backing repository for settings-manager, falling on chrome.storage.local (or sync) as an implementation.
304 lines (263 loc) • 9.42 kB
JavaScript
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(["exports"], factory);
} else if (typeof exports !== "undefined") {
factory(exports);
} else {
var mod = {
exports: {}
};
factory(mod.exports);
global.SettingsManager = mod.exports;
}
})(this, function (_exports) {
"use strict";
Object.defineProperty(_exports, "__esModule", {
value: true
});
_exports.isFunction = isFunction;
_exports.isObject = isObject;
_exports.isArray = isArray;
_exports.merge = merge;
_exports.execute = execute;
_exports.SettingsManager = _exports.InMemoryStore = void 0;
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
/**
* Determines if a value is a function.
*
* @param {*} candidate the candidate to test
* @returns {boolean} true, if candidate is a Function
*/
function isFunction(candidate) {
return 'function' === typeof candidate;
}
/**
* Determines if a value is an object.
*
* @param {*} candidate the candidate to test
* @returns {boolean} true, if candidate is an object
*/
function isObject(candidate) {
return null !== candidate && 'object' === _typeof(candidate) && !isArray(candidate);
}
/**
* Determines if a value is an array.
*
* @param {*} candidate the candidate to test
* @returns {boolean} true, if candidate is an array
*/
function isArray(candidate) {
return Array.isArray(candidate);
}
/**
* Merges two objects; members of the second object take precedence over the first object.
*
* @param {Object} target an object to merge to
* @returns {Object} the target object, populated with the source object's members
*/
function merge(target, source) {
// Merge algorithm adapted from:
// From (http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically)
var array = isArray(source),
dest = array && [] || {};
if (array) {
target = target || [];
dest = dest.concat(target);
source.forEach(function onItem(e, i) {
if ('undefined' === typeof dest[i]) {
dest[i] = e;
} else if ('object' === _typeof(e)) {
dest[i] = merge(target[i], e);
} else {
if (target.indexOf(e) === -1) {
dest.push(e);
}
}
});
} else {
if (isObject(target)) {
Object.keys(target || {}).forEach(function (key) {
dest[key] = target[key];
});
}
Object.keys(source || {}).forEach(function (key) {
if (!isObject(source[key]) || !source[key]) {
dest[key] = source[key];
} else {
if (!target[key]) {
dest[key] = source[key];
} else {
dest[key] = merge(target[key], source[key]);
}
}
});
}
return dest;
}
/**
* Executes a function.
*
* @param {Function} fn the function to execute
* @param {*[]} args an array of arguments to execute the function with
* @param {Object} [context] the optional context
* @returns {*} the return value of the function execution, or null if no function is provided
*/
function execute(fn, args, context) {
return new Promise(function (resolve, reject) {
if (isFunction(fn)) {
try {
resolve(fn.apply(context || {}, args));
} catch (error) {
reject(error);
}
}
resolve();
});
}
/**
* A store implementation in memory.
*/
var InMemoryStore =
/*#__PURE__*/
function () {
/**
* Creates an instance.
*/
function InMemoryStore() {
_classCallCheck(this, InMemoryStore);
this.settings = {};
}
/**
* Loads values.
*
* @returns {Promise} resolves with the settings
*/
_createClass(InMemoryStore, [{
key: "load",
value: function load() {
return Promise.resolve(merge({}, this.settings));
}
}, {
key: "save",
/**
* Saves values.
*
* @param {Object} settings the settings to save
* @returns {Promise} resolves with the settings
*/
value: function save(settings) {
// Assign the settings
this.settings = settings; // Return the settings
return this.load();
}
}, {
key: "clear",
/**
* Clears values.
*
* @returns {Promise} resolves with empty settings
*/
value: function clear() {
// Save the empty settings
return this.save({});
}
}]);
return InMemoryStore;
}();
_exports.InMemoryStore = InMemoryStore;
;
/**
* Implementation for SettingsManager, a class for storing settings.
*
* @param {Object} [backingStore] optional backing store to wrap
*/
var SettingsManager =
/*#__PURE__*/
function () {
/**
* Creates a SettingsManager instance backed by an optional store.
*
* @param {Store} [backingStore] optional store implementation to use
*/
function SettingsManager(backingStore) {
_classCallCheck(this, SettingsManager);
this.backingStore = backingStore || new InMemoryStore();
}
/**
* Loads values.
*
* @param {Function} [successCallback] the callback invoked on success (invoked with the settings)
* @param {Function} [errorCallback] the error callback, invoked on failure
* @returns {Promise} always resolves (never rejects) after the callback has been invoked
*/
_createClass(SettingsManager, [{
key: "load",
value: function load(successCallback, errorCallback) {
return cleanBackingStoreFunctionPromise(this.backingStore.load(), successCallback, errorCallback);
}
}, {
key: "save",
/**
* Saves values.
*
* @param {Object} settings the settings to save
* @param {Function} [successCallback] the callback invoked on success
* @param {Function} [errorCallback] the error callback, invoked on failure
* @returns {Promise} always resolves (never rejects) after the callback has been invoked
*/
value: function save(settings, successCallback, errorCallback) {
var _this = this;
if (!settings || !isObject(settings)) {
return cleanPromise(execute(errorCallback, ['"settings" is not an object']));
} else {
return cleanBackingStoreFunctionPromise( // Merge with existing settings
this.backingStore.load().then(function (loadedSettings) {
return _this.backingStore.save(merge(loadedSettings, settings));
}).then(function () {
return _this.backingStore.load();
}), successCallback, errorCallback);
}
}
}, {
key: "clear",
/**
* Clears values.
*
* @param {Function} successCallback the success callback to invoke on success
* @param {Function} [errorCallback] the error callback, invoked on failure
* @returns {Promise} resolves
*/
value: function clear(successCallback, errorCallback) {
return cleanBackingStoreFunctionPromise(this.backingStore.clear(), successCallback, errorCallback);
}
}]);
return SettingsManager;
}();
/**
* Cleans a promise by adding a no-op then and a no-op catch.
*
* @param {Promise} promise the promise to clean
* @return {Promise} a promise which always resolves nothing
*/
_exports.SettingsManager = SettingsManager;
function cleanPromise(promise) {
return promise.then(function () {})["catch"](function () {});
}
/**
* Cleans a promise from a backing store call and invokes the correct callback.
*
* @param {Promise} backingStorePromise the promise from the backing store function
* @param {Function} [successCallback] the success callback (invoked with the result from the backing store function)
* @param {Function} [errorCallback] the error callback (invoked with the error from the backing store function)
*/
function cleanBackingStoreFunctionPromise(backingStorePromise, successCallback, errorCallback) {
return backingStorePromise.then(function (result) {
return cleanPromise(execute(successCallback, [result]));
})["catch"](function (error) {
return cleanPromise(execute(errorCallback, [error]));
});
}
});