/** * A component * @constructor * @param {Object} options * @param {Element} options.element * @param {String} options.name * @param {Object} options.listen A listener */ hui.ui.Component = function(options) { options = options || {}; this.name = options.name; this.state = hui.override({}, this.state); if (!this.name) { hui.ui.latestObjectIndex++; this.name = 'unnamed'+hui.ui.latestObjectIndex; } this.element = hui.get(options.element); this.delegates = []; if (this.nodes) { this.nodes = hui.collect(this.nodes,this.element); } else { this.nodes = []; } this.nodes.root = this.element if (options.listen) { this.listen(options.listen); } hui.ui.registerComponent(this); }; hui.ui.Component.prototype = { /** * Add event listener * @param {Object} listener An object with methods for different events */ listen : function(listener) { this.delegates.push(listener); }, on : function(listener) { this.delegates.push(listener); }, /** * Fire an event with this component as the sender */ fire : function(name,value,event) { return hui.ui.callDelegates(this,name,value,event); }, /** * Get the components root element * @returns Element */ getElement : function() { return this.element; }, /** * Removes the component from the DOM * @see hui.ui.destroy */ destroy : function() { hui.ui.destroy(this) }, detach : function() { // TODO: Can we auto-remove all listeners // Override this }, valueForProperty : function(property) { return this[property]; }, /** * Notify others of a value change */ fireValueChange : function() { this.fire('valueChanged',this.value); hui.ui.firePropertyChange(this,'value',this.value); hui.ui.callAncestors(this,'childValueChanged',this.value); }, /** * Notify others of a layout change */ fireSizeChange : function() { hui.ui.callAncestors(this,'$$childSizeChanged'); }, change : function(newState) { var changed = {}; for (key in newState) { if (newState.hasOwnProperty(key) && this.state.hasOwnProperty(key) ) { if (this.state[key] !== newState[key]) { this.state[key] = newState[key]; changed[key] = newState[key]; } } } if (changed!={} && this.draw) { this.draw(changed); } } }; hui.component = function(name, spec) { hui.ui[name] = function(options) { options = options || {}; hui.ui.Component.call(this, options); this.init && this.init(options); this.change(options); this.attach && this.attach(); for (key in this) { if (key[0] == '!' && typeof(this[key]) == 'function') { (function(self, key) { hui.listen(self.element, key.substr(1), function(e) { e = hui.event(e); self[key](e); }); })(this, key) } } } var component = hui.ui[name] component.create = function(state) { var cp = {element: spec.create()}; hui.extend(cp, state); if (state.testName) { cp.element.setAttribute('data-test', state.testName); } var obj = new component(cp); obj.change(state); return obj; }; component.prototype = spec; hui.extend(component.prototype, hui.ui.Component.prototype); if (spec['with']) { for (var i = 0; i < spec['with'].length; i++) { var mixin = hui.component[spec['with'][i]]; for (prop in mixin) { if (typeof(mixin[prop]) == 'function') { component.prototype[prop] = mixin[prop] } } if (mixin.state) { for (prop in mixin.state) { if (component.prototype.state[prop] === undefined) { component.prototype.state[prop] = mixin.state[prop] } } } } } }; hui.component.value = { state: {value: undefined}, setValue : function(v) { this.change({value: v}); }, getValue : function() { return this.state.value; }, /* TODO: Already a fireValueChange */ tellValueChange : function(){ var v = this.state.value; hui.ui.callAncestors(this, 'childValueChanged', v); this.fire('valueChanged', v); hui.ui.firePropertyChange(this, 'value', v); } }; hui.component.enabled = { state: {enabled: true}, setEnabled : function(v) { this.change({enabled: !!v}); }, isEnabled : function() { return !!this.state.enabled; } }; hui.component.key = { state: {key: undefined}, getKey: function() { return this.state.key; } };