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.
182 lines
5.1 KiB
182 lines
5.1 KiB
/* 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; |
|
};
|
|
|