/** * The main base class. Manages the signal system. Objects based on this class * can be instantiated declaratively using XML. */ module.exports = class VnObject { /** * Tag to be used when the class instance is defined via XML. All classes * must define this attribute, even if it is not used. */ static Tag = 'vn-object'; /** * Class public properties. */ static Properties = {}; /* * Reference count. */ _refCount = 1; /* * Signal handlers data. */ _thisArg = null; /** * Initializes the object and sets all properties passed to the class * constructor. * * @param {Object} props The properties passed to the contructor */ constructor(props) { this.setProperties(props); } initialize(props) { this.setProperties(props); } /** * Sets a group of object properties. * * @param {Object} props Properties */ setProperties(props) { for (var prop in props) this[prop] = props[prop]; } /** * Increases the object reference count. */ ref() { this._refCount++; return this; } /** * Decreases the object reference count. */ unref() { this._refCount--; if (this._refCount === 0) this._destroy(); } /** * Called from @Vn.Builder when it finds a custom tag as a child of the * element. * * @param {Vn.Scope} scope The scope instance * @param {Node} node The custom tag child nodes */ loadXml() {} /** * Called from @Vn.Builder when it finds a a child tag that isn't * associated to any property. * * @param {Object} child The child object instance */ appendChild() {} /** * Conects a signal with a function. * * @param {string} id The signal identifier * @param {function} callback The callback * @param {Object} instance The instance */ on(id, callback, instance) { if (!(callback instanceof Function)) { console.warn('Vn.Object: Invalid callback for signal \'%s\'', id); return; } this._signalInit(); var callbacks = this._thisArg.signals[id]; if (!callbacks) callbacks = this._thisArg.signals[id] = []; callbacks.push({ blocked: false ,callback: callback ,instance: instance }); } /** * Locks/Unlocks a signal emission to the specified object. * * @param {string} id The signal identifier * @param {function} callback The callback * @param {boolean} block %true for lock the signal, %false for unlock */ blockSignal(id, callback, block, instance) { if (!this._thisArg) return; var callbacks = this._thisArg.signals[id]; if (!callbacks) return; for (var i = 0; i < callbacks.length; i++) if (callbacks[i].callback == callback && callbacks[i].instance == instance) callbacks[i].blocked = block; } /** * Emits a signal in the object. * * @param {string} id The signal identifier */ emit(id) { if (!this._thisArg) return; var callbacks = this._thisArg.signals[id]; if (!callbacks) return; var callbackArgs = []; callbackArgs.push(this); for (var i = 1; i < arguments.length; i++) callbackArgs.push(arguments[i]); for (var i = 0; i < callbacks.length; i++) if (!callbacks[i].blocked) callbacks[i].callback.apply(callbacks[i].instance, callbackArgs); } /** * Disconnects a signal from current object. * * @param {string} id The signal identifier * @param {function} callback The connected callback * @param {Object} instance The instance */ disconnect(id, callback, instance) { if (!this._thisArg) return; var callbacks = this._thisArg.signals[id]; if (callbacks) for (var i = callbacks.length; i--;) if (callbacks[i].callback === callback && callbacks[i].instance === instance) callbacks.splice(i, 1); } /** * Disconnects all signals for the given instance. * * @param {Object} instance The instance */ disconnectByInstance(instance) { if (!this._thisArg) return; var signals = this._thisArg.signals; for (var signalId in signals) { var callbacks = signals[signalId]; if (callbacks) for (var i = callbacks.length; i--;) if (callbacks[i].instance === instance) callbacks.splice(i, 1); } } /** * Destroys the object, this method should only be called before losing * the last reference to the object. It can be overwritten by child classes * but should always call the parent method. */ _destroy() { if (!this._thisArg) return; var links = this._thisArg.links; for (var key in links) this._unlink(links[key]); this._thisArg = null; } /** * Links the object with another object. * * @param {Object} prop The linked property * @param {Object} handlers The object events to listen with */ link(prop, handlers) { this._signalInit(); var links = this._thisArg.links; for (var key in prop) { var newObject = prop[key]; var oldObject = this[key]; if (oldObject) this._unlink(oldObject); this[key] = newObject; if (newObject) { links[key] = newObject.ref(); for (var signal in handlers) newObject.on(signal, handlers[signal], this); } else if (oldObject) links[key] = undefined; } } _unlink(object) { object.disconnectByInstance(this); object.unref(); } _signalInit() { if (!this._thisArg) this._thisArg = { signals: {}, links: {} }; } }