/******************************************************************************
* Component implementation
* ***************************************************************************/
/**
* Defines main traits of components in a namespace regrouping important methods
*
* @namespace
*/
ARCS.Component = {
/** Error message */
SourceIsNotComponent : {message : "Source is not a component"},
/** Error message */
UndefinedSignal : {message : "Signal is not defined"},
/** Error message */
UndefinedSlot : {message : "Slot is not defined"},
/**
* External constructor: give component traits to any constructor.
*
* Component traits are the following:
* <ul>
* <li>Slot functions listed in an array;</li>
* <li>A signal list described in an array;</li>
* <li>A method returning the slot list;</li>
* <li>A method returnung the signal list;</li>
* <li>An emit method, to trigger signals by their names;</li>
* <li>A slot method to cast an internal method to a slot;</li>
* <li>A signal mehtod to register a possible signal.</li>
* </ul>
* @param name {string} Class name to transform to a component
* @param sltList {string[]} names of functions designated as slots, may be empty.
* @param sgnList {string[]} names of functions designated as signals, may be empty.
*/
create : function (name, sltList, sgnList) {
if (name.prototype === undefined) {
console.error("Cannot create such a component");
return 0;
}
name.prototype.slots = [];
name.prototype.signals = {};
name.slotList = function () {
return name.prototype.slots;
};
name.prototype.slotList = function () {
return name.prototype.slots;
};
name.prototype.signalList = function () {
var res = [], i;
for (i in name.prototype.signals) {
res.push(i);
}
return res;
};
name.signalList = function () {
return name.prototype.signalList();
};
name.prototype.emit = function (signal) {
var slt, func, obj;
var args = Array.prototype.slice.call(arguments,1);
for (slt in this.signals[signal]) {
func = this.signals[signal][slt].func;
obj = this.signals[signal][slt].obj;
func.apply(obj, args);
}
};
name.slot = function (slot, func) {
var i;
if (slot instanceof Array) {
for (i = 0; i < slot.length; i++) {
name.prototype.slots.push(slot[i]);
}
} else {
name.prototype.slots.push(slot);
if (func !== undefined) {
name.prototype[slot] = func;
}
}
};
name.signal = function (signal) {
var i;
if (signal instanceof Array) {
for (i = 0; i < signal.length; i++) {
name.prototype.signals[signal[i]] = 1;
}
} else {
name.prototype.signals[signal] = 1;
}
};
// code for returning component, and or completing its definition
if (sltList !== undefined) {
name.slot(sltList);
}
if (sgnList !== undefined) {
name.signal(sgnList);
}
return name;
},
/**
* Checks if the given prototype has traits of a component
* @param name {string} name of the prototype
*/
check : function (name) {
if (name.prototype === undefined) {
return false;
}
if (name.prototype.signals === undefined ||
name.prototype.slots === undefined) {
return false;
}
return true;
},
/**
* Connects two different components by using their signal and slots
* @param source {object} component sending data
* @param signal {string} name of the signal to connect
* @param destination {object} component receiving data
* @param slt {string} name of the slot to connect
*/
connect : function (source, signal, destination, slt) {
var orig, p;
// here we can perform various checks.
if (source.signals === undefined) {
throw ARCS.Component.SourceIsNotComponent;
}
if (source.signals[signal] === undefined) {
throw ARCS.Component.UndefinedSignal;
}
if (destination[slt] === undefined) {
throw ARCS.Component.UndefinedSlot;
}
// we must also check if the signals dispose of their own implementation
if (!source.hasOwnProperty('signals')) {
// otherwise, we should clone it so that each component dispose of its
// own signal copy.
orig = source.signals;
source.signals = {};
for (p in orig) {
source.signals[p] = [];
}
}
source.signals[signal].push({obj: destination, func: destination[slt]});
},
/**
* Diconnects a signal/slot connection between two components
* @param source {object} component sending data
* @param signal {string} name of the signal to connect
* @param destination {object} component receiving data
* @param slt {string} name of the slot to connect
*/
disconnect : function (source, signal, destination, slt) {
var i;
for (i = 0; i < source.signals[signal].length; i++) {
if (source.signals[signal][i].obj === destination) {
if (source.signals[signal][i].func === destination[slt]) {
source.signals[signal].splice(i, 1);
i--;
}
}
}
},
/**
* Invokes a specific slot of a given component
* @param destination {object} component upon which invocation is performed
* @param slt {string} name of the slot to invoke
* @param value {mixed} value to input
*/
invoke : function (destination, slt, value) {
var func = destination[slt];
func.apply(destination, value);
},
/**
* Specific hook that can be called when initializing a component
* @param component {object} prototype of the component
* @param obj {object} the actual object
*/
config : function (component, obj) {
if (typeof component.config === 'function') {
component.config(obj);
}
}
};