;(function() { "use strict"; // Utility class to enable extension of objects that cannot use // normal class based inheritance e.g. custom functions // // Simply put, it adds a mixin static that contains all the property // descriptors that class intends to pass on class Mixable { static mixin(target) { if (!this.hasOwnProperty("mixin")) { this.mixin = Object.assign( Mixable.mixin.bind(this), this.mixin, Object.getOwnPropertyDescriptors(this.prototype) ); } return Object.defineProperties(target, this.mixin); } } // Creates basic value storage functions class BoxedValue extends Mixable { static create(currValue) { return this.init(function boxedValue(nextValue) { return arguments.length > 0 ? (currValue = nextValue) : currValue; }); } static init(box) { return this.mixin(box); } } // Building on BoxedValue this includes the concepts for using boxes as properties // Since a box is just a function that takes 0 or 1 arguments this uses the box // as both the setter and the getter for a property class BoxedProperty extends BoxedValue { // The box itself is also a property-descriptor which can be used in // Object.defineProperty etc. static init(box) { return Object.assign(super.init(box), { enumerable : true, configurable : true, get : box, set : box }); } // defines an own property on target(arg1) of name(arg2) which uses // the box(arg3) as both the setter and the getter // Ref: https://www.ecma-international.org/ecma-262/9.0/index.html#table-3 static define(target, name, box = this.create()) { if (!(box instanceof BoxedProperty)) { this.init(box); } Object.defineProperty(target, name, box); return box; } // This will look-up a BoxedProperty from a getter property of an object static get(target, name) { const { get } = Object.getOwnPropertyDescriptor(target, name) || false; if (get instanceof BoxedProperty) { return get; } } // This will either return an existing BoxedProperty of an object or define one static ensure(target, name, box) { return this.get(...arguments) || this.define(...arguments); } // Link two BoxedProperties from the same or different objects static link(source, name, target, targetName = name) { return this.ensure(source, name).attach(target, targetName); } // Attach a boxed property to an object attach(target, name) { return this.constructor.define(target, name, this); } } Object.assign( typeof module !== "undefined" ? module["exports"] : window, { Mixable, BoxedValue, BoxedProperty } ); }());