UNPKG

quasi-

Version:
154 lines (150 loc) 5.41 kB
/** * Invokes a function with the given arguments if it's a function * @param {function} Function? - Function? to be invoked. * @param Arguments - Arguments for Function? to be invoked with. */ const ifFunctionInvoke = (fn, ...args) => { if (typeof fn === 'function') { return fn(...args); } else { return false; } } /** * Clones something, removing reference to original object. * @param something - Object to clone. */ const makeClone = (thing) => { if(Array.isArray(thing)) { return thing.slice(0); } else if (typeof thing === 'object') { return Object.assign({}, thing); } else if (typeof thing === 'function') { return thing.bind(); } else { return JSON.parse(JSON.stringify(thing)); } }; class mockFunc extends Function{ constructor(primitive) { super(); this._ = primitive; } }; /** * Creates a quasi-primitive * @param thing - Thing to create a quasi from. Can be an array, object, number, boolean, string, etc. */ const quasi = (primitiveVar) => { //Variables enclosed for internal use. let __onChange = []; let __onRetrieve = []; let simpleAccess = false; let __applyFunc; let __constructor; let __functionCalls = {}; return new Proxy(new mockFunc(primitiveVar), { get: (target, prop, receiver) => { if (prop === 'on') { return (event, cb) => { let index; if(typeof cb !== 'function') { throw new Error(`Callback of must be a Function`); } if (event.toLowerCase() === 'change') { index = __onChange.push(cb); } else if (event.toLowerCase() === 'retrieve') { index = __onRetrieve.push(cb); } else if (event.toLowerCase() === 'call') { __applyFunc = cb; index = 1; } else if (event.toLowerCase() === 'new') { __constructor = cb; index = 1; } else if (typeof target._[event] === 'function') { __functionCalls[event] = Array.isArray(__functionCalls[event]) ? __functionCalls[event] : []; index = __functionCalls[event].push(cb); } return index; } } else { __onRetrieve.forEach((callback) => { ifFunctionInvoke(callback, target[prop], target._, prop, receiver); }); } if (typeof target._[prop] === 'function' && Array.isArray(__functionCalls[prop])) { __functionCalls[prop].forEach((callback) => { ifFunctionInvoke(callback, target._[prop], target._, prop, receiver); }); } if (simpleAccess && prop.toString() === 'Symbol(Symbol.toPrimitive)') { return () => target._; } else if (prop === '_') { return target._; } else if (typeof target._[prop] === 'function') { let prev = makeClone(target._); return (...args) => { let newVal = target._[prop](...args); if(target._.toString() !== prev.toString()) { __onChange.forEach((callback) => { ifFunctionInvoke(callback, newVal, target[prop], prop, target) }); } return newVal; } } else { return target._[prop]; } }, set: (target, prop, newVal) => { __onChange.forEach((callback) => { ifFunctionInvoke(callback, newVal, target[prop], prop, target) }); if(prop === 'simpleAccess') { //Special case where the user has interfaced with module //and wants to do things which could lead to worse behaviour but simpler reading. return simpleAccess = newVal; } else if (prop !== '_' && typeof target._ !== 'object') { //Case where trying to set property of proxy, but should only work if the object s throw new Error("Can't define property of quasi-primitive"); } else if (prop === '_') { //Case where trying to change the entire stored value return target[prop] = newVal; } else { //Case where stored is an object, and trying to set a propert on it. return target._[prop] = newVal; } // target[prop] = newVal; }, //Apply is here in case the user wants to set up convenient calling for a function that might be used a lot, //or the primitive is a function. apply: (target, thisArg, argumentsList) => { if (typeof __applyFunc === 'function') { return __applyFunc.bind(thisArg, ...argumentsList)(); } else if (typeof target._ === 'function') { return target._.bind(thisArg, ...argumentsList)(); } else if (__applyFunc === undefined) { throw new Error('No function set on quasi: set using quasi.on("call", functionToBeCalled)'); } else { throw new Error(`Unexpected value for __applyFunc, should be of type function, was of type ${typeof __applyFunc}`); } }, has: (target, prop) => { if(prop === 'on' || prop === '_') { return true; } else { return target._[prop] !== undefined; } }, construct: (target, argumentsList, newTarget) => { let constructor = __constructor !== undefined ? __constructor : target._; try { return new constructor(...argumentsList); } catch(e) { e.message = e.message + " <= regardless, the quasi you passed might not be a constructor"; throw e; } } }); }; module.exports = quasi;