can-observable-array
Version:
Observable arrays
308 lines (252 loc) • 12.6 kB
JavaScript
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); }
function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
function _typeof(obj) { "@babel/helpers - typeof"; 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); }
var canReflect = require("can-reflect");
var computedHelpers = require("./computed-helpers");
var mapBindings = require("can-event-queue/map/map");
var ObservationRecorder = require("can-observation-recorder");
var _require = require("./helpers"),
assignNonEnumerable = _require.assignNonEnumerable,
convertItem = _require.convertItem,
dispatchIndexEvent = _require.dispatchIndexEvent,
shouldRecordObservationOnAllKeysExceptFunctionsOnProto = _require.shouldRecordObservationOnAllKeysExceptFunctionsOnProto;
var _require2 = require("can-observable-mixin"),
mixins = _require2.mixins;
var hasOwn = Object.prototype.hasOwnProperty;
var isSymbolLike = canReflect.isSymbolLike;
var metaSymbol = Symbol.for("can.meta");
var proxiedObjects = new WeakMap();
var proxies = new WeakSet();
var proxyKeys = Object.create(null);
Object.getOwnPropertySymbols(mapBindings).forEach(function (symbol) {
assignNonEnumerable(proxyKeys, symbol, mapBindings[symbol]);
});
computedHelpers.addKeyDependencies(proxyKeys);
var mutateMethods = {
"push": function push(arr, args) {
return [{
index: arr.length - args.length,
deleteCount: 0,
insert: args,
type: "splice"
}];
},
"pop": function pop(arr) {
return [{
index: arr.length,
deleteCount: 1,
insert: [],
type: "splice"
}];
},
"shift": function shift() {
return [{
index: 0,
deleteCount: 1,
insert: [],
type: "splice"
}];
},
"unshift": function unshift(arr, args) {
return [{
index: 0,
deleteCount: 0,
insert: args,
type: "splice"
}];
},
"splice": function splice(arr, args) {
return [{
index: args[0],
deleteCount: args[1],
insert: args.slice(2),
type: "splice"
}];
},
"sort": function sort(arr) {
// The array replaced everything.
return [{
index: 0,
deleteCount: arr.length,
insert: arr,
type: "splice"
}];
},
"reverse": function reverse(arr) {
// The array replaced everything.
return [{
index: 0,
deleteCount: arr.length,
insert: arr,
type: "splice"
}];
}
}; // Overwrite Array's methods that mutate to:
// - prevent other events from being fired off (index events and length events.)
// - dispatch patches events.
canReflect.eachKey(mutateMethods, function (makePatches, prop) {
var protoFn = Array.prototype[prop];
var mutateMethod = function mutateMethod() {
var meta = this[metaSymbol],
// Capture if this function should be making sideEffects
makeSideEffects = meta.preventSideEffects === 0,
oldLength = meta.target.length; // Prevent proxy from calling ObservationRecorder and sending events.
meta.preventSideEffects++; // Call the function -- note that *this* is the Proxy here, so
// accesses in the function still go through `get()` and `set()`.
var ret = protoFn.apply(meta.target, arguments);
var patches = makePatches(meta.target, Array.from(arguments), oldLength);
if (makeSideEffects === true) {
//!steal-remove-start
var reasonLog = [canReflect.getName(meta.proxy) + "." + prop + " called with", arguments]; //!steal-remove-end
var dispatchArgs = {
type: "length",
key: "length",
value: meta.target.length,
oldValue: oldLength,
patches: patches
}; //!steal-remove-start
if (process.env.NODE_ENV !== 'production') {
dispatchArgs.reasonLog = reasonLog;
} //!steal-remove-end
mapBindings.dispatch.call(meta.proxy, dispatchArgs, [meta.target.length, oldLength]);
}
meta.preventSideEffects--;
return ret;
}; //!steal-remove-start
if (process.env.NODE_ENV !== 'production') {
Object.defineProperty(mutateMethod, "name", {
value: prop
});
} //!steal-remove-end
// Store the proxied method so it will be used instead of the
// prototype method.
proxiedObjects.set(protoFn, mutateMethod);
proxies.add(mutateMethod);
});
function setValueAndOnChange(key, value, target, proxy, onChange) {
var old, change;
var hadOwn = hasOwn.call(target, key);
var descriptor = Object.getOwnPropertyDescriptor(target, key); // call the setter on the Proxy to properly do any side-effect sets (and run corresponding handlers)
// -- setters do not return values, so it is unnecessary to check for changes.
if (descriptor && descriptor.set) {
descriptor.set.call(proxy, value);
} else {
// otherwise check for a changed value
old = target[key];
change = old !== value;
if (change) {
var keyType = _typeof(key);
var keyIsString = keyType === "string"; // String keys added to the instance (and is not "length")
// Are newly defined properties and have propertyDefaults provided.
if (keyIsString && !(key in target)) {
mixins.expando(target, key, value);
} else {
// arr[0] = { foo: 'bar' } should convert to MyArray.items
if (keyType === "number") {
value = convertItem(target.constructor, value);
}
target[key] = value;
onChange(hadOwn, old);
}
}
}
}
var proxyHandlers = {
get: function get(target, key, receiver) {
if (isSymbolLike(key)) {
return target[key];
}
var proxy = proxiedObjects.get(target);
ObservationRecorder.add(proxy, key.toString());
var numberKey = !isSymbolLike(key) && +key;
if (Number.isInteger(numberKey)) {
ObservationRecorder.add(proxy, "length");
}
var value = Reflect.get(target, key, receiver);
return value;
},
set: function set(target, key, newValue, receiver) {
var proxy = proxiedObjects.get(target);
var numberKey = !isSymbolLike(key) && +key;
if (Number.isInteger(numberKey)) {
key = numberKey;
}
setValueAndOnChange(key, newValue, target, proxy, function onChange(hadOwn, oldValue) {
if (Number.isInteger(key)) {
dispatchIndexEvent.call(receiver, key, hadOwn ? typeof newValue !== 'undefined' ? "set" : "remove" : "add", newValue, oldValue);
}
});
return true;
},
deleteProperty: function deleteProperty(target, key) {
var old = this.target[key];
var deleteSuccessful = delete this.target[key]; // Fire event handlers if we were able to delete and the value changed.
if (deleteSuccessful && this.preventSideEffects === 0 && old !== undefined) {
dispatchIndexEvent.call(this.proxy, key, "remove", undefined, old);
}
return deleteSuccessful;
},
ownKeys: function ownKeys() {
ObservationRecorder.add(this.proxy, "can.keys");
var keysSet = new Set(Object.getOwnPropertyNames(this.target).concat(Object.getOwnPropertySymbols(this.target)).concat(Object.getOwnPropertySymbols(this.proxyKeys)));
return Array.from(keysSet);
}
};
function makeObservable(array, options) {
var meta = {
target: array,
proxyKeys: options.proxyKeys !== undefined ? options.proxyKeys : Object.create(proxyKeys),
computedKeys: Object.create(null),
options: options,
// `preventSideEffects` is a counter used to "turn off" the proxy. This is incremented when some
// function (like `Array.splice`) wants to handle event dispatching and/or calling
// `ObservationRecorder` itself for performance reasons.
preventSideEffects: 0
};
meta.proxyKeys[metaSymbol] = meta;
meta.proxy = new Proxy(array, {
get: proxyHandlers.get.bind(meta),
set: proxyHandlers.set.bind(meta),
ownKeys: proxyHandlers.ownKeys.bind(meta),
deleteProperty: proxyHandlers.deleteProperty.bind(meta),
meta: meta
});
mapBindings.addHandlers(meta.proxy, meta);
return meta.proxy;
}
function proxyArray() {
return /*#__PURE__*/function (_Array) {
_inherits(ProxyArray, _Array);
var _super = _createSuper(ProxyArray);
function ProxyArray() {
var _this;
_classCallCheck(this, ProxyArray);
for (var _len = arguments.length, items = new Array(_len), _key = 0; _key < _len; _key++) {
items[_key] = arguments[_key];
}
_this = _super.call.apply(_super, [this].concat(items));
var localProxyKeys = Object.create(proxyKeys);
localProxyKeys.constructor = _this.constructor;
var observable = makeObservable(_assertThisInitialized(_this), {
//observe: makeObserve.observe,
proxyKeys: localProxyKeys,
shouldRecordObservation: shouldRecordObservationOnAllKeysExceptFunctionsOnProto
});
proxiedObjects.set(_assertThisInitialized(_this), observable);
proxies.add(observable);
return _possibleConstructorReturn(_this, observable);
}
return ProxyArray;
}( /*#__PURE__*/_wrapNativeSuper(Array));
}
module.exports = proxyArray;
;