183 lines
5.1 KiB
JavaScript
183 lines
5.1 KiB
JavaScript
/* eslint no-eq-null: 0, eqeqeq: 0, no-unused-vars: 0 */
|
|
|
|
"use strict";
|
|
|
|
var customError = require("es5-ext/error/custom")
|
|
, defineLength = require("es5-ext/function/_define-length")
|
|
, d = require("d")
|
|
, ee = require("event-emitter").methods
|
|
, resolveResolve = require("./resolve-resolve")
|
|
, resolveNormalize = require("./resolve-normalize");
|
|
|
|
var apply = Function.prototype.apply
|
|
, call = Function.prototype.call
|
|
, create = Object.create
|
|
, defineProperties = Object.defineProperties
|
|
, on = ee.on
|
|
, emit = ee.emit;
|
|
|
|
module.exports = function (original, length, options) {
|
|
var cache = create(null)
|
|
, conf
|
|
, memLength
|
|
, get
|
|
, set
|
|
, del
|
|
, clear
|
|
, extDel
|
|
, extGet
|
|
, extHas
|
|
, normalizer
|
|
, getListeners
|
|
, setListeners
|
|
, deleteListeners
|
|
, memoized
|
|
, resolve;
|
|
if (length !== false) memLength = length;
|
|
else if (isNaN(original.length)) memLength = 1;
|
|
else memLength = original.length;
|
|
|
|
if (options.normalizer) {
|
|
normalizer = resolveNormalize(options.normalizer);
|
|
get = normalizer.get;
|
|
set = normalizer.set;
|
|
del = normalizer.delete;
|
|
clear = normalizer.clear;
|
|
}
|
|
if (options.resolvers != null) resolve = resolveResolve(options.resolvers);
|
|
|
|
if (get) {
|
|
memoized = defineLength(function (arg) {
|
|
var id, result, args = arguments;
|
|
if (resolve) args = resolve(args);
|
|
id = get(args);
|
|
if (id !== null) {
|
|
if (hasOwnProperty.call(cache, id)) {
|
|
if (getListeners) conf.emit("get", id, args, this);
|
|
return cache[id];
|
|
}
|
|
}
|
|
if (args.length === 1) result = call.call(original, this, args[0]);
|
|
else result = apply.call(original, this, args);
|
|
if (id === null) {
|
|
id = get(args);
|
|
if (id !== null) throw customError("Circular invocation", "CIRCULAR_INVOCATION");
|
|
id = set(args);
|
|
} else if (hasOwnProperty.call(cache, id)) {
|
|
throw customError("Circular invocation", "CIRCULAR_INVOCATION");
|
|
}
|
|
cache[id] = result;
|
|
if (setListeners) conf.emit("set", id, null, result);
|
|
return result;
|
|
}, memLength);
|
|
} else if (length === 0) {
|
|
memoized = function () {
|
|
var result;
|
|
if (hasOwnProperty.call(cache, "data")) {
|
|
if (getListeners) conf.emit("get", "data", arguments, this);
|
|
return cache.data;
|
|
}
|
|
if (arguments.length) result = apply.call(original, this, arguments);
|
|
else result = call.call(original, this);
|
|
if (hasOwnProperty.call(cache, "data")) {
|
|
throw customError("Circular invocation", "CIRCULAR_INVOCATION");
|
|
}
|
|
cache.data = result;
|
|
if (setListeners) conf.emit("set", "data", null, result);
|
|
return result;
|
|
};
|
|
} else {
|
|
memoized = function (arg) {
|
|
var result, args = arguments, id;
|
|
if (resolve) args = resolve(arguments);
|
|
id = String(args[0]);
|
|
if (hasOwnProperty.call(cache, id)) {
|
|
if (getListeners) conf.emit("get", id, args, this);
|
|
return cache[id];
|
|
}
|
|
if (args.length === 1) result = call.call(original, this, args[0]);
|
|
else result = apply.call(original, this, args);
|
|
if (hasOwnProperty.call(cache, id)) {
|
|
throw customError("Circular invocation", "CIRCULAR_INVOCATION");
|
|
}
|
|
cache[id] = result;
|
|
if (setListeners) conf.emit("set", id, null, result);
|
|
return result;
|
|
};
|
|
}
|
|
conf = {
|
|
original: original,
|
|
memoized: memoized,
|
|
profileName: options.profileName,
|
|
get: function (args) {
|
|
if (resolve) args = resolve(args);
|
|
if (get) return get(args);
|
|
return String(args[0]);
|
|
},
|
|
has: function (id) { return hasOwnProperty.call(cache, id); },
|
|
delete: function (id) {
|
|
var result;
|
|
if (!hasOwnProperty.call(cache, id)) return;
|
|
if (del) del(id);
|
|
result = cache[id];
|
|
delete cache[id];
|
|
if (deleteListeners) conf.emit("delete", id, result);
|
|
},
|
|
clear: function () {
|
|
var oldCache = cache;
|
|
if (clear) clear();
|
|
cache = create(null);
|
|
conf.emit("clear", oldCache);
|
|
},
|
|
on: function (type, listener) {
|
|
if (type === "get") getListeners = true;
|
|
else if (type === "set") setListeners = true;
|
|
else if (type === "delete") deleteListeners = true;
|
|
return on.call(this, type, listener);
|
|
},
|
|
emit: emit,
|
|
updateEnv: function () { original = conf.original; }
|
|
};
|
|
if (get) {
|
|
extDel = defineLength(function (arg) {
|
|
var id, args = arguments;
|
|
if (resolve) args = resolve(args);
|
|
id = get(args);
|
|
if (id === null) return;
|
|
conf.delete(id);
|
|
}, memLength);
|
|
} else if (length === 0) {
|
|
extDel = function () { return conf.delete("data"); };
|
|
} else {
|
|
extDel = function (arg) {
|
|
if (resolve) arg = resolve(arguments)[0];
|
|
return conf.delete(arg);
|
|
};
|
|
}
|
|
extGet = defineLength(function () {
|
|
var id, args = arguments;
|
|
if (length === 0) return cache.data;
|
|
if (resolve) args = resolve(args);
|
|
if (get) id = get(args);
|
|
else id = String(args[0]);
|
|
return cache[id];
|
|
});
|
|
extHas = defineLength(function () {
|
|
var id, args = arguments;
|
|
if (length === 0) return conf.has("data");
|
|
if (resolve) args = resolve(args);
|
|
if (get) id = get(args);
|
|
else id = String(args[0]);
|
|
if (id === null) return false;
|
|
return conf.has(id);
|
|
});
|
|
defineProperties(memoized, {
|
|
__memoized__: d(true),
|
|
delete: d(extDel),
|
|
clear: d(conf.clear),
|
|
_get: d(extGet),
|
|
_has: d(extHas)
|
|
});
|
|
return conf;
|
|
};
|