@sugarcoated/fondant-binder
Version:
Bind to an object.
366 lines (308 loc) • 9.99 kB
JavaScript
/**
* 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.`);
}
}
}