UNPKG

@sugarcoated/fondant-binder

Version:
366 lines (308 loc) 9.99 kB
/** * Declaration of the Binder class. Binder allows the trapping * of operation on objects, and listening to those operations. * * @fileoverview Binder. Trap object operations. * @author Joshua Crowe, Junior Web Developer. * @copyright Joshua Crowe, 2017. */ /** * Binder can be extended to bind to different kinds of object, * trapping different events as required. * * @class Binder * @classdesc Trap object operations. */ class Binder { /** * @property accession * @description Events fired upon accession. * @type {Actionable} */ get accession () { return this._binderAccession; } /** * @property augment * @description Events fired upon augmentation. * @type {Actionable} */ get augment () { return this._binderAugment; } /** * @property mutation * @description Events fired upon mutation. * @type {Actionable} */ get mutation () { return this._binderMutation; } /** * @property mution * @description Events fired upon mution. * @type {Actionable} */ get mution () { return this._binderMution; } /** * Create a Binder. * @constructor * * @name Binder#constructor * @example new Binder({one: 1, two: 2}) * @param {Object} binderTarget * @param {Object} [binderHandler] * @param {Function} [binderHandler.get] Getter for the Binder. * @param {Function} [binderHandler.set] Setter for the Binder. * @param {Function} [binderHandler.has] Enumeration for the Binder. * @param {Function} [binderHandler.deleteProperty] Deletion for the Binder. * @returns {Binder} */ constructor (binderTarget, binderHandler = {}) { /** * @property _binderTarget * @description Target to proxy. * @type {Object} */ this._binderTarget = binderTarget; /** * @const binderKeys * @description Object keys of the target. * @type {Array.<String>} */ const binderKeys = Object.keys(binderTarget); /** * @property _binderAccession * @description Events to fire uponb accession. * @type {Actionable} */ this._binderAccession = new Actionable(binderKeys); /** * @property _binderAugment * @description Events to fire upon augmentation. * @type {Actionable} */ this._binderAugment = new Actionable(binderKeys); /** * @property _binderMutation * @description Events to fire upon mutation. * @type {Actionable} */ this._binderMutation = new Actionable(binderKeys); /** * @property _binderMution * @description Events to fire upon mution. * @type {Actionable} */ this._binderMution = new Actionable(binderKeys); if (!('get' in binderHandler)) { binderHandler.get = (binderTarget, binderProperty) => { /** * @const binderAccession * @description Object reference to the Binder's accession events. * @type {Actionable} */ const binderAccession = this._binderAccession; /** * @const binderValue * @description Value of the property getting. * @type {Actionable} */ const binderValue = binderTarget[binderProperty]; binderAccession.run(binderProperty, binderValue); return binderValue; } } if (!('set' in binderHandler)) { binderHandler.set = (binderTarget, binderProperty, binderValue) => { /** * @const binderAugment * @description Object reference to the Binder's augmentation events. * @type {Actionable} */ const binderAugment = this._binderAugment; binderTarget[binderProperty] = binderValue; binderAugment.run(binderProperty, binderValue); return true; } } if (!('has' in binderHandler)) { binderHandler.has = (binderTarget, binderProperty) => { if (binderProperty in binderTarget) { /** * @const binderAccession * @description Object reference to the Binder's accession events. * @type {Actionable} */ const binderAccession = this._binderAccession; /** * @const binderValue * @description Value of accessed property. * @type {Object} */ const binderValue = binderTarget[binderProperty]; binderAccession.run(binderProperty, binderValue); } return (binderProperty in binderTarget); } } if (!('deleteProperty' in binderHandler)) { binderHandler.deleteProperty = (binderTarget, binderProperty) => { /** * @const binderMution * @description Object reference to the Binder's mution events. * @type {Actionable} */ const binderMution = this._binderMution; /** * @const binderValue * @description Value of the deleted property. * @type {Object} */ const binderValue = binderTarget[binderProperty]; if (binderProperty.startsWith('_')) { throw Error(`Cannot delete private keys.`); } delete binderTarget[binderProperty]; return true; } } /** * @property _binderProxy * @description Proxy object for the target. * @type {Proxy} */ this._binderProxy = new Proxy(binderTarget, binderHandler); } /** * Change a property on the bound target. * * @name Binder#property * @example myBinder.property('myProperty', 1) * @param {String} propertyName Name of the property. * @param {Object} [propertyValue] New value of the property. * @returns {Object} Value of the property. */ property (propertyName, propertyValue) { /** * @const propertyProxy * @description Object reference to the target proxy. * @type {Proxy} */ const propertyProxy = this._binderProxy; if (propertyValue) { propertyProxy[propertyName] = propertyValue; } return propertyProxy[propertyName]; } /** * Remove a property from the bound target. * * @name Binder#remove * @example myBinder.remove('myProperty') * @param {String} removeProperty Name of the property to remove. * @returns {Object} Value of property before removal. */ remove (removeProperty) { /** * @const removeProxy * @description Object reference to thee target proxy. * @type {Proxy} */ const removeProxy = this._binderProxy; /** * @const removeValue * @description Value of the property being removed. * @type {Object} */ const removeValue = removeProxy[removeProperty]; if (removeProperty) delete removeProxy[removeProperty]; return removeValue; } /** * Fire an event on accession of a property. * * @name Binder#accessed * @example myBinder.accessed('myProperty', (x) => console.log(x)) * @param {String} accessedProperty Property to fire event on access of. * @param {Function} accessHandler Function to call on property accession. * @returns {Void} */ accessed (accessedProperty, accessHandler) { /** * @const accessedTarget * @description Object reference to the core target. * @type {Object} */ const accessedTarget = this._binderTarget; /** * @const accessedEvents * @description Object reference to the accession events. * @type {Actionable} */ const accessedEvents = this._binderAccession; if (accessedProperty in accessedTarget) { accessedEvents.on(accessedProperty, accessHandler); } else { throw new TypeError(`'${accessedProperty}' does not exist.`); } } /** * Fire an event on augmentation of a property. * * @name Binder#augment * @example myBinder.augment('myProperty', (x) => console.log(x)) * @param {String} augmentedProperty Property to fire event on augment of. * @param {Function} augmentedHandler Function to call on property augmentation. * @returns {Void} */ augmented (augmentedProperty, augmentedHandler) { /** * @const augmentedTargets * @description Object reference to the core target. * @type {Object} */ const augmentedTarget = this._binderTarget; /** * @const augmentedEvents * @description Object reference to the augmentation events. * @type {Actionable} */ const augmentedEvents = this._binderAugment; if (augmentedProperty in augmentedTarget) { augmentedEvents.on(augmentedProperty, augmentedHandler); } else { throw new TypeError(`'${augmentedProperty}' does not exist.`); } } /** * Fire an even on mute of a property. * * @name Binder#muted * @example myBinder.muted('myProperty', (x) => console.log(x)) * @param {String} mutedProperty Property to fire event on mute of. * @param {Function} mutedHandler Function to call on property mute. * @returns {Void} */ muted (mutedProperty, mutedHandler) { /** * @const mutedTargets * @description Object reference to core target. * @type {Object} */ const mutedTarget = this._binderTarget; /** * @const mutedEvents * @description Object reference to the mute events. * @type {Actionable} */ const mutedEvents = this._binderMution; if (mutedProperty in mutedTarget) { mutedEvents.on(mutedProperty, mutedHandler); } else { throw new TypeError(`'${mutedProperty}' does not exist.`); } } }