vivisector
Advanced tools
Comparing version 1.1.2 to 1.1.3
@@ -1,22 +0,83 @@ | ||
/** | ||
* @param {Function} fn The callback to be executed after the timeout is lifted. | ||
* @param {Number} ms A number denoting milliseconds until timeout is lifted. | ||
* @summary Debounces a function call by `ms` milliseconds. | ||
*/ | ||
function _typeof(obj) { | ||
"@babel/helpers - typeof"; | ||
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { | ||
_typeof = function (obj) { | ||
return typeof obj; | ||
}; | ||
} else { | ||
_typeof = function (obj) { | ||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; | ||
}; | ||
} | ||
return _typeof(obj); | ||
} | ||
function _slicedToArray(arr, i) { | ||
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); | ||
} | ||
function _arrayWithHoles(arr) { | ||
if (Array.isArray(arr)) return arr; | ||
} | ||
function _iterableToArrayLimit(arr, i) { | ||
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; | ||
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"] != null) _i["return"](); | ||
} finally { | ||
if (_d) throw _e; | ||
} | ||
} | ||
return _arr; | ||
} | ||
function _unsupportedIterableToArray(o, minLen) { | ||
if (!o) return; | ||
if (typeof o === "string") return _arrayLikeToArray(o, minLen); | ||
var n = Object.prototype.toString.call(o).slice(8, -1); | ||
if (n === "Object" && o.constructor) n = o.constructor.name; | ||
if (n === "Map" || n === "Set") return Array.from(o); | ||
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); | ||
} | ||
function _arrayLikeToArray(arr, len) { | ||
if (len == null || len > arr.length) len = arr.length; | ||
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | ||
return arr2; | ||
} | ||
function _nonIterableRest() { | ||
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); | ||
} | ||
function debounce(fn, ms) { | ||
let timeout; | ||
return args => { | ||
var timeout; | ||
return function (args) { | ||
clearTimeout(timeout); | ||
timeout = setTimeout(() => fn(args), ms); | ||
timeout = setTimeout(function () { | ||
return fn(args); | ||
}, ms); | ||
}; | ||
} | ||
/** | ||
* @param {Function} fn The function to be granted a computed name property. | ||
* @param {String} name The value of prop `name`. | ||
* @summary Accepts as input a function and assigns to it a computed name property of value `name`. | ||
* @returns {Function} The passed function, now accompanied by a computed name property. | ||
*/ | ||
function computeNamedFunction(fn, name) { | ||
@@ -29,11 +90,4 @@ Object.defineProperty(fn, 'name', { | ||
} | ||
/** | ||
* @param {Object} context The Object instance on which to define prop `addEventListener`. | ||
* @param {Object} handlers The provided instance's handlers store. | ||
* @summary Defines `addEventListener` property. | ||
*/ | ||
function defineAddEventListener(context, handlers) { | ||
// override addEventListener method of given array | ||
Object.defineProperty(context, 'addEventListener', { | ||
@@ -43,7 +97,4 @@ configurable: false, | ||
writable: false, | ||
// false; we do not want further tampering here | ||
value: function (eventName, handler, ms) { | ||
// sanitize and validate handler submissions | ||
// simple type-check: concatenate an empty string to coerce `eventName` | ||
eventName = ('' + eventName).toLowerCase(); // ensure registered event's name corresponds to one of the presets in `_handlers` | ||
value: function value(eventName, handler, ms) { | ||
eventName = ('' + eventName).toLowerCase(); | ||
@@ -59,10 +110,8 @@ if (!(eventName in handlers)) { | ||
if (ms) { | ||
const machinedProperty = handler.name; | ||
var machinedProperty = handler.name; | ||
handler = debounce(handler, ms); | ||
handler = computeNamedFunction(handler, machinedProperty); | ||
} // add handler to respective event store Array | ||
} | ||
handlers[eventName].push(handler); // explicitly return `context` to allow method chaining across consistent parent Object / execution context | ||
handlers[eventName].push(handler); | ||
return context; | ||
@@ -72,11 +121,4 @@ } | ||
} | ||
/** | ||
* @param {Object} context The Object instance on which to define prop `removeEventListener`. | ||
* @param {Object} handlers The provided instance's handlers store. | ||
* @summary Defines `removeEventListener` property. | ||
*/ | ||
function defineRemoveEventListener(context, handlers) { | ||
// override removeEventListener method of given array | ||
Object.defineProperty(context, 'removeEventListener', { | ||
@@ -86,3 +128,3 @@ configurable: false, | ||
writable: false, | ||
value: function (eventName, handler) { | ||
value: function value(eventName, handler) { | ||
eventName = ('' + eventName).toLowerCase(); | ||
@@ -96,19 +138,13 @@ | ||
throw new Error('Error: Invalid handler.'); | ||
} // reference all handlers of given `eventName` | ||
} | ||
var handlerSet = handlers[eventName]; | ||
var handlerSetLen = handlerSet.length; | ||
const handlerSet = handlers[eventName]; | ||
let handlerSetLen = handlerSet.length; // ensure handler exists, lookup, remove | ||
while (--handlerSetLen >= 0) { | ||
// we eval via the handler's name property given some handlers will have been recomputed with the | ||
// `debounce` wrapper; by-value comparison is included to handle anon fns. | ||
if (handlerSet[handlerSetLen] === handler || handlerSet[handlerSetLen].name === handler.name) { | ||
// handler exists, remove | ||
// NOTE: w/out explicit `return`, this loop will continue removing *all* matched handlers | ||
handlerSet.splice(handlerSetLen, 1); | ||
} | ||
} // explicitly return `context` to allow method chaining across consistent parent Object / execution context | ||
} | ||
return context; | ||
@@ -118,12 +154,5 @@ } | ||
} | ||
/** | ||
* @param {Object} event An Object containing event-contingent data. | ||
* @param {Object} context The `this` value on which to call each instance. | ||
* @param {Object} handlers The provided instance's handlers store. | ||
* @summary Systematically calls each handler of a given event type. | ||
*/ | ||
function raiseEvent(event, context, handlers) { | ||
handlers[event.type].forEach(handler => { | ||
handlers[event.type].forEach(function (handler) { | ||
handler.call(context, event); | ||
@@ -133,17 +162,7 @@ }); | ||
/** | ||
* @override | ||
* @summary Factory for instantiating observable Array-like objects. | ||
* @description Copies array into Array-like object and hijacks specific instance's base prototype, | ||
* thus creating an observable of type Array. Provides custom handlers for | ||
* Array methods `push`, `pop`, `shift`, `unshift`, `splice`. `length`, | ||
* Makes available custom index accessors provided the ObservableArray has been mutated by way | ||
* of aforementioned methods, `length`, or the `value` accessor, which is common to all `Observables`. | ||
* @augments Array | ||
*/ | ||
function ObservableArray(items) { | ||
var _this = this; | ||
function ObservableArray(items) { | ||
// `this` is used throughout as a mutable constructor | ||
const _self = this, | ||
_handlers = { | ||
var _self = this, | ||
_handlers = { | ||
itemadded: [], | ||
@@ -153,20 +172,15 @@ itemremoved: [], | ||
mutated: [] | ||
}; // internal value store | ||
}; | ||
var _array = []; | ||
let _array = []; | ||
/** | ||
* @summary Dynamically defines index accessors. | ||
*/ | ||
const defineIndexProperty = index => { | ||
var defineIndexProperty = function defineIndexProperty(index) { | ||
if (!(index in _self)) { | ||
Object.defineProperty(_self, index, { | ||
configurable: true, | ||
// type of descriptor may be changed / deleted from corresponding obj | ||
enumerable: true, | ||
// enumerate to `true` so as to expose item indices | ||
get: () => _array[index], | ||
set: inboundItem => { | ||
// set actual item to provided index | ||
get: function get() { | ||
return _array[index]; | ||
}, | ||
set: function set(inboundItem) { | ||
_array[index] = inboundItem; | ||
@@ -182,12 +196,10 @@ raiseEvent({ | ||
}; | ||
/** | ||
* @summary Defines accessor for actual array value (core val). | ||
*/ | ||
Object.defineProperty(_self, 'value', { | ||
configurable: false, | ||
enumerable: false, | ||
get: () => _array, | ||
set: inboundItems => { | ||
get: function get() { | ||
return _array; | ||
}, | ||
set: function set(inboundItems) { | ||
if (inboundItems instanceof Array) { | ||
@@ -203,10 +215,2 @@ _array = inboundItems; | ||
}); | ||
/* Custom Methods */ | ||
/** | ||
* @param {Any} value The value to be matched against core arr items. | ||
* @summary Returns an Array of all indices that contain a match to given argument. Does not evaluate nested items. | ||
* @returns {Array} A multi-dimensional array of matched indices. | ||
*/ | ||
Object.defineProperty(_self, 'findIndexAll', { | ||
@@ -216,7 +220,7 @@ configurable: false, | ||
writable: false, | ||
value: value => { | ||
const indices = []; | ||
value: function value(_value) { | ||
var indices = []; | ||
_array.forEach((item, index) => { | ||
if (item === value) { | ||
_array.forEach(function (item, index) { | ||
if (item === _value) { | ||
indices.push(index); | ||
@@ -229,8 +233,2 @@ } | ||
}); | ||
/** | ||
* @param {Any} value The value to be matched against core arr items. | ||
* @summary Returns an Array of all indices that contain a match to given argument. Walks entire Array tree and evaluates nested items. | ||
* @returns {Array} A multi-dimensional array of matched indices. | ||
*/ | ||
Object.defineProperty(_self, 'findIndexAllDeep', { | ||
@@ -240,34 +238,20 @@ configurable: false, | ||
writable: false, | ||
value: value => { | ||
const indices = []; // path will be an Array with the current level therein | ||
value: function value(_value2) { | ||
var indices = []; | ||
this.some(function walk(path) { | ||
// will keep 'matching' indices until it hits the actual value; creates multi-dimensional Array | ||
_this.some(function walk(path) { | ||
return function (item, i) { | ||
// while item is a match, continue pushing corresponding indices into Array `indices` | ||
if (item === value) { | ||
if (item === _value2) { | ||
indices.push(path.concat(i)); | ||
} // else, ensure `item` is an Array (indicating another level to walk) | ||
// recurse and call `walk`; `some` implicitly passes `this` aka `item` as the first argument | ||
} | ||
return Array.isArray(item) && item.some(walk(path.concat(i))); | ||
}; | ||
}([])); | ||
/* we initialize as an IIFE and pass an empty Array as our default param*/ | ||
// return multi-dimensional Array mapping of matched indices | ||
return indices; | ||
} | ||
}); // Note: we could later add something like `JSON.stringify(_array[i]).indexOf(value) > -1` to eval whether or not | ||
// the value exists and we should continue recursing the tree | ||
// define props for event-binding | ||
}); | ||
defineAddEventListener(_self, _handlers); | ||
defineRemoveEventListener(_self, _handlers); | ||
/** | ||
* @summary Defines event-bound `push` method. | ||
* @override `Array.prototype.push` | ||
*/ | ||
Object.defineProperty(_self, 'push', { | ||
@@ -277,17 +261,19 @@ configurable: false, | ||
writable: false, | ||
value: (...args) => { | ||
let index; // for each provided element, push into copy `_array` | ||
value: function value() { | ||
var index; | ||
args.forEach(item => { | ||
// persist index at which item will be added | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
args.forEach(function (item) { | ||
index = _array.length; | ||
_array.push(item); // define index accessor for each element | ||
_array.push(item); | ||
defineIndexProperty(index); | ||
raiseEvent({ | ||
type: 'itemadded', | ||
index, | ||
item | ||
index: index, | ||
item: item | ||
}, _self, _handlers); | ||
@@ -298,7 +284,2 @@ }); | ||
}); | ||
/** | ||
* @summary Defines event-bound `pop` method. | ||
* @override `Array.prototype.pop` | ||
*/ | ||
Object.defineProperty(_self, 'pop', { | ||
@@ -308,6 +289,6 @@ configurable: false, | ||
writable: false, | ||
value: () => { | ||
value: function value() { | ||
if (_array.length > 0) { | ||
const index = _array.length - 1, | ||
item = _array.pop(); | ||
var index = _array.length - 1, | ||
item = _array.pop(); | ||
@@ -317,4 +298,4 @@ delete _self[index]; | ||
type: 'itemremoved', | ||
index, | ||
item | ||
index: index, | ||
item: item | ||
}, _self, _handlers); | ||
@@ -325,7 +306,2 @@ return item; | ||
}); | ||
/** | ||
* @summary Defines event-bound `unshift` method. | ||
* @override `Array.prototype.unshift` | ||
*/ | ||
Object.defineProperty(_self, 'unshift', { | ||
@@ -335,7 +311,8 @@ configurable: false, | ||
writable: false, | ||
value: (...args) => { | ||
// NOTE this is one of those rare instances where we *need* `var`, lest the next loop's `i` be undefined due to | ||
// scoping behaviors of `let` | ||
args.forEach((item, index) => { | ||
// add arg to beginning of core val | ||
value: function value() { | ||
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
args[_key2] = arguments[_key2]; | ||
} | ||
args.forEach(function (item, index) { | ||
_array.splice(index, 0, item); | ||
@@ -346,13 +323,13 @@ | ||
type: 'itemadded', | ||
index, | ||
item | ||
index: index, | ||
item: item | ||
}, _self, _handlers); | ||
}); | ||
_array.forEach((item, index) => { | ||
_array.forEach(function (item, index) { | ||
if (index >= args.length) { | ||
raiseEvent({ | ||
type: 'itemset', | ||
index, | ||
item | ||
index: index, | ||
item: item | ||
}, _self, _handlers); | ||
@@ -365,7 +342,2 @@ } | ||
}); | ||
/** | ||
* @summary Defines event-bound `shift` method. | ||
* @override `Array.prototype.shift` | ||
*/ | ||
Object.defineProperty(_self, 'shift', { | ||
@@ -375,9 +347,6 @@ configurable: false, | ||
writable: false, | ||
value: () => { | ||
// only actionable if Array contains elements | ||
value: function value() { | ||
if (_array.length > 0) { | ||
const item = _array.shift(); // NOTE imperative; `shift` will not persist this change; | ||
// we simulate this behavior by deleting value at index `_array.length` | ||
var item = _array.shift(); | ||
delete _self[_array.length]; | ||
@@ -387,3 +356,3 @@ raiseEvent({ | ||
index: 0, | ||
item | ||
item: item | ||
}, _self, _handlers); | ||
@@ -394,7 +363,2 @@ return item; | ||
}); | ||
/** | ||
* @summary Defines event-bound `splice` method. | ||
* @override `Array.prototype.splice` | ||
*/ | ||
Object.defineProperty(_self, 'splice', { | ||
@@ -404,31 +368,28 @@ configurable: false, | ||
writable: false, | ||
value: (...args) => { | ||
// removed items store | ||
const removed = []; | ||
let [index, numToRemove, ...itemsToAdd] = args, | ||
// explicitly hoist to outer context | ||
item; // calculate index qua splice parameters | ||
// we need `==` so as to coerce possible `undefined` to null during eval | ||
// this is also where we simulate `splice's` capacity to accept negative indices and walk recessively from core val length | ||
value: function value() { | ||
var removed = []; | ||
index = index == null ? 0 : index < 0 ? _array.length + index : index; // calculate number of elements qua splice parameters | ||
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { | ||
args[_key3] = arguments[_key3]; | ||
} | ||
var index = args[0], | ||
numToRemove = args[1], | ||
itemsToAdd = args.slice(2), | ||
item; | ||
index = index == null ? 0 : index < 0 ? _array.length + index : index; | ||
numToRemove = numToRemove == null ? _array.length - index : numToRemove > 0 ? numToRemove : 0; | ||
while (numToRemove--) { | ||
// we specify the 0th index to coerce to val as opposed to an array with val therein | ||
item = _array.splice(index, 1)[0]; | ||
removed.push(item); // reflect changes in constructor arr | ||
removed.push(item); | ||
delete _self[_array.length]; | ||
raiseEvent({ | ||
type: 'itemremoved', | ||
// index begins at first removal; increment by ea. removed item | ||
index: index + removed.length - 1, | ||
item | ||
item: item | ||
}, _self, _handlers); | ||
} | ||
itemsToAdd.forEach(item => { | ||
// no need to update _self as `splice` interceptor will do this for us | ||
itemsToAdd.forEach(function (item) { | ||
_array.splice(index, 0, item); | ||
@@ -439,6 +400,5 @@ | ||
type: 'itemadded', | ||
index, | ||
item | ||
}, _self, _handlers); // explicitly increment index on ea. iteration | ||
index: index, | ||
item: item | ||
}, _self, _handlers); | ||
index++; | ||
@@ -449,41 +409,26 @@ }); | ||
}); | ||
/** | ||
* @summary Defines event-bound `length` method. | ||
* @override `Array.prototype.length` | ||
*/ | ||
Object.defineProperty(_self, 'length', { | ||
configurable: false, | ||
enumerable: false, | ||
get: () => _array.length, | ||
set: value => { | ||
// coerce input to Number | ||
const symbolicLength = Number(value); // est core val length | ||
get: function get() { | ||
return _array.length; | ||
}, | ||
set: function set(value) { | ||
var symbolicLength = Number(value); | ||
var length = _array.length; | ||
const length = _array.length; // user-specified length must be whole number | ||
if (symbolicLength % 1 === 0 && symbolicLength >= 0) { | ||
// if len provided less than that of current arr value, we truncate the core val w/`splice` | ||
if (symbolicLength < length) { | ||
_self.splice(symbolicLength); | ||
} // user is extending core val; push new generated Array of specified len sans core len | ||
else if (symbolicLength > length) { | ||
} else if (symbolicLength > length) { | ||
Reflect.apply(_self.push, _self, new Array(symbolicLength - length)); | ||
} | ||
} // catch edge cases | ||
else { | ||
} else { | ||
throw new RangeError('Error: Invalid array length.'); | ||
} // finally, we extend actual core | ||
} | ||
_array.length = symbolicLength; | ||
} | ||
}); | ||
/** | ||
* @summary Process prototype for instance to ensure we extend undeclared Array methods. | ||
* @override `Array.prototype` | ||
*/ | ||
Object.getOwnPropertyNames(Array.prototype).forEach(name => { | ||
// ensure prop isn't already allocated so as to avoid collisions | ||
Object.getOwnPropertyNames(Array.prototype).forEach(function (name) { | ||
if (!(name in _self)) { | ||
@@ -497,6 +442,5 @@ Object.defineProperty(_self, name, { | ||
} | ||
}); // allocate inputs | ||
}); | ||
if (items instanceof Array) { | ||
// coerces _self to a container Array into which we copy `items` | ||
Reflect.apply(_self.push, _self, items); | ||
@@ -506,17 +450,5 @@ } | ||
/** | ||
* @override | ||
* @readonly | ||
* @summary Factory for instantiating observable String-like objects. | ||
* @description Copies given string input into new String object; proceeds to assign props for adding/removing | ||
* event-listeners, adds custom `reassign` method and accessors, assimilates extended String prototype props. | ||
* Extended String prototype methods are conformed as computed properties that are called on the primitive String value | ||
* contained by the `ObservableString`, and not the `ObservableString` itself. | ||
* Registers as events `mutated`. | ||
* @augments String | ||
*/ | ||
function ObservableString(value) { | ||
const _self = this, | ||
_handlers = { | ||
var _self = this, | ||
_handlers = { | ||
mutated: [] | ||
@@ -526,40 +458,19 @@ }; | ||
if (typeof value === 'string') { | ||
/* | ||
The actual primitive value is stored in String Object. | ||
Unlike the `ObservableArray`, we don't need to copy the value to an internal, nested prop given the limited scope of operations | ||
to be performed on a primitive i.e. the String versus an Object such as an Array | ||
*/ | ||
_self[0] = String(value); | ||
} | ||
/** | ||
* @param {Object} coreObject Given Object to be mutated | ||
* @param {String} coreValue User-provided value to which the internal/core val will be mutated | ||
* @summary Supplants core value with that provided by user. | ||
* @returns {Object} Comprised of the previous core value `value` and the new core value `mutant`. | ||
*/ | ||
const mutateCoreValue = (coreObject, coreValue) => { | ||
// persist pre-mutated value (note, not the String Object but the value therein) | ||
const value = coreObject[0].valueOf(); | ||
coreObject[0] = String(coreValue); // get current mutated/new String value | ||
const mutant = coreObject[0].valueOf(); // execute callback; | ||
var mutateCoreValue = function mutateCoreValue(coreObject, coreValue) { | ||
var value = coreObject[0].valueOf(); | ||
coreObject[0] = String(coreValue); | ||
var mutant = coreObject[0].valueOf(); | ||
return { | ||
value, | ||
mutant | ||
value: value, | ||
mutant: mutant | ||
}; | ||
}; | ||
/** | ||
* @summary Defines accessor for String primitive / core value. | ||
*/ | ||
Object.defineProperty(_self, 'value', { | ||
configurable: false, | ||
enumerable: false, | ||
get() { | ||
// if `undefined`, we implicitly return `undefined` and mitigate the exception that would be thrown by calling `valueOf` thereon | ||
get: function get() { | ||
if (_self[0]) { | ||
@@ -571,10 +482,4 @@ return _self[0].valueOf(); | ||
}, | ||
set(arg) { | ||
set: function set(arg) { | ||
if (typeof arg === 'string') { | ||
/* | ||
By using `Object.assign`, we can create, in-line, a new Object consisting of a key/value pair to denote our desired event type. | ||
`assign` will 'merge' the in-line Object with that returned from `mutateCoreValue`. | ||
Given `raiseEvent` accepts as input an Object, we can pass this expression directly; it will resolve to the necessary values. | ||
*/ | ||
raiseEvent(Object.assign({ | ||
@@ -585,8 +490,3 @@ type: 'mutated' | ||
} | ||
}); | ||
/** | ||
* @summary Defines custom method `reassign`. | ||
*/ | ||
Object.defineProperty(_self, 'reassign', { | ||
@@ -596,39 +496,15 @@ configurable: false, | ||
writable: false, | ||
/** | ||
* @param {String|Any} candidate User provided value to which the Observable will be reassigned (provided it is a String). | ||
* @summary Supplants internal value with `candidate` and broadcasts 'mutated' event. | ||
* @returns {Object} The Observable instance, returned so as to allow method chaining. | ||
*/ | ||
value(candidate) { | ||
// type-check and validations go here | ||
value: function value(candidate) { | ||
if (!(typeof candidate === 'string')) { | ||
throw new Error('Error: Invalid type.'); | ||
} | ||
/* | ||
_self.value = stringInput; | ||
Mutating the `value` prop here as demonstrated in the above-commented code *would* fire all `mutated` events given the | ||
execution context of prop `value` is recursively applicable. However, we'd like to maintain as great a degree and | ||
granularity of control as possible. As such, we do *not* use the `value` accessor here, instead opting to raise | ||
the `mutated` event in a discrete context. | ||
*/ | ||
raiseEvent(Object.assign({ | ||
type: 'mutated' | ||
}, mutateCoreValue(_self, candidate)), _self, _handlers); // return `_self` to allow method to be chainable; see other like-comments | ||
}, mutateCoreValue(_self, candidate)), _self, _handlers); | ||
return _self; | ||
} | ||
}); // define props for event-binding | ||
}); | ||
defineAddEventListener(_self, _handlers); | ||
defineRemoveEventListener(_self, _handlers); | ||
/** | ||
* @override | ||
* @summary Define event-bound `split` method. | ||
*/ | ||
Object.defineProperty(_self, 'split', { | ||
@@ -638,45 +514,20 @@ configurable: false, | ||
writable: false, | ||
value: function value(delimiter) { | ||
var currentStringVal = _self[0].valueOf(); | ||
value(delimiter) { | ||
// get current String value | ||
const currentStringVal = _self[0].valueOf(); // split current with provided argument `delimiter` | ||
const splitArr = currentStringVal.split(delimiter); // non-mutating, no need to raise event; return | ||
var splitArr = currentStringVal.split(delimiter); | ||
return splitArr; | ||
} | ||
}); | ||
/** | ||
* @override | ||
* @summary Define event-bound `length` method / accessor. | ||
*/ | ||
Object.defineProperty(_self, 'length', { | ||
configurable: false, | ||
enumerable: false, | ||
get() { | ||
get: function get() { | ||
return _self[0].length; | ||
}, | ||
// use noop here lest babel 7 complain | ||
set() { | ||
// essentially the 'only' noop in JavaScript (save for an empty function expression) | ||
// note we do not use an empty arrow func for linting/consistency purposes | ||
set: function set() { | ||
Function.prototype(); | ||
} | ||
}); | ||
/** | ||
* @override | ||
* @summary Extend `String` prototype as computed values. | ||
* @description Here, we extend onto `ObservableString` all String prototype methods not already extant | ||
* and, for each, conform get/set to the execution context of the primitive value contained therein. | ||
* @extends String | ||
*/ | ||
Object.getOwnPropertyNames(String.prototype).forEach(name => { | ||
// ensure prop isn't already allocated so as to avoid collisions | ||
Object.getOwnPropertyNames(String.prototype).forEach(function (name) { | ||
if (!(name in _self)) { | ||
@@ -687,12 +538,7 @@ Object.defineProperty(_self, name, { | ||
writable: false, | ||
value: function value() { | ||
var _self$; | ||
/** | ||
* @description Here, we intercept the getter/setter conformation of each method on String's prototype | ||
* We do this so as to set the execution context to point to the primitive String and *not* its parent Object, | ||
* in this case `ObservableString` | ||
*/ | ||
value(...args) { | ||
return _self[0][name](...args); | ||
return (_self$ = _self[0])[name].apply(_self$, arguments); | ||
} | ||
}); | ||
@@ -703,18 +549,8 @@ } | ||
/* eslint-disable no-unused-vars */ | ||
/** | ||
* @param {Object} obj Object whose value will be copied and proxied. | ||
* @summary Factory for instantiating proxied, observable Objects. | ||
* @description Copies Object and proxifies; configures necessary traps and broadcasts events upon the access thereof. | ||
* Recurses the target Object so as to enforce traps on nested props/accessors. | ||
* @augments Proxy | ||
*/ | ||
function ObservableObject(obj) { | ||
/* Configuration Artifacts */ | ||
const _symbols = { | ||
__INTERNAL__: Symbol.for('_internal'), | ||
__ISPROXY__: Symbol.for('_isProxy') | ||
var _symbols = { | ||
__INTERNAL__: Symbol["for"]('_internal'), | ||
__ISPROXY__: Symbol["for"]('_isProxy') | ||
}, | ||
_handlers = { | ||
_handlers = { | ||
itemget: [], | ||
@@ -724,48 +560,32 @@ itemdeleted: [], | ||
}, | ||
// non-enumerable prop store - mitigate get|set events from being called thereon | ||
_internals = ['addEventListener', 'removeEventListener', 'identifier', 'type']; | ||
/** | ||
* @param {String} prop The property presently being accessed. | ||
* @summary Intercepts attempts to set|delete non-configurable properties. | ||
* @throws {Error} Raises exception if property `prop` is present in `_internals`. | ||
*/ | ||
_internals = ['addEventListener', 'removeEventListener', 'identifier', 'type']; | ||
const enforceConfigurableProps = prop => { | ||
var enforceConfigurableProps = function enforceConfigurableProps(prop) { | ||
if (_internals.includes(prop)) { | ||
throw new Error(`Error: Property '${prop}' is non-configurable.`); | ||
throw new Error("Error: Property '".concat(prop, "' is non-configurable.")); | ||
} | ||
}; | ||
/** | ||
* @summary Root Proxy handler; injects event broadcasts into get|set|delete traps. | ||
*/ | ||
const _rootHandler = { | ||
get(target, prop, recv) { | ||
// type-check mechanism for testing | ||
var _rootHandler = { | ||
get: function get(target, prop, recv) { | ||
if (prop === _symbols.__ISPROXY__) { | ||
return true; | ||
} // conform to `value` accessor and return raw value | ||
} | ||
if (prop === _symbols.__INTERNAL__) { | ||
return target; | ||
} // we use `Reflect` here as a simple mitigative effort to avoid violating Proxy invariants, as described in the specification here: | ||
// https://www.ecma-international.org/ecma-262/8.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver | ||
} | ||
var value = Reflect.get(target, prop, recv); | ||
const value = Reflect.get(target, prop, recv); // recurse and continue chain of Proxies for nested props to ensure traps are executed upon access thereof | ||
if (typeof value === 'object') { | ||
if (_typeof(value) === 'object') { | ||
return new Proxy(value, _rootHandler); | ||
} // tradeoff here is `raiseEvent` won't be called for root getters; else, the cb would fire for each branch | ||
// TODO fix this by caching the Object with a WeakMap or something and walking the tree on ea. `get`...I think this would work | ||
} | ||
if (value && !_internals.includes(prop)) { | ||
raiseEvent({ | ||
type: 'itemget', | ||
prop, | ||
target, | ||
value | ||
prop: prop, | ||
target: target, | ||
value: value | ||
}, this, _handlers); | ||
@@ -776,59 +596,41 @@ } | ||
}, | ||
set(target, prop, value, recv) { | ||
set: function set(target, prop, value, recv) { | ||
enforceConfigurableProps(prop); | ||
raiseEvent({ | ||
type: 'itemset', | ||
prop, | ||
target, | ||
value | ||
prop: prop, | ||
target: target, | ||
value: value | ||
}, this, _handlers); | ||
return Reflect.set(target, prop, value); | ||
}, | ||
deleteProperty(target, prop) { | ||
enforceConfigurableProps(prop); // `toString` returns a default value when argument is type Object, ergo, we use `JSON.stringify` here | ||
// see: https://es5.github.io/#x9.8 | ||
const ephemeralTarget = JSON.stringify(target, null, 0); | ||
const value = Reflect.deleteProperty(target, prop); | ||
deleteProperty: function deleteProperty(target, prop) { | ||
enforceConfigurableProps(prop); | ||
var ephemeralTarget = JSON.stringify(target, null, 0); | ||
var value = Reflect.deleteProperty(target, prop); | ||
raiseEvent({ | ||
type: 'itemdeleted', | ||
prop, | ||
prop: prop, | ||
target: ephemeralTarget, | ||
value | ||
value: value | ||
}, this, _handlers); | ||
return value; | ||
} | ||
}; | ||
/* Root Object Conformations */ | ||
// initialize root Proxy | ||
if (!(obj instanceof Object)) { | ||
return; | ||
} // make a deep clone here so as to break the chain of reference to the provided argument `obj` | ||
// if we pass `obj` directly, the Proxy will modify the original target | ||
} | ||
var _self = new Proxy(JSON.parse(JSON.stringify(obj)), _rootHandler); | ||
const _self = new Proxy(JSON.parse(JSON.stringify(obj)), _rootHandler); | ||
/** | ||
* @summary Defines accessor for core Object value, decoupled from original target ref. | ||
*/ | ||
Object.defineProperty(_self, 'value', { | ||
configurable: false, | ||
enumerable: false, | ||
get() { | ||
// `assign` here to decouple execution context from returned val | ||
// this allows users to, at any time, copy internal value without Proxy properties | ||
get: function get() { | ||
return Object.assign({}, _self[_symbols.__INTERNAL__]); | ||
}, | ||
set(inboundItems) { | ||
set: function set(inboundItems) { | ||
return; | ||
} | ||
}); | ||
@@ -840,47 +642,15 @@ defineAddEventListener(_self, _handlers); | ||
/* eslint-disable no-unused-vars */ | ||
/** | ||
* @summary A factory / wrapper for exporting the Vivisector.js `Observables` and their associated properties. | ||
* @description Exposes various JavaScript datatypes and primitives and extends them with both event-driven | ||
* properties (qua the `Observable`'s execution context), and ubiquitous Vivisector-contingent properties (qua the macro execution context). | ||
*/ | ||
// wrap in IIFE to align execution context in prospective 'non-Node' environments | ||
// we do this in the case we want to expose on global explicitly | ||
var index = (function (global) { | ||
// mitigate need to use `new` keyword by returning a discrete function constructor | ||
// to generate the object | ||
const Vx = function (datatype, data, options) { | ||
var Vx = function Vx(datatype, data, options) { | ||
return new Vx.init(datatype, data, options); | ||
}; | ||
/* Private/Unexposed Props */ | ||
// global aggregation object - used to store and index all `Observables` | ||
var _observables = {}; | ||
Vx.prototype = {}; | ||
const _observables = {}; | ||
/* Method Injection Library */ | ||
// meta-prototype for storing methods accessible to all `Observable` instances | ||
// place methods which you wish to expose on Vx instances here and uncomment reassignments below (`global` wrapper util) | ||
Vx.prototype = {// any methods added here will be exposed to *all* `Observables` | ||
// we can actually import other modules or libs here; in doing so, we need to further tighten the security | ||
// on global denominations so as to mitigate nasty dependency collisions (assuming we are using `global` - currently, no) | ||
// typecast: function(inboundType) { | ||
// do stuff | ||
// return new Vx.init(datatype, data, options); | ||
// } | ||
}; | ||
/* Datatype Factory */ | ||
// the actual method which is executed | ||
// this is mostly config for prospective macro-object use and ubiquitous methods | ||
Vx.init = function (datatype, data, options) { | ||
// this assignment will point to the execution context of the newly generated `Observable` | ||
// remaining vars are hoisted | ||
const _observableKeys = Object.keys(_observables); // transient Object for assembling prototype and defaults injection | ||
var _observableKeys = Object.keys(_observables); | ||
var _intermediateObject; | ||
let _intermediateObject; | ||
if (datatype === 'Array') { | ||
@@ -892,96 +662,50 @@ _intermediateObject = new ObservableArray(data); | ||
_intermediateObject = new ObservableObject(data); | ||
} // unsupported / unprovided type | ||
else { | ||
throw new Error(`Error: datatype ${datatype} is not a supported option.`); | ||
} else { | ||
throw new Error("Error: datatype ".concat(datatype, " is not a supported option.")); | ||
} | ||
/* set defaults here */ | ||
// the type of a given `Observable` instance e.g. 'Array' | ||
var _type = datatype; | ||
const _type = datatype; // the unique identifier for a given `Observable` instance | ||
var _identifier; | ||
let _identifier; // if options passed, configure accordingly | ||
if (options) { | ||
/* | ||
Destructure id and enforce unique filter | ||
Here, we are evaluating a ternary expression wrapped inside of an IIFE. | ||
This IIFE accepts as input the `options` object and destructures the id inline. We are explicitly | ||
mapping the prop `uniqueIdentifier` and destructuring it away from the resolved IIFE. | ||
*/ | ||
const { | ||
uniqueIdentifier | ||
} = (({ | ||
id | ||
}) => ({ | ||
uniqueIdentifier: _observableKeys.includes(id.toString()) ? undefined : id | ||
/* prop2, prop3... */ | ||
var _ref = function (_ref2) { | ||
var id = _ref2.id; | ||
return { | ||
uniqueIdentifier: _observableKeys.includes(id.toString()) ? undefined : id | ||
}; | ||
}(options), | ||
uniqueIdentifier = _ref.uniqueIdentifier; | ||
}))(options); // if id is found in keys array of `_observables`, the instantiation should be terminated as the id is a duplicate | ||
if (!uniqueIdentifier) { | ||
throw new Error(`Error: Identifier ${options.id} is currently in use.`); | ||
throw new Error("Error: Identifier ".concat(options.id, " is currently in use.")); | ||
} | ||
_identifier = uniqueIdentifier; | ||
} // no options object param provided | ||
else { | ||
// destructure length from keys Array | ||
const { | ||
length | ||
} = _observableKeys; | ||
} else { | ||
var length = _observableKeys.length; | ||
_identifier = length; | ||
} // we can do this later at any point within this scope; leave until we need to do something with the type | ||
// _type = datatype; | ||
// Create a new Object of all ubiquitous props for which `defineProperty` will be called. Then, | ||
// destructure the key/value pairs from the Array produced by `Object.entries` and call `forEach` thereon | ||
// Ea. key will be collated as a computed property with its accompanying value | ||
} | ||
Object.entries({ | ||
identifier: _identifier, | ||
type: _type | ||
}).forEach(([key, value]) => // use `defineProperty` for greater control granularity; set prop to non-enumerable | ||
Object.defineProperty(_intermediateObject, key, { | ||
configurable: false, | ||
enumerable: false, | ||
value: value | ||
})); // persist new `Observable` inside `_observables` at index `_identifier` | ||
}).forEach(function (_ref3) { | ||
var _ref4 = _slicedToArray(_ref3, 2), | ||
key = _ref4[0], | ||
value = _ref4[1]; | ||
return Object.defineProperty(_intermediateObject, key, { | ||
configurable: false, | ||
enumerable: false, | ||
value: value | ||
}); | ||
}); | ||
_observables[_identifier] = _intermediateObject; | ||
return _intermediateObject; | ||
}; | ||
/* | ||
~ For use with currently inactive `global` execution context wrapper ~ | ||
point prototype of each `Observable` instance to the aforementioned meta prototype to expose ubiquitous methods | ||
ObservableArray.prototype = Vx.prototype; | ||
ObservableString.prototype = Vx.prototype; | ||
ObservableObject.prototype = Vx.prototype; | ||
point proto to same execution context so as to provide an optional caller alias, `Vx` | ||
global.Observable = global.Vx = Observable; | ||
*/ | ||
return Vx; | ||
})(); | ||
/* | ||
Passing `global` in lieu of `this`. | ||
Currently inactive, but applicable if we ever decide to go back to the `global` diad context approach. | ||
We pass the global object in lieu of `this` for a couple of very specific performance reasons: | ||
1. JavaScript always performs scope 'lookups' from the current function's scope *upward* until it finds an identifier; | ||
if we pass `this` as `global` into an IIFE, the IIFE's execution is the only time our code will need to process a scope lookup beyond | ||
the local scope for `this`. Subsequent references to `global` inside the IIFE will therefore never require a lookup beyond the local scope of the IIFE. | ||
2. JS minifiers won't minify direct references to anything without the context of the IIFE; | ||
we may need this param as a point-of-reference. | ||
*/ | ||
export default index; |
@@ -7,23 +7,84 @@ (function (global, factory) { | ||
/** | ||
* @param {Function} fn The callback to be executed after the timeout is lifted. | ||
* @param {Number} ms A number denoting milliseconds until timeout is lifted. | ||
* @summary Debounces a function call by `ms` milliseconds. | ||
*/ | ||
function _typeof(obj) { | ||
"@babel/helpers - typeof"; | ||
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { | ||
_typeof = function (obj) { | ||
return typeof obj; | ||
}; | ||
} else { | ||
_typeof = function (obj) { | ||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; | ||
}; | ||
} | ||
return _typeof(obj); | ||
} | ||
function _slicedToArray(arr, i) { | ||
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); | ||
} | ||
function _arrayWithHoles(arr) { | ||
if (Array.isArray(arr)) return arr; | ||
} | ||
function _iterableToArrayLimit(arr, i) { | ||
if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; | ||
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"] != null) _i["return"](); | ||
} finally { | ||
if (_d) throw _e; | ||
} | ||
} | ||
return _arr; | ||
} | ||
function _unsupportedIterableToArray(o, minLen) { | ||
if (!o) return; | ||
if (typeof o === "string") return _arrayLikeToArray(o, minLen); | ||
var n = Object.prototype.toString.call(o).slice(8, -1); | ||
if (n === "Object" && o.constructor) n = o.constructor.name; | ||
if (n === "Map" || n === "Set") return Array.from(o); | ||
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); | ||
} | ||
function _arrayLikeToArray(arr, len) { | ||
if (len == null || len > arr.length) len = arr.length; | ||
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; | ||
return arr2; | ||
} | ||
function _nonIterableRest() { | ||
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); | ||
} | ||
function debounce(fn, ms) { | ||
let timeout; | ||
return args => { | ||
var timeout; | ||
return function (args) { | ||
clearTimeout(timeout); | ||
timeout = setTimeout(() => fn(args), ms); | ||
timeout = setTimeout(function () { | ||
return fn(args); | ||
}, ms); | ||
}; | ||
} | ||
/** | ||
* @param {Function} fn The function to be granted a computed name property. | ||
* @param {String} name The value of prop `name`. | ||
* @summary Accepts as input a function and assigns to it a computed name property of value `name`. | ||
* @returns {Function} The passed function, now accompanied by a computed name property. | ||
*/ | ||
function computeNamedFunction(fn, name) { | ||
@@ -36,11 +97,4 @@ Object.defineProperty(fn, 'name', { | ||
} | ||
/** | ||
* @param {Object} context The Object instance on which to define prop `addEventListener`. | ||
* @param {Object} handlers The provided instance's handlers store. | ||
* @summary Defines `addEventListener` property. | ||
*/ | ||
function defineAddEventListener(context, handlers) { | ||
// override addEventListener method of given array | ||
Object.defineProperty(context, 'addEventListener', { | ||
@@ -50,7 +104,4 @@ configurable: false, | ||
writable: false, | ||
// false; we do not want further tampering here | ||
value: function (eventName, handler, ms) { | ||
// sanitize and validate handler submissions | ||
// simple type-check: concatenate an empty string to coerce `eventName` | ||
eventName = ('' + eventName).toLowerCase(); // ensure registered event's name corresponds to one of the presets in `_handlers` | ||
value: function value(eventName, handler, ms) { | ||
eventName = ('' + eventName).toLowerCase(); | ||
@@ -66,10 +117,8 @@ if (!(eventName in handlers)) { | ||
if (ms) { | ||
const machinedProperty = handler.name; | ||
var machinedProperty = handler.name; | ||
handler = debounce(handler, ms); | ||
handler = computeNamedFunction(handler, machinedProperty); | ||
} // add handler to respective event store Array | ||
} | ||
handlers[eventName].push(handler); // explicitly return `context` to allow method chaining across consistent parent Object / execution context | ||
handlers[eventName].push(handler); | ||
return context; | ||
@@ -79,11 +128,4 @@ } | ||
} | ||
/** | ||
* @param {Object} context The Object instance on which to define prop `removeEventListener`. | ||
* @param {Object} handlers The provided instance's handlers store. | ||
* @summary Defines `removeEventListener` property. | ||
*/ | ||
function defineRemoveEventListener(context, handlers) { | ||
// override removeEventListener method of given array | ||
Object.defineProperty(context, 'removeEventListener', { | ||
@@ -93,3 +135,3 @@ configurable: false, | ||
writable: false, | ||
value: function (eventName, handler) { | ||
value: function value(eventName, handler) { | ||
eventName = ('' + eventName).toLowerCase(); | ||
@@ -103,19 +145,13 @@ | ||
throw new Error('Error: Invalid handler.'); | ||
} // reference all handlers of given `eventName` | ||
} | ||
var handlerSet = handlers[eventName]; | ||
var handlerSetLen = handlerSet.length; | ||
const handlerSet = handlers[eventName]; | ||
let handlerSetLen = handlerSet.length; // ensure handler exists, lookup, remove | ||
while (--handlerSetLen >= 0) { | ||
// we eval via the handler's name property given some handlers will have been recomputed with the | ||
// `debounce` wrapper; by-value comparison is included to handle anon fns. | ||
if (handlerSet[handlerSetLen] === handler || handlerSet[handlerSetLen].name === handler.name) { | ||
// handler exists, remove | ||
// NOTE: w/out explicit `return`, this loop will continue removing *all* matched handlers | ||
handlerSet.splice(handlerSetLen, 1); | ||
} | ||
} // explicitly return `context` to allow method chaining across consistent parent Object / execution context | ||
} | ||
return context; | ||
@@ -125,12 +161,5 @@ } | ||
} | ||
/** | ||
* @param {Object} event An Object containing event-contingent data. | ||
* @param {Object} context The `this` value on which to call each instance. | ||
* @param {Object} handlers The provided instance's handlers store. | ||
* @summary Systematically calls each handler of a given event type. | ||
*/ | ||
function raiseEvent(event, context, handlers) { | ||
handlers[event.type].forEach(handler => { | ||
handlers[event.type].forEach(function (handler) { | ||
handler.call(context, event); | ||
@@ -140,17 +169,7 @@ }); | ||
/** | ||
* @override | ||
* @summary Factory for instantiating observable Array-like objects. | ||
* @description Copies array into Array-like object and hijacks specific instance's base prototype, | ||
* thus creating an observable of type Array. Provides custom handlers for | ||
* Array methods `push`, `pop`, `shift`, `unshift`, `splice`. `length`, | ||
* Makes available custom index accessors provided the ObservableArray has been mutated by way | ||
* of aforementioned methods, `length`, or the `value` accessor, which is common to all `Observables`. | ||
* @augments Array | ||
*/ | ||
function ObservableArray(items) { | ||
var _this = this; | ||
function ObservableArray(items) { | ||
// `this` is used throughout as a mutable constructor | ||
const _self = this, | ||
_handlers = { | ||
var _self = this, | ||
_handlers = { | ||
itemadded: [], | ||
@@ -160,20 +179,15 @@ itemremoved: [], | ||
mutated: [] | ||
}; // internal value store | ||
}; | ||
var _array = []; | ||
let _array = []; | ||
/** | ||
* @summary Dynamically defines index accessors. | ||
*/ | ||
const defineIndexProperty = index => { | ||
var defineIndexProperty = function defineIndexProperty(index) { | ||
if (!(index in _self)) { | ||
Object.defineProperty(_self, index, { | ||
configurable: true, | ||
// type of descriptor may be changed / deleted from corresponding obj | ||
enumerable: true, | ||
// enumerate to `true` so as to expose item indices | ||
get: () => _array[index], | ||
set: inboundItem => { | ||
// set actual item to provided index | ||
get: function get() { | ||
return _array[index]; | ||
}, | ||
set: function set(inboundItem) { | ||
_array[index] = inboundItem; | ||
@@ -189,12 +203,10 @@ raiseEvent({ | ||
}; | ||
/** | ||
* @summary Defines accessor for actual array value (core val). | ||
*/ | ||
Object.defineProperty(_self, 'value', { | ||
configurable: false, | ||
enumerable: false, | ||
get: () => _array, | ||
set: inboundItems => { | ||
get: function get() { | ||
return _array; | ||
}, | ||
set: function set(inboundItems) { | ||
if (inboundItems instanceof Array) { | ||
@@ -210,10 +222,2 @@ _array = inboundItems; | ||
}); | ||
/* Custom Methods */ | ||
/** | ||
* @param {Any} value The value to be matched against core arr items. | ||
* @summary Returns an Array of all indices that contain a match to given argument. Does not evaluate nested items. | ||
* @returns {Array} A multi-dimensional array of matched indices. | ||
*/ | ||
Object.defineProperty(_self, 'findIndexAll', { | ||
@@ -223,7 +227,7 @@ configurable: false, | ||
writable: false, | ||
value: value => { | ||
const indices = []; | ||
value: function value(_value) { | ||
var indices = []; | ||
_array.forEach((item, index) => { | ||
if (item === value) { | ||
_array.forEach(function (item, index) { | ||
if (item === _value) { | ||
indices.push(index); | ||
@@ -236,8 +240,2 @@ } | ||
}); | ||
/** | ||
* @param {Any} value The value to be matched against core arr items. | ||
* @summary Returns an Array of all indices that contain a match to given argument. Walks entire Array tree and evaluates nested items. | ||
* @returns {Array} A multi-dimensional array of matched indices. | ||
*/ | ||
Object.defineProperty(_self, 'findIndexAllDeep', { | ||
@@ -247,34 +245,20 @@ configurable: false, | ||
writable: false, | ||
value: value => { | ||
const indices = []; // path will be an Array with the current level therein | ||
value: function value(_value2) { | ||
var indices = []; | ||
this.some(function walk(path) { | ||
// will keep 'matching' indices until it hits the actual value; creates multi-dimensional Array | ||
_this.some(function walk(path) { | ||
return function (item, i) { | ||
// while item is a match, continue pushing corresponding indices into Array `indices` | ||
if (item === value) { | ||
if (item === _value2) { | ||
indices.push(path.concat(i)); | ||
} // else, ensure `item` is an Array (indicating another level to walk) | ||
// recurse and call `walk`; `some` implicitly passes `this` aka `item` as the first argument | ||
} | ||
return Array.isArray(item) && item.some(walk(path.concat(i))); | ||
}; | ||
}([])); | ||
/* we initialize as an IIFE and pass an empty Array as our default param*/ | ||
// return multi-dimensional Array mapping of matched indices | ||
return indices; | ||
} | ||
}); // Note: we could later add something like `JSON.stringify(_array[i]).indexOf(value) > -1` to eval whether or not | ||
// the value exists and we should continue recursing the tree | ||
// define props for event-binding | ||
}); | ||
defineAddEventListener(_self, _handlers); | ||
defineRemoveEventListener(_self, _handlers); | ||
/** | ||
* @summary Defines event-bound `push` method. | ||
* @override `Array.prototype.push` | ||
*/ | ||
Object.defineProperty(_self, 'push', { | ||
@@ -284,17 +268,19 @@ configurable: false, | ||
writable: false, | ||
value: (...args) => { | ||
let index; // for each provided element, push into copy `_array` | ||
value: function value() { | ||
var index; | ||
args.forEach(item => { | ||
// persist index at which item will be added | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
args.forEach(function (item) { | ||
index = _array.length; | ||
_array.push(item); // define index accessor for each element | ||
_array.push(item); | ||
defineIndexProperty(index); | ||
raiseEvent({ | ||
type: 'itemadded', | ||
index, | ||
item | ||
index: index, | ||
item: item | ||
}, _self, _handlers); | ||
@@ -305,7 +291,2 @@ }); | ||
}); | ||
/** | ||
* @summary Defines event-bound `pop` method. | ||
* @override `Array.prototype.pop` | ||
*/ | ||
Object.defineProperty(_self, 'pop', { | ||
@@ -315,6 +296,6 @@ configurable: false, | ||
writable: false, | ||
value: () => { | ||
value: function value() { | ||
if (_array.length > 0) { | ||
const index = _array.length - 1, | ||
item = _array.pop(); | ||
var index = _array.length - 1, | ||
item = _array.pop(); | ||
@@ -324,4 +305,4 @@ delete _self[index]; | ||
type: 'itemremoved', | ||
index, | ||
item | ||
index: index, | ||
item: item | ||
}, _self, _handlers); | ||
@@ -332,7 +313,2 @@ return item; | ||
}); | ||
/** | ||
* @summary Defines event-bound `unshift` method. | ||
* @override `Array.prototype.unshift` | ||
*/ | ||
Object.defineProperty(_self, 'unshift', { | ||
@@ -342,7 +318,8 @@ configurable: false, | ||
writable: false, | ||
value: (...args) => { | ||
// NOTE this is one of those rare instances where we *need* `var`, lest the next loop's `i` be undefined due to | ||
// scoping behaviors of `let` | ||
args.forEach((item, index) => { | ||
// add arg to beginning of core val | ||
value: function value() { | ||
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { | ||
args[_key2] = arguments[_key2]; | ||
} | ||
args.forEach(function (item, index) { | ||
_array.splice(index, 0, item); | ||
@@ -353,13 +330,13 @@ | ||
type: 'itemadded', | ||
index, | ||
item | ||
index: index, | ||
item: item | ||
}, _self, _handlers); | ||
}); | ||
_array.forEach((item, index) => { | ||
_array.forEach(function (item, index) { | ||
if (index >= args.length) { | ||
raiseEvent({ | ||
type: 'itemset', | ||
index, | ||
item | ||
index: index, | ||
item: item | ||
}, _self, _handlers); | ||
@@ -372,7 +349,2 @@ } | ||
}); | ||
/** | ||
* @summary Defines event-bound `shift` method. | ||
* @override `Array.prototype.shift` | ||
*/ | ||
Object.defineProperty(_self, 'shift', { | ||
@@ -382,9 +354,6 @@ configurable: false, | ||
writable: false, | ||
value: () => { | ||
// only actionable if Array contains elements | ||
value: function value() { | ||
if (_array.length > 0) { | ||
const item = _array.shift(); // NOTE imperative; `shift` will not persist this change; | ||
// we simulate this behavior by deleting value at index `_array.length` | ||
var item = _array.shift(); | ||
delete _self[_array.length]; | ||
@@ -394,3 +363,3 @@ raiseEvent({ | ||
index: 0, | ||
item | ||
item: item | ||
}, _self, _handlers); | ||
@@ -401,7 +370,2 @@ return item; | ||
}); | ||
/** | ||
* @summary Defines event-bound `splice` method. | ||
* @override `Array.prototype.splice` | ||
*/ | ||
Object.defineProperty(_self, 'splice', { | ||
@@ -411,31 +375,28 @@ configurable: false, | ||
writable: false, | ||
value: (...args) => { | ||
// removed items store | ||
const removed = []; | ||
let [index, numToRemove, ...itemsToAdd] = args, | ||
// explicitly hoist to outer context | ||
item; // calculate index qua splice parameters | ||
// we need `==` so as to coerce possible `undefined` to null during eval | ||
// this is also where we simulate `splice's` capacity to accept negative indices and walk recessively from core val length | ||
value: function value() { | ||
var removed = []; | ||
index = index == null ? 0 : index < 0 ? _array.length + index : index; // calculate number of elements qua splice parameters | ||
for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { | ||
args[_key3] = arguments[_key3]; | ||
} | ||
var index = args[0], | ||
numToRemove = args[1], | ||
itemsToAdd = args.slice(2), | ||
item; | ||
index = index == null ? 0 : index < 0 ? _array.length + index : index; | ||
numToRemove = numToRemove == null ? _array.length - index : numToRemove > 0 ? numToRemove : 0; | ||
while (numToRemove--) { | ||
// we specify the 0th index to coerce to val as opposed to an array with val therein | ||
item = _array.splice(index, 1)[0]; | ||
removed.push(item); // reflect changes in constructor arr | ||
removed.push(item); | ||
delete _self[_array.length]; | ||
raiseEvent({ | ||
type: 'itemremoved', | ||
// index begins at first removal; increment by ea. removed item | ||
index: index + removed.length - 1, | ||
item | ||
item: item | ||
}, _self, _handlers); | ||
} | ||
itemsToAdd.forEach(item => { | ||
// no need to update _self as `splice` interceptor will do this for us | ||
itemsToAdd.forEach(function (item) { | ||
_array.splice(index, 0, item); | ||
@@ -446,6 +407,5 @@ | ||
type: 'itemadded', | ||
index, | ||
item | ||
}, _self, _handlers); // explicitly increment index on ea. iteration | ||
index: index, | ||
item: item | ||
}, _self, _handlers); | ||
index++; | ||
@@ -456,41 +416,26 @@ }); | ||
}); | ||
/** | ||
* @summary Defines event-bound `length` method. | ||
* @override `Array.prototype.length` | ||
*/ | ||
Object.defineProperty(_self, 'length', { | ||
configurable: false, | ||
enumerable: false, | ||
get: () => _array.length, | ||
set: value => { | ||
// coerce input to Number | ||
const symbolicLength = Number(value); // est core val length | ||
get: function get() { | ||
return _array.length; | ||
}, | ||
set: function set(value) { | ||
var symbolicLength = Number(value); | ||
var length = _array.length; | ||
const length = _array.length; // user-specified length must be whole number | ||
if (symbolicLength % 1 === 0 && symbolicLength >= 0) { | ||
// if len provided less than that of current arr value, we truncate the core val w/`splice` | ||
if (symbolicLength < length) { | ||
_self.splice(symbolicLength); | ||
} // user is extending core val; push new generated Array of specified len sans core len | ||
else if (symbolicLength > length) { | ||
} else if (symbolicLength > length) { | ||
Reflect.apply(_self.push, _self, new Array(symbolicLength - length)); | ||
} | ||
} // catch edge cases | ||
else { | ||
} else { | ||
throw new RangeError('Error: Invalid array length.'); | ||
} // finally, we extend actual core | ||
} | ||
_array.length = symbolicLength; | ||
} | ||
}); | ||
/** | ||
* @summary Process prototype for instance to ensure we extend undeclared Array methods. | ||
* @override `Array.prototype` | ||
*/ | ||
Object.getOwnPropertyNames(Array.prototype).forEach(name => { | ||
// ensure prop isn't already allocated so as to avoid collisions | ||
Object.getOwnPropertyNames(Array.prototype).forEach(function (name) { | ||
if (!(name in _self)) { | ||
@@ -504,6 +449,5 @@ Object.defineProperty(_self, name, { | ||
} | ||
}); // allocate inputs | ||
}); | ||
if (items instanceof Array) { | ||
// coerces _self to a container Array into which we copy `items` | ||
Reflect.apply(_self.push, _self, items); | ||
@@ -513,17 +457,5 @@ } | ||
/** | ||
* @override | ||
* @readonly | ||
* @summary Factory for instantiating observable String-like objects. | ||
* @description Copies given string input into new String object; proceeds to assign props for adding/removing | ||
* event-listeners, adds custom `reassign` method and accessors, assimilates extended String prototype props. | ||
* Extended String prototype methods are conformed as computed properties that are called on the primitive String value | ||
* contained by the `ObservableString`, and not the `ObservableString` itself. | ||
* Registers as events `mutated`. | ||
* @augments String | ||
*/ | ||
function ObservableString(value) { | ||
const _self = this, | ||
_handlers = { | ||
var _self = this, | ||
_handlers = { | ||
mutated: [] | ||
@@ -533,40 +465,19 @@ }; | ||
if (typeof value === 'string') { | ||
/* | ||
The actual primitive value is stored in String Object. | ||
Unlike the `ObservableArray`, we don't need to copy the value to an internal, nested prop given the limited scope of operations | ||
to be performed on a primitive i.e. the String versus an Object such as an Array | ||
*/ | ||
_self[0] = String(value); | ||
} | ||
/** | ||
* @param {Object} coreObject Given Object to be mutated | ||
* @param {String} coreValue User-provided value to which the internal/core val will be mutated | ||
* @summary Supplants core value with that provided by user. | ||
* @returns {Object} Comprised of the previous core value `value` and the new core value `mutant`. | ||
*/ | ||
const mutateCoreValue = (coreObject, coreValue) => { | ||
// persist pre-mutated value (note, not the String Object but the value therein) | ||
const value = coreObject[0].valueOf(); | ||
coreObject[0] = String(coreValue); // get current mutated/new String value | ||
const mutant = coreObject[0].valueOf(); // execute callback; | ||
var mutateCoreValue = function mutateCoreValue(coreObject, coreValue) { | ||
var value = coreObject[0].valueOf(); | ||
coreObject[0] = String(coreValue); | ||
var mutant = coreObject[0].valueOf(); | ||
return { | ||
value, | ||
mutant | ||
value: value, | ||
mutant: mutant | ||
}; | ||
}; | ||
/** | ||
* @summary Defines accessor for String primitive / core value. | ||
*/ | ||
Object.defineProperty(_self, 'value', { | ||
configurable: false, | ||
enumerable: false, | ||
get() { | ||
// if `undefined`, we implicitly return `undefined` and mitigate the exception that would be thrown by calling `valueOf` thereon | ||
get: function get() { | ||
if (_self[0]) { | ||
@@ -578,10 +489,4 @@ return _self[0].valueOf(); | ||
}, | ||
set(arg) { | ||
set: function set(arg) { | ||
if (typeof arg === 'string') { | ||
/* | ||
By using `Object.assign`, we can create, in-line, a new Object consisting of a key/value pair to denote our desired event type. | ||
`assign` will 'merge' the in-line Object with that returned from `mutateCoreValue`. | ||
Given `raiseEvent` accepts as input an Object, we can pass this expression directly; it will resolve to the necessary values. | ||
*/ | ||
raiseEvent(Object.assign({ | ||
@@ -592,8 +497,3 @@ type: 'mutated' | ||
} | ||
}); | ||
/** | ||
* @summary Defines custom method `reassign`. | ||
*/ | ||
Object.defineProperty(_self, 'reassign', { | ||
@@ -603,39 +503,15 @@ configurable: false, | ||
writable: false, | ||
/** | ||
* @param {String|Any} candidate User provided value to which the Observable will be reassigned (provided it is a String). | ||
* @summary Supplants internal value with `candidate` and broadcasts 'mutated' event. | ||
* @returns {Object} The Observable instance, returned so as to allow method chaining. | ||
*/ | ||
value(candidate) { | ||
// type-check and validations go here | ||
value: function value(candidate) { | ||
if (!(typeof candidate === 'string')) { | ||
throw new Error('Error: Invalid type.'); | ||
} | ||
/* | ||
_self.value = stringInput; | ||
Mutating the `value` prop here as demonstrated in the above-commented code *would* fire all `mutated` events given the | ||
execution context of prop `value` is recursively applicable. However, we'd like to maintain as great a degree and | ||
granularity of control as possible. As such, we do *not* use the `value` accessor here, instead opting to raise | ||
the `mutated` event in a discrete context. | ||
*/ | ||
raiseEvent(Object.assign({ | ||
type: 'mutated' | ||
}, mutateCoreValue(_self, candidate)), _self, _handlers); // return `_self` to allow method to be chainable; see other like-comments | ||
}, mutateCoreValue(_self, candidate)), _self, _handlers); | ||
return _self; | ||
} | ||
}); // define props for event-binding | ||
}); | ||
defineAddEventListener(_self, _handlers); | ||
defineRemoveEventListener(_self, _handlers); | ||
/** | ||
* @override | ||
* @summary Define event-bound `split` method. | ||
*/ | ||
Object.defineProperty(_self, 'split', { | ||
@@ -645,45 +521,20 @@ configurable: false, | ||
writable: false, | ||
value: function value(delimiter) { | ||
var currentStringVal = _self[0].valueOf(); | ||
value(delimiter) { | ||
// get current String value | ||
const currentStringVal = _self[0].valueOf(); // split current with provided argument `delimiter` | ||
const splitArr = currentStringVal.split(delimiter); // non-mutating, no need to raise event; return | ||
var splitArr = currentStringVal.split(delimiter); | ||
return splitArr; | ||
} | ||
}); | ||
/** | ||
* @override | ||
* @summary Define event-bound `length` method / accessor. | ||
*/ | ||
Object.defineProperty(_self, 'length', { | ||
configurable: false, | ||
enumerable: false, | ||
get() { | ||
get: function get() { | ||
return _self[0].length; | ||
}, | ||
// use noop here lest babel 7 complain | ||
set() { | ||
// essentially the 'only' noop in JavaScript (save for an empty function expression) | ||
// note we do not use an empty arrow func for linting/consistency purposes | ||
set: function set() { | ||
Function.prototype(); | ||
} | ||
}); | ||
/** | ||
* @override | ||
* @summary Extend `String` prototype as computed values. | ||
* @description Here, we extend onto `ObservableString` all String prototype methods not already extant | ||
* and, for each, conform get/set to the execution context of the primitive value contained therein. | ||
* @extends String | ||
*/ | ||
Object.getOwnPropertyNames(String.prototype).forEach(name => { | ||
// ensure prop isn't already allocated so as to avoid collisions | ||
Object.getOwnPropertyNames(String.prototype).forEach(function (name) { | ||
if (!(name in _self)) { | ||
@@ -694,12 +545,7 @@ Object.defineProperty(_self, name, { | ||
writable: false, | ||
value: function value() { | ||
var _self$; | ||
/** | ||
* @description Here, we intercept the getter/setter conformation of each method on String's prototype | ||
* We do this so as to set the execution context to point to the primitive String and *not* its parent Object, | ||
* in this case `ObservableString` | ||
*/ | ||
value(...args) { | ||
return _self[0][name](...args); | ||
return (_self$ = _self[0])[name].apply(_self$, arguments); | ||
} | ||
}); | ||
@@ -710,18 +556,8 @@ } | ||
/* eslint-disable no-unused-vars */ | ||
/** | ||
* @param {Object} obj Object whose value will be copied and proxied. | ||
* @summary Factory for instantiating proxied, observable Objects. | ||
* @description Copies Object and proxifies; configures necessary traps and broadcasts events upon the access thereof. | ||
* Recurses the target Object so as to enforce traps on nested props/accessors. | ||
* @augments Proxy | ||
*/ | ||
function ObservableObject(obj) { | ||
/* Configuration Artifacts */ | ||
const _symbols = { | ||
__INTERNAL__: Symbol.for('_internal'), | ||
__ISPROXY__: Symbol.for('_isProxy') | ||
var _symbols = { | ||
__INTERNAL__: Symbol["for"]('_internal'), | ||
__ISPROXY__: Symbol["for"]('_isProxy') | ||
}, | ||
_handlers = { | ||
_handlers = { | ||
itemget: [], | ||
@@ -731,48 +567,32 @@ itemdeleted: [], | ||
}, | ||
// non-enumerable prop store - mitigate get|set events from being called thereon | ||
_internals = ['addEventListener', 'removeEventListener', 'identifier', 'type']; | ||
/** | ||
* @param {String} prop The property presently being accessed. | ||
* @summary Intercepts attempts to set|delete non-configurable properties. | ||
* @throws {Error} Raises exception if property `prop` is present in `_internals`. | ||
*/ | ||
_internals = ['addEventListener', 'removeEventListener', 'identifier', 'type']; | ||
const enforceConfigurableProps = prop => { | ||
var enforceConfigurableProps = function enforceConfigurableProps(prop) { | ||
if (_internals.includes(prop)) { | ||
throw new Error(`Error: Property '${prop}' is non-configurable.`); | ||
throw new Error("Error: Property '".concat(prop, "' is non-configurable.")); | ||
} | ||
}; | ||
/** | ||
* @summary Root Proxy handler; injects event broadcasts into get|set|delete traps. | ||
*/ | ||
const _rootHandler = { | ||
get(target, prop, recv) { | ||
// type-check mechanism for testing | ||
var _rootHandler = { | ||
get: function get(target, prop, recv) { | ||
if (prop === _symbols.__ISPROXY__) { | ||
return true; | ||
} // conform to `value` accessor and return raw value | ||
} | ||
if (prop === _symbols.__INTERNAL__) { | ||
return target; | ||
} // we use `Reflect` here as a simple mitigative effort to avoid violating Proxy invariants, as described in the specification here: | ||
// https://www.ecma-international.org/ecma-262/8.0/#sec-proxy-object-internal-methods-and-internal-slots-get-p-receiver | ||
} | ||
var value = Reflect.get(target, prop, recv); | ||
const value = Reflect.get(target, prop, recv); // recurse and continue chain of Proxies for nested props to ensure traps are executed upon access thereof | ||
if (typeof value === 'object') { | ||
if (_typeof(value) === 'object') { | ||
return new Proxy(value, _rootHandler); | ||
} // tradeoff here is `raiseEvent` won't be called for root getters; else, the cb would fire for each branch | ||
// TODO fix this by caching the Object with a WeakMap or something and walking the tree on ea. `get`...I think this would work | ||
} | ||
if (value && !_internals.includes(prop)) { | ||
raiseEvent({ | ||
type: 'itemget', | ||
prop, | ||
target, | ||
value | ||
prop: prop, | ||
target: target, | ||
value: value | ||
}, this, _handlers); | ||
@@ -783,59 +603,41 @@ } | ||
}, | ||
set(target, prop, value, recv) { | ||
set: function set(target, prop, value, recv) { | ||
enforceConfigurableProps(prop); | ||
raiseEvent({ | ||
type: 'itemset', | ||
prop, | ||
target, | ||
value | ||
prop: prop, | ||
target: target, | ||
value: value | ||
}, this, _handlers); | ||
return Reflect.set(target, prop, value); | ||
}, | ||
deleteProperty(target, prop) { | ||
enforceConfigurableProps(prop); // `toString` returns a default value when argument is type Object, ergo, we use `JSON.stringify` here | ||
// see: https://es5.github.io/#x9.8 | ||
const ephemeralTarget = JSON.stringify(target, null, 0); | ||
const value = Reflect.deleteProperty(target, prop); | ||
deleteProperty: function deleteProperty(target, prop) { | ||
enforceConfigurableProps(prop); | ||
var ephemeralTarget = JSON.stringify(target, null, 0); | ||
var value = Reflect.deleteProperty(target, prop); | ||
raiseEvent({ | ||
type: 'itemdeleted', | ||
prop, | ||
prop: prop, | ||
target: ephemeralTarget, | ||
value | ||
value: value | ||
}, this, _handlers); | ||
return value; | ||
} | ||
}; | ||
/* Root Object Conformations */ | ||
// initialize root Proxy | ||
if (!(obj instanceof Object)) { | ||
return; | ||
} // make a deep clone here so as to break the chain of reference to the provided argument `obj` | ||
// if we pass `obj` directly, the Proxy will modify the original target | ||
} | ||
var _self = new Proxy(JSON.parse(JSON.stringify(obj)), _rootHandler); | ||
const _self = new Proxy(JSON.parse(JSON.stringify(obj)), _rootHandler); | ||
/** | ||
* @summary Defines accessor for core Object value, decoupled from original target ref. | ||
*/ | ||
Object.defineProperty(_self, 'value', { | ||
configurable: false, | ||
enumerable: false, | ||
get() { | ||
// `assign` here to decouple execution context from returned val | ||
// this allows users to, at any time, copy internal value without Proxy properties | ||
get: function get() { | ||
return Object.assign({}, _self[_symbols.__INTERNAL__]); | ||
}, | ||
set(inboundItems) { | ||
set: function set(inboundItems) { | ||
return; | ||
} | ||
}); | ||
@@ -847,47 +649,15 @@ defineAddEventListener(_self, _handlers); | ||
/* eslint-disable no-unused-vars */ | ||
/** | ||
* @summary A factory / wrapper for exporting the Vivisector.js `Observables` and their associated properties. | ||
* @description Exposes various JavaScript datatypes and primitives and extends them with both event-driven | ||
* properties (qua the `Observable`'s execution context), and ubiquitous Vivisector-contingent properties (qua the macro execution context). | ||
*/ | ||
// wrap in IIFE to align execution context in prospective 'non-Node' environments | ||
// we do this in the case we want to expose on global explicitly | ||
var index = (function (global) { | ||
// mitigate need to use `new` keyword by returning a discrete function constructor | ||
// to generate the object | ||
const Vx = function (datatype, data, options) { | ||
var Vx = function Vx(datatype, data, options) { | ||
return new Vx.init(datatype, data, options); | ||
}; | ||
/* Private/Unexposed Props */ | ||
// global aggregation object - used to store and index all `Observables` | ||
var _observables = {}; | ||
Vx.prototype = {}; | ||
const _observables = {}; | ||
/* Method Injection Library */ | ||
// meta-prototype for storing methods accessible to all `Observable` instances | ||
// place methods which you wish to expose on Vx instances here and uncomment reassignments below (`global` wrapper util) | ||
Vx.prototype = {// any methods added here will be exposed to *all* `Observables` | ||
// we can actually import other modules or libs here; in doing so, we need to further tighten the security | ||
// on global denominations so as to mitigate nasty dependency collisions (assuming we are using `global` - currently, no) | ||
// typecast: function(inboundType) { | ||
// do stuff | ||
// return new Vx.init(datatype, data, options); | ||
// } | ||
}; | ||
/* Datatype Factory */ | ||
// the actual method which is executed | ||
// this is mostly config for prospective macro-object use and ubiquitous methods | ||
Vx.init = function (datatype, data, options) { | ||
// this assignment will point to the execution context of the newly generated `Observable` | ||
// remaining vars are hoisted | ||
const _observableKeys = Object.keys(_observables); // transient Object for assembling prototype and defaults injection | ||
var _observableKeys = Object.keys(_observables); | ||
var _intermediateObject; | ||
let _intermediateObject; | ||
if (datatype === 'Array') { | ||
@@ -899,98 +669,52 @@ _intermediateObject = new ObservableArray(data); | ||
_intermediateObject = new ObservableObject(data); | ||
} // unsupported / unprovided type | ||
else { | ||
throw new Error(`Error: datatype ${datatype} is not a supported option.`); | ||
} else { | ||
throw new Error("Error: datatype ".concat(datatype, " is not a supported option.")); | ||
} | ||
/* set defaults here */ | ||
// the type of a given `Observable` instance e.g. 'Array' | ||
var _type = datatype; | ||
const _type = datatype; // the unique identifier for a given `Observable` instance | ||
var _identifier; | ||
let _identifier; // if options passed, configure accordingly | ||
if (options) { | ||
/* | ||
Destructure id and enforce unique filter | ||
Here, we are evaluating a ternary expression wrapped inside of an IIFE. | ||
This IIFE accepts as input the `options` object and destructures the id inline. We are explicitly | ||
mapping the prop `uniqueIdentifier` and destructuring it away from the resolved IIFE. | ||
*/ | ||
const { | ||
uniqueIdentifier | ||
} = (({ | ||
id | ||
}) => ({ | ||
uniqueIdentifier: _observableKeys.includes(id.toString()) ? undefined : id | ||
/* prop2, prop3... */ | ||
var _ref = function (_ref2) { | ||
var id = _ref2.id; | ||
return { | ||
uniqueIdentifier: _observableKeys.includes(id.toString()) ? undefined : id | ||
}; | ||
}(options), | ||
uniqueIdentifier = _ref.uniqueIdentifier; | ||
}))(options); // if id is found in keys array of `_observables`, the instantiation should be terminated as the id is a duplicate | ||
if (!uniqueIdentifier) { | ||
throw new Error(`Error: Identifier ${options.id} is currently in use.`); | ||
throw new Error("Error: Identifier ".concat(options.id, " is currently in use.")); | ||
} | ||
_identifier = uniqueIdentifier; | ||
} // no options object param provided | ||
else { | ||
// destructure length from keys Array | ||
const { | ||
length | ||
} = _observableKeys; | ||
} else { | ||
var length = _observableKeys.length; | ||
_identifier = length; | ||
} // we can do this later at any point within this scope; leave until we need to do something with the type | ||
// _type = datatype; | ||
// Create a new Object of all ubiquitous props for which `defineProperty` will be called. Then, | ||
// destructure the key/value pairs from the Array produced by `Object.entries` and call `forEach` thereon | ||
// Ea. key will be collated as a computed property with its accompanying value | ||
} | ||
Object.entries({ | ||
identifier: _identifier, | ||
type: _type | ||
}).forEach(([key, value]) => // use `defineProperty` for greater control granularity; set prop to non-enumerable | ||
Object.defineProperty(_intermediateObject, key, { | ||
configurable: false, | ||
enumerable: false, | ||
value: value | ||
})); // persist new `Observable` inside `_observables` at index `_identifier` | ||
}).forEach(function (_ref3) { | ||
var _ref4 = _slicedToArray(_ref3, 2), | ||
key = _ref4[0], | ||
value = _ref4[1]; | ||
return Object.defineProperty(_intermediateObject, key, { | ||
configurable: false, | ||
enumerable: false, | ||
value: value | ||
}); | ||
}); | ||
_observables[_identifier] = _intermediateObject; | ||
return _intermediateObject; | ||
}; | ||
/* | ||
~ For use with currently inactive `global` execution context wrapper ~ | ||
point prototype of each `Observable` instance to the aforementioned meta prototype to expose ubiquitous methods | ||
ObservableArray.prototype = Vx.prototype; | ||
ObservableString.prototype = Vx.prototype; | ||
ObservableObject.prototype = Vx.prototype; | ||
point proto to same execution context so as to provide an optional caller alias, `Vx` | ||
global.Observable = global.Vx = Observable; | ||
*/ | ||
return Vx; | ||
})(); | ||
/* | ||
Passing `global` in lieu of `this`. | ||
Currently inactive, but applicable if we ever decide to go back to the `global` diad context approach. | ||
We pass the global object in lieu of `this` for a couple of very specific performance reasons: | ||
1. JavaScript always performs scope 'lookups' from the current function's scope *upward* until it finds an identifier; | ||
if we pass `this` as `global` into an IIFE, the IIFE's execution is the only time our code will need to process a scope lookup beyond | ||
the local scope for `this`. Subsequent references to `global` inside the IIFE will therefore never require a lookup beyond the local scope of the IIFE. | ||
2. JS minifiers won't minify direct references to anything without the context of the IIFE; | ||
we may need this param as a point-of-reference. | ||
*/ | ||
return index; | ||
}))); |
@@ -1,1 +0,1 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Vx=t()}(this,(function(){"use strict";function e(e,t){Object.defineProperty(e,"addEventListener",{configurable:!1,enumerable:!1,writable:!1,value:function(r,n,i){if(!((r=(""+r).toLowerCase())in t))throw new Error("Error: Invalid event name.");if("function"!=typeof n)throw new Error("Error: Invalid handler.");if(i){const e=n.name;n=function(e,t){let r;return n=>{clearTimeout(r),r=setTimeout((()=>e(n)),t)}}(n,i),o=n,l=e,Object.defineProperty(o,"name",{value:l,configurable:!0}),n=o}var o,l;return t[r].push(n),e}})}function t(e,t){Object.defineProperty(e,"removeEventListener",{configurable:!1,enumerable:!1,writable:!1,value:function(r,n){if(!((r=(""+r).toLowerCase())in t))throw new Error("Error: Invalid event name.");if("function"!=typeof n)throw new Error("Error: Invalid handler.");const i=t[r];let o=i.length;for(;--o>=0;)i[o]!==n&&i[o].name!==n.name||i.splice(o,1);return e}})}function r(e,t,r){r[e.type].forEach((r=>{r.call(t,e)}))}function n(n){const i=this,o={itemadded:[],itemremoved:[],itemset:[],mutated:[]};let l=[];const a=e=>{e in i||Object.defineProperty(i,e,{configurable:!0,enumerable:!0,get:()=>l[e],set:t=>{l[e]=t,r({type:"itemset",index:e,item:t},i,o)}})};Object.defineProperty(i,"value",{configurable:!1,enumerable:!1,get:()=>l,set:e=>{e instanceof Array&&(l=e,r({type:"mutated",index:"all",item:e},i,o))}}),Object.defineProperty(i,"findIndexAll",{configurable:!1,enumerable:!1,writable:!1,value:e=>{const t=[];return l.forEach(((r,n)=>{r===e&&t.push(n)})),t}}),Object.defineProperty(i,"findIndexAllDeep",{configurable:!1,enumerable:!1,writable:!1,value:e=>{const t=[];return this.some(function r(n){return function(i,o){return i===e&&t.push(n.concat(o)),Array.isArray(i)&&i.some(r(n.concat(o)))}}([])),t}}),e(i,o),t(i,o),Object.defineProperty(i,"push",{configurable:!1,enumerable:!1,writable:!1,value:(...e)=>{let t;return e.forEach((e=>{t=l.length,l.push(e),a(t),r({type:"itemadded",index:t,item:e},i,o)})),l.length}}),Object.defineProperty(i,"pop",{configurable:!1,enumerable:!1,writable:!1,value:()=>{if(l.length>0){const e=l.length-1,t=l.pop();return delete i[e],r({type:"itemremoved",index:e,item:t},i,o),t}}}),Object.defineProperty(i,"unshift",{configurable:!1,enumerable:!1,writable:!1,value:(...e)=>(e.forEach(((e,t)=>{l.splice(t,0,e),a(l.length-1),r({type:"itemadded",index:t,item:e},i,o)})),l.forEach(((t,n)=>{n>=e.length&&r({type:"itemset",index:n,item:t},i,o)})),l.length)}),Object.defineProperty(i,"shift",{configurable:!1,enumerable:!1,writable:!1,value:()=>{if(l.length>0){const e=l.shift();return delete i[l.length],r({type:"itemremoved",index:0,item:e},i,o),e}}}),Object.defineProperty(i,"splice",{configurable:!1,enumerable:!1,writable:!1,value:(...e)=>{const t=[];let n,[u,f,...c]=e;for(u=null==u?0:u<0?l.length+u:u,f=null==f?l.length-u:f>0?f:0;f--;)n=l.splice(u,1)[0],t.push(n),delete i[l.length],r({type:"itemremoved",index:u+t.length-1,item:n},i,o);return c.forEach((e=>{l.splice(u,0,e),a(l.length-1),r({type:"itemadded",index:u,item:e},i,o),u++})),t}}),Object.defineProperty(i,"length",{configurable:!1,enumerable:!1,get:()=>l.length,set:e=>{const t=Number(e),r=l.length;if(!(t%1==0&&t>=0))throw new RangeError("Error: Invalid array length.");t<r?i.splice(t):t>r&&Reflect.apply(i.push,i,new Array(t-r)),l.length=t}}),Object.getOwnPropertyNames(Array.prototype).forEach((e=>{e in i||Object.defineProperty(i,e,{configurable:!1,enumerable:!1,writable:!1,value:Array.prototype[e]})})),n instanceof Array&&Reflect.apply(i.push,i,n)}function i(n){const i=this,o={mutated:[]};"string"==typeof n&&(i[0]=String(n));const l=(e,t)=>{const r=e[0].valueOf();e[0]=String(t);return{value:r,mutant:e[0].valueOf()}};Object.defineProperty(i,"value",{configurable:!1,enumerable:!1,get(){if(i[0])return i[0].valueOf()},set(e){"string"==typeof e&&r(Object.assign({type:"mutated"},l(i,e)),i,o)}}),Object.defineProperty(i,"reassign",{configurable:!1,enumerable:!1,writable:!1,value(e){if("string"!=typeof e)throw new Error("Error: Invalid type.");return r(Object.assign({type:"mutated"},l(i,e)),i,o),i}}),e(i,o),t(i,o),Object.defineProperty(i,"split",{configurable:!1,enumerable:!1,writable:!1,value:e=>i[0].valueOf().split(e)}),Object.defineProperty(i,"length",{configurable:!1,enumerable:!1,get:()=>i[0].length,set(){Function.prototype()}}),Object.getOwnPropertyNames(String.prototype).forEach((e=>{e in i||Object.defineProperty(i,e,{configurable:!1,enumerable:!1,writable:!1,value:(...t)=>i[0][e](...t)})}))}function o(n){const i=Symbol.for("_internal"),o=Symbol.for("_isProxy"),l={itemget:[],itemdeleted:[],itemset:[]},a=["addEventListener","removeEventListener","identifier","type"],u=e=>{if(a.includes(e))throw new Error(`Error: Property '${e}' is non-configurable.`)},f={get(e,t,n){if(t===o)return!0;if(t===i)return e;const u=Reflect.get(e,t,n);return"object"==typeof u?new Proxy(u,f):(u&&!a.includes(t)&&r({type:"itemget",prop:t,target:e,value:u},this,l),u)},set(e,t,n,i){return u(t),r({type:"itemset",prop:t,target:e,value:n},this,l),Reflect.set(e,t,n)},deleteProperty(e,t){u(t);const n=JSON.stringify(e,null,0),i=Reflect.deleteProperty(e,t);return r({type:"itemdeleted",prop:t,target:n,value:i},this,l),i}};if(!(n instanceof Object))return;const c=new Proxy(JSON.parse(JSON.stringify(n)),f);return Object.defineProperty(c,"value",{configurable:!1,enumerable:!1,get:()=>Object.assign({},c[i]),set(e){}}),e(c,l),t(c,l),c}return function(e){const t=function(e,r,n){return new t.init(e,r,n)},r={};return t.prototype={},t.init=function(e,t,l){const a=Object.keys(r);let u;if("Array"===e)u=new n(t);else if("String"===e)u=new i(t);else{if("Object"!==e)throw new Error(`Error: datatype ${e} is not a supported option.`);u=new o(t)}const f=e;let c;if(l){const{uniqueIdentifier:e}=(({id:e})=>({uniqueIdentifier:a.includes(e.toString())?void 0:e}))(l);if(!e)throw new Error(`Error: Identifier ${l.id} is currently in use.`);c=e}else{const{length:e}=a;c=e}return Object.entries({identifier:c,type:f}).forEach((([e,t])=>Object.defineProperty(u,e,{configurable:!1,enumerable:!1,value:t}))),r[c]=u,u},t}()})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Vx=t()}(this,(function(){"use strict";function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){if("undefined"==typeof Symbol||!(Symbol.iterator in Object(e)))return;var r=[],n=!0,i=!1,o=void 0;try{for(var u,a=e[Symbol.iterator]();!(n=(u=a.next()).done)&&(r.push(u.value),!t||r.length!==t);n=!0);}catch(e){i=!0,o=e}finally{try{n||null==a.return||a.return()}finally{if(i)throw o}}return r}(e,t)||function(e,t){if(!e)return;if("string"==typeof e)return r(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return r(e,t)}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r<t;r++)n[r]=e[r];return n}function n(e,t){Object.defineProperty(e,"addEventListener",{configurable:!1,enumerable:!1,writable:!1,value:function(r,n,i){if(!((r=(""+r).toLowerCase())in t))throw new Error("Error: Invalid event name.");if("function"!=typeof n)throw new Error("Error: Invalid handler.");if(i){var o=n.name;n=function(e,t){var r;return function(n){clearTimeout(r),r=setTimeout((function(){return e(n)}),t)}}(n,i),u=n,a=o,Object.defineProperty(u,"name",{value:a,configurable:!0}),n=u}var u,a;return t[r].push(n),e}})}function i(e,t){Object.defineProperty(e,"removeEventListener",{configurable:!1,enumerable:!1,writable:!1,value:function(r,n){if(!((r=(""+r).toLowerCase())in t))throw new Error("Error: Invalid event name.");if("function"!=typeof n)throw new Error("Error: Invalid handler.");for(var i=t[r],o=i.length;--o>=0;)i[o]!==n&&i[o].name!==n.name||i.splice(o,1);return e}})}function o(e,t,r){r[e.type].forEach((function(r){r.call(t,e)}))}function u(e){var t=this,r=this,u={itemadded:[],itemremoved:[],itemset:[],mutated:[]},a=[],f=function(e){e in r||Object.defineProperty(r,e,{configurable:!0,enumerable:!0,get:function(){return a[e]},set:function(t){a[e]=t,o({type:"itemset",index:e,item:t},r,u)}})};Object.defineProperty(r,"value",{configurable:!1,enumerable:!1,get:function(){return a},set:function(e){e instanceof Array&&(a=e,o({type:"mutated",index:"all",item:e},r,u))}}),Object.defineProperty(r,"findIndexAll",{configurable:!1,enumerable:!1,writable:!1,value:function(e){var t=[];return a.forEach((function(r,n){r===e&&t.push(n)})),t}}),Object.defineProperty(r,"findIndexAllDeep",{configurable:!1,enumerable:!1,writable:!1,value:function(e){var r=[];return t.some(function t(n){return function(i,o){return i===e&&r.push(n.concat(o)),Array.isArray(i)&&i.some(t(n.concat(o)))}}([])),r}}),n(r,u),i(r,u),Object.defineProperty(r,"push",{configurable:!1,enumerable:!1,writable:!1,value:function(){for(var e,t=arguments.length,n=new Array(t),i=0;i<t;i++)n[i]=arguments[i];return n.forEach((function(t){e=a.length,a.push(t),f(e),o({type:"itemadded",index:e,item:t},r,u)})),a.length}}),Object.defineProperty(r,"pop",{configurable:!1,enumerable:!1,writable:!1,value:function(){if(a.length>0){var e=a.length-1,t=a.pop();return delete r[e],o({type:"itemremoved",index:e,item:t},r,u),t}}}),Object.defineProperty(r,"unshift",{configurable:!1,enumerable:!1,writable:!1,value:function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];return t.forEach((function(e,t){a.splice(t,0,e),f(a.length-1),o({type:"itemadded",index:t,item:e},r,u)})),a.forEach((function(e,n){n>=t.length&&o({type:"itemset",index:n,item:e},r,u)})),a.length}}),Object.defineProperty(r,"shift",{configurable:!1,enumerable:!1,writable:!1,value:function(){if(a.length>0){var e=a.shift();return delete r[a.length],o({type:"itemremoved",index:0,item:e},r,u),e}}}),Object.defineProperty(r,"splice",{configurable:!1,enumerable:!1,writable:!1,value:function(){for(var e=[],t=arguments.length,n=new Array(t),i=0;i<t;i++)n[i]=arguments[i];var l,c=n[0],p=n[1],y=n.slice(2);for(c=null==c?0:c<0?a.length+c:c,p=null==p?a.length-c:p>0?p:0;p--;)l=a.splice(c,1)[0],e.push(l),delete r[a.length],o({type:"itemremoved",index:c+e.length-1,item:l},r,u);return y.forEach((function(e){a.splice(c,0,e),f(a.length-1),o({type:"itemadded",index:c,item:e},r,u),c++})),e}}),Object.defineProperty(r,"length",{configurable:!1,enumerable:!1,get:function(){return a.length},set:function(e){var t=Number(e),n=a.length;if(!(t%1==0&&t>=0))throw new RangeError("Error: Invalid array length.");t<n?r.splice(t):t>n&&Reflect.apply(r.push,r,new Array(t-n)),a.length=t}}),Object.getOwnPropertyNames(Array.prototype).forEach((function(e){e in r||Object.defineProperty(r,e,{configurable:!1,enumerable:!1,writable:!1,value:Array.prototype[e]})})),e instanceof Array&&Reflect.apply(r.push,r,e)}function a(e){var t=this,r={mutated:[]};"string"==typeof e&&(t[0]=String(e));var u=function(e,t){var r=e[0].valueOf();return e[0]=String(t),{value:r,mutant:e[0].valueOf()}};Object.defineProperty(t,"value",{configurable:!1,enumerable:!1,get:function(){if(t[0])return t[0].valueOf()},set:function(e){"string"==typeof e&&o(Object.assign({type:"mutated"},u(t,e)),t,r)}}),Object.defineProperty(t,"reassign",{configurable:!1,enumerable:!1,writable:!1,value:function(e){if("string"!=typeof e)throw new Error("Error: Invalid type.");return o(Object.assign({type:"mutated"},u(t,e)),t,r),t}}),n(t,r),i(t,r),Object.defineProperty(t,"split",{configurable:!1,enumerable:!1,writable:!1,value:function(e){return t[0].valueOf().split(e)}}),Object.defineProperty(t,"length",{configurable:!1,enumerable:!1,get:function(){return t[0].length},set:function(){Function.prototype()}}),Object.getOwnPropertyNames(String.prototype).forEach((function(e){e in t||Object.defineProperty(t,e,{configurable:!1,enumerable:!1,writable:!1,value:function(){var r;return(r=t[0])[e].apply(r,arguments)}})}))}function f(t){var r=Symbol.for("_internal"),u=Symbol.for("_isProxy"),a={itemget:[],itemdeleted:[],itemset:[]},f=["addEventListener","removeEventListener","identifier","type"],l=function(e){if(f.includes(e))throw new Error("Error: Property '".concat(e,"' is non-configurable."))},c={get:function(t,n,i){if(n===u)return!0;if(n===r)return t;var l=Reflect.get(t,n,i);return"object"===e(l)?new Proxy(l,c):(l&&!f.includes(n)&&o({type:"itemget",prop:n,target:t,value:l},this,a),l)},set:function(e,t,r,n){return l(t),o({type:"itemset",prop:t,target:e,value:r},this,a),Reflect.set(e,t,r)},deleteProperty:function(e,t){l(t);var r=JSON.stringify(e,null,0),n=Reflect.deleteProperty(e,t);return o({type:"itemdeleted",prop:t,target:r,value:n},this,a),n}};if(t instanceof Object){var p=new Proxy(JSON.parse(JSON.stringify(t)),c);return Object.defineProperty(p,"value",{configurable:!1,enumerable:!1,get:function(){return Object.assign({},p[r])},set:function(e){}}),n(p,a),i(p,a),p}}var l,c;return c={},(l=function e(t,r,n){return new e.init(t,r,n)}).prototype={},l.init=function(e,r,n){var i,o=Object.keys(c);if("Array"===e)i=new u(r);else if("String"===e)i=new a(r);else{if("Object"!==e)throw new Error("Error: datatype ".concat(e," is not a supported option."));i=new f(r)}var l,p,y=e;if(n){var b=(p=n.id,{uniqueIdentifier:o.includes(p.toString())?void 0:p}).uniqueIdentifier;if(!b)throw new Error("Error: Identifier ".concat(n.id," is currently in use."));l=b}else l=o.length;return Object.entries({identifier:l,type:y}).forEach((function(e){var r=t(e,2),n=r[0],o=r[1];return Object.defineProperty(i,n,{configurable:!1,enumerable:!1,value:o})})),c[l]=i,i},l})); |
{ | ||
"name": "vivisector", | ||
"version": "1.1.2", | ||
"version": "1.1.3", | ||
"description": "node.js library for creating observable datatypes", | ||
@@ -15,3 +15,2 @@ "main": "./dist/vivisector.cjs.js", | ||
}, | ||
"type": "module", | ||
"files": [ | ||
@@ -21,2 +20,5 @@ "dist/" | ||
"jest": { | ||
"transform": { | ||
"^.+\\.js$": "babel-jest" | ||
}, | ||
"coverageDirectory": "./coverage", | ||
@@ -86,3 +88,3 @@ "collectCoverage": true, | ||
"@babel/cli": "^7.13.0", | ||
"@babel/preset-env": "^7.13.9", | ||
"@babel/preset-env": "7.13.9", | ||
"@commitlint/cli": "^12.0.1", | ||
@@ -93,2 +95,3 @@ "@commitlint/config-conventional": "^12.0.1", | ||
"babel-eslint": "^10.1.0", | ||
"babel-jest": "26.6.3", | ||
"coveralls": "^3.1.0", | ||
@@ -95,0 +98,0 @@ "cz-conventional-changelog": "^3.3.0", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
89910
18
1989
No