/**
* 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;
}
};