trackr-objects
Version:
Turns JavaScript objects and arrays into reactive resources for Trackr.
791 lines (693 loc) • 18 kB
JavaScript
'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;