UNPKG

trackr-objects

Version:

Turns JavaScript objects and arrays into reactive resources for Trackr.

791 lines (693 loc) 18 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var Trackr = _interopDefault(require('trackr')); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var has = function has(obj, key) { return Object.prototype.hasOwnProperty.call(obj, key); }; function trackProperty(obj, prop, value, options) { if ((typeof obj === "undefined" ? "undefined" : _typeof(obj)) !== "object" || obj == null) { throw new Error("Expecting object to define the reactive property on."); } if (typeof prop !== "string") throw new Error("Expecting string for property name."); var dep = new Trackr.Dependency(); Object.defineProperty(obj, prop, { configurable: options != null && Boolean(options.configurable), enumerable: options == null || options.enumerable !== false, set: function set(val) { if (val !== value) { value = val; dep.changed(); } return value; }, get: function get() { dep.depend(); return value; } }); return obj; } function trackProperties(obj, props, options) { if ((typeof obj === "undefined" ? "undefined" : _typeof(obj)) !== "object" || obj == null) { throw new Error("Expecting object of properties to define."); } if (Array.isArray(props)) { props.forEach(function (p) { return trackProperty(obj, p, options); }); return obj; } Object.keys(props).map(function (prop) { var opts = props[prop]; if ((typeof opts === "undefined" ? "undefined" : _typeof(opts)) !== "object" || opts == null || !has(opts, "value")) { throw new Error("Property '" + prop + "' was not set to an object with a 'value' key."); } return [prop, opts]; }).forEach(function (_ref) { var _ref2 = slicedToArray(_ref, 2); var prop = _ref2[0]; var opts = _ref2[1]; trackProperty(obj, prop, opts.value, _extends({}, options, opts.options)); }); return obj; } function hasOwn(o, k) { return Object.prototype.hasOwnProperty.call(o, k); } var Map = function () { function Map(obj) { classCallCheck(this, Map); this._keysDep = new Trackr.Dependency(); this._valuesDep = new Trackr.Dependency(); this._deps = {}; this._values = {}; if ((typeof obj === "undefined" ? "undefined" : _typeof(obj)) === "object" && obj != null) { for (var key in obj) { this.set(key, obj[key]); } } } createClass(Map, [{ key: "set", value: function set(key, value) { if ((typeof key === "undefined" ? "undefined" : _typeof(key)) === "object") { for (var k in key) { if (hasOwn(key, k)) this.set(k, key[k]); } return this; } var has = hasOwn(this._values, key); if (!has || this._values[key] !== value) { this._values[key] = value; this._getDep(key).changed(); if (!has) this._keysDep.changed(); this._valuesDep.changed(); } return this; } }, { key: "clear", value: function clear() { var keys = Object.keys(this._values); if (keys.length) { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = keys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var k = _step.value; delete this._values[k]; this._getDep(k).changed(); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } this._keysDep.changed(); this._valuesDep.changed(); } return this; } }, { key: "delete", value: function _delete(key) { if (hasOwn(this._values, key)) { delete this._values[key]; this._getDep(key).changed(); this._keysDep.changed(); this._valuesDep.changed(); } return this; } }, { key: "forEach", value: function forEach(fn, ctx) { var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = this.keys()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var k = _step2.value; fn.call(ctx, this.get(k), k, this); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } return this; } }, { key: "_getDep", value: function _getDep(k) { if (this._deps[k] == null) this._deps[k] = new Trackr.Dependency(); return this._deps[k]; } }, { key: "get", value: function get(key) { this._getDep(key).depend(); return this._values[key]; } }, { key: "has", value: function has(key) { this._getDep(key).depend(); return hasOwn(this._values, key); } }, { key: "keys", value: function keys() { this._keysDep.depend(); return Object.keys(this._values); } // TODO enable when ES6 settles // * entries() { // for (let k of this.keys()) { // yield [ k, this.get(k) ]; // } // } // // * values() { // for (let k of this.keys()) { // yield this.get(k); // } // } }, { key: "toJSON", value: function toJSON() { var out = {}; var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = this.keys()[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var k = _step3.value; out[k] = this.get(k); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } return out; } }, { key: "__trackr", get: function get() { return true; } }, { key: "__trackr_type", get: function get() { return "map"; } }, { key: "size", get: function get() { return this.keys().length; } }]); return Map; }(); // Map.prototype[Symbol.iterator] = Map.prototype.values; Map.isMap = function (o) { return o && o.__trackr && o.__trackr_type === "map"; }; var slice = Array.prototype.slice.call.bind(Array.prototype.slice); var arrayLike = function arrayLike(o) { return typeof o.length === "number" && o.length >= 0; }; function summariseSpliceOperation(array, args) { var index, addedItems, removedItems; if (!args) return null; // figure out where the changes started... index = +(args[0] < 0 ? array.length + args[0] : args[0]); // ...and how many items were added to or removed from the array addedItems = Math.max(0, args.length - 2); removedItems = args[1] !== undefined ? args[1] : array.length - index; // It's possible to do e.g. [ 1, 2, 3 ].splice( 2, 2 ) - i.e. the second argument // means removing more items from the end of the array than there are. In these // cases we need to curb JavaScript's enthusiasm or we'll get out of sync removedItems = Math.min(removedItems, array.length - index); return { index: index, added: addedItems, removed: removedItems }; } var List = function () { function List() { classCallCheck(this, List); this._itemDep = new Trackr.Dependency(); this._lengthDep = new Trackr.Dependency(); this._deps = {}; this._items = []; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = slice(arguments)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var a = _step.value; pushDynamic(this, a); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } createClass(List, [{ key: "get", value: function get(index) { this._getDep(index).depend(); return this._items[index]; } }, { key: "_getDep", value: function _getDep(k) { if (this._deps[k] == null) this._deps[k] = new Trackr.Dependency(); return this._deps[k]; } }, { key: "splice", value: function splice() { var args = slice(arguments); var op = summariseSpliceOperation(this._items, args); var r = this._items.splice.apply(this._items, args); this._itemDep.changed(); if (op.added !== op.removed) this._lengthDep.changed(); for (var i = 0; i < Math.max(op.added, op.removed); i++) { this._getDep(op.index + i).changed(); } return r; } }, { key: "push", value: function push() { var args = [this._items.length, 0].concat(slice(arguments)); this.splice.apply(this, args); return this._items.length; } }, { key: "pop", value: function pop() { return this.splice(this._items.length - 1, 1)[0]; } }, { key: "unshift", value: function unshift() { var args = [0, 0].concat(slice(arguments)); this.splice.apply(this, args); return this._items.length; } }, { key: "shift", value: function shift() { return this.splice(0, 1)[0]; } }, { key: "reverse", value: function reverse() { this._items.reverse(); this._itemDep.changed(); for (var i = 0; i < this._items.length; i++) { this._getDep(i).changed(); } return this; } }, { key: "sort", value: function sort(f) { this._items.sort(f); this._itemDep.changed(); for (var i = 0; i < this._items.length; i++) { this._getDep(i).changed(); } return this; } }, { key: "set", value: function set(index, value) { this.splice(index, 1, value); return this; } }, { key: "concat", value: function concat() { var l = new List(this); var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = slice(arguments)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var a = _step2.value; pushDynamic(l, a); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } return l; } }, { key: "join", value: function join(char) { var str = ""; var len = this.length; for (var v, i = 0; i < len; i++) { v = this.get(i); if (v == null) v = ""; if (typeof v !== "string") v = v.toString(); str += v + (i !== len - 1 ? char : ""); } return str; } }, { key: "slice", value: function slice(start, end) { var i = void 0, cloned = [], upTo = void 0, size = void 0, len = this._items.length; // Handle negative value for "start" if (typeof start === "number" && !isNaN(start)) { if (start < 0) { this._lengthDep.depend(); start = Math.max(0, len + start); } } else { start = 0; } // Handle negative value for "end" if (typeof end === "number" && !isNaN(end)) { upTo = Math.min(end, len); if (end < 0) { this._lengthDep.depend(); upTo = len + end; } } else { this._lengthDep.depend(); upTo = len; } // Actual expected size of the slice size = upTo - start; if (size > 0) { cloned = new Array(size); for (i = 0; i < size; i++) { cloned[i] = this.get(start + i); } } return cloned; } }, { key: "indexOf", value: function indexOf(member, start) { var len = this._items.length; if (typeof start === "number" && !isNaN(start)) { start = Math.min(Math.max(start, 0), len); } else { start = 0; } for (var i = start; i < len; i++) { if (this.get(i) === member) return i; } // only depend on length change if member wasn't found this._lengthDep.depend(); return -1; } }, { key: "forEach", value: function forEach(fn, ctx) { this._itemDep.depend(); for (var i = 0; i < this._items.length; i++) { fn.call(ctx, this._items[i], i, this); } return this; } }, { key: "reduce", value: function reduce(fn, value) { this.forEach(function (current, index, list) { value = fn.call(null, value, current, index, list); }); return value; } }, { key: "map", value: function map(fn, ctx) { return this.reduce(function (els) { els.push(fn.apply(ctx, slice(arguments, 1))); return els; }, []); } }, { key: "filter", value: function filter(fn, ctx) { return this.reduce(function (els, item) { if (fn.apply(ctx, slice(arguments, 1))) els.push(item); return els; }, []); } }, { key: "some", value: function some(fn, ctx) { var len = this._items.length; for (var i = 0; i < len; i++) { if (fn.call(ctx, this.get(i), i, this)) return true; } // only depend on length if none of the elements pass this._lengthDep.depend(); return false; } }, { key: "every", value: function every(fn, ctx) { var len = this._items.length; for (var i = 0; i < len; i++) { if (!fn.call(ctx, this.get(i), i, this)) return false; } // only depend on length if all of the elements pass this._lengthDep.depend(); return true; } // TODO enable when ES6 settles // * values() { // let len = this._items.length; // // for (let i = 0; i < len; i++) { // yield this.get(i); // } // // // depend on length if it gets to the end // this._lengthDep.depend(); // } }, { key: "toJSON", value: function toJSON() { return this.slice(0); } }, { key: "toArray", value: function toArray() { return this.slice(0); } }, { key: "__trackr", get: function get() { return true; } }, { key: "__trackr_type", get: function get() { return "list"; } }, { key: "length", get: function get() { this._lengthDep.depend(); return this._items.length; } }]); return List; }(); // List.prototype[Symbol.iterator] = List.prototype.values; List.isList = function (o) { return o && o.__trackr && o.__trackr_type === "list"; }; function pushDynamic(l, v) { if (arrayLike(v)) l.push.apply(l, slice(v));else if (List.isList(v)) v.forEach(function (i) { return l.push(i); });else l.push(v); } var Variable = function () { function Variable(value) { classCallCheck(this, Variable); this._value = void 0; this._dep = new Trackr.Dependency(); this.set(value); } createClass(Variable, [{ key: "set", value: function set(value) { if (value !== this._value) { this._value = value; this._dep.changed(); } } }, { key: "get", value: function get() { this._dep.depend(); return this._value; } }, { key: "toString", value: function toString() { return "" + this.get(); } }, { key: "toJSON", value: function toJSON() { return this.get(); } }, { key: "__trackr", get: function get() { return true; } }, { key: "__trackr_type", get: function get() { return "variable"; } }]); return Variable; }(); Variable.isVariable = function (o) { return o && o.__trackr && o.__trackr_type === "variable"; }; exports.List = List; exports.Map = Map; exports.Variable = Variable; exports.trackProperty = trackProperty; exports.trackProperties = trackProperties;