UNPKG

can-observable-array

Version:
308 lines (252 loc) 12.6 kB
"use strict"; 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;