component.js
6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/******************************************************************************
* Component implementation
* ***************************************************************************/
/**
* Defines main traits of components in a namespace regrouping important methods
*
* @namespace
*/
var 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 Component.SourceIsNotComponent;
}
if (source.signals[signal] === undefined) {
throw Component.UndefinedSignal;
}
if (destination[slt] === undefined) {
throw 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) {
if (destination[slt] === undefined) {
throw Component.UndefinedSlot;
}
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);
}
}
};
export default { Component: Component};