1 |
// Could be refactored to a Joose.Class (by manually building the class) |
2 |
|
3 |
/** |
4 |
* Assorted tools to build a class |
5 |
* |
6 |
* The functions Class(), Module() and joosify() are global. All other methods |
7 |
* may be used inside Class definitons like this: |
8 |
* |
9 |
* <pre> |
10 |
* Module("com.test.me", function () { |
11 |
* Class("MyClass", { |
12 |
* isa: SuperClass, |
13 |
* methods: { |
14 |
* hello: function () { alert('world') } |
15 |
* } |
16 |
* }) |
17 |
* }) |
18 |
* </pre> |
19 |
* @constructor |
20 |
*/ |
21 |
Joose.Builder = function () { |
22 |
/** @ignore */ |
23 |
this.globalize = function () { |
24 |
Joose.O.each(Joose.Builder.Globals, function (func, name) { |
25 |
joose.top[name] = func |
26 |
}); |
27 |
} |
28 |
} |
29 |
|
30 |
/** @ignore */ |
31 |
Joose.Builder.Globals = { |
32 |
/** |
33 |
* Global function that creates or extends a module |
34 |
* @function |
35 |
* @param name {string} Name of the module |
36 |
* @param functionThatCreatesClassesAndRoles {function} Pass a function reference that calls Class(...) as often as you want. The created classes will be put into the module |
37 |
* @name Module |
38 |
*/ |
39 |
/** @ignore */ |
40 |
Module: function (name, functionThatCreatesClassesAndRoles) { |
41 |
return Joose.Module.setup(name, functionThatCreatesClassesAndRoles) |
42 |
}, |
43 |
|
44 |
Role: function (name, props) { |
45 |
if(!props.meta) { |
46 |
props.meta = Joose.Role; |
47 |
} |
48 |
return Class(name, props) |
49 |
}, |
50 |
|
51 |
Prototype: function (name, props) { |
52 |
if(!props.meta) { |
53 |
props.meta = Joose.Prototype; |
54 |
} |
55 |
return Class(name, props); |
56 |
}, |
57 |
|
58 |
/** |
59 |
* Global function that creates a class (If the class already exists it will be extended) |
60 |
* @function |
61 |
* @param name {string} Name of the the class |
62 |
* @param props {object} Declaration if the class. The object keys are used as builder methods. The values are passed as arguments to the builder methods. |
63 |
* @name Class |
64 |
*/ |
65 |
/** @ignore */ |
66 |
Class: function (name, props) { |
67 |
|
68 |
var c = null; |
69 |
|
70 |
if(name) { |
71 |
var className = name; |
72 |
if(joose.currentModule) { |
73 |
className = joose.currentModule.getName() + "." + name |
74 |
} |
75 |
var root = joose.top; |
76 |
var parts = className.split(".") |
77 |
|
78 |
for(var i = 0; i < parts.length; i++) { |
79 |
root = root[parts[i]] |
80 |
} |
81 |
c = root; |
82 |
} |
83 |
|
84 |
if(c == null) { |
85 |
|
86 |
var metaClass; |
87 |
|
88 |
/* Use the custom meta class if provided */ |
89 |
if(props && props.meta) { |
90 |
metaClass = props.meta |
91 |
delete props.meta |
92 |
} |
93 |
/* Otherwise use the meta class of the parent class (If there is one) |
94 |
* If the parent class is Joose.Class, we don't change the meta class but use the default |
95 |
* because that Joose.Class's meta class is only needed for bootstrapping |
96 |
* purposes. */ |
97 |
else if(props && props.isa && props.isa != Joose.Class) { |
98 |
metaClass = props.isa.meta.builder |
99 |
//alert(name + metaClass + props.isa.meta) |
100 |
} |
101 |
/* Default meta class is Joose.Class */ |
102 |
else { |
103 |
metaClass = Joose.Class; |
104 |
} |
105 |
|
106 |
var aClass = new metaClass(); |
107 |
|
108 |
aClass.builder = metaClass; |
109 |
|
110 |
var c = aClass.createClass(name, null, joose.currentModule) |
111 |
|
112 |
c.meta.builder = metaClass |
113 |
|
114 |
var className = c.meta.className() |
115 |
|
116 |
if(name && className) { |
117 |
var root = joose.top; |
118 |
var n = new String(className); |
119 |
var parts = n.split("."); |
120 |
for(var i = 0; i < parts.length - 1; i++) { |
121 |
if(root[parts[i]] == null) { |
122 |
root[parts[i]] = {}; |
123 |
} |
124 |
root = root[parts[i]]; |
125 |
} |
126 |
root[parts[parts.length - 1]] = c |
127 |
} |
128 |
|
129 |
} |
130 |
joose.cc = c; |
131 |
if(props) { |
132 |
Joose.O.each(props, function (value, name) { |
133 |
var builder = Joose.Builder.Builders[name]; |
134 |
if(!builder) { |
135 |
throw new Error("Called invalid builder "+name+" while creating class "+c.meta.className()) |
136 |
} |
137 |
var paras = value; |
138 |
//if(! (paras instanceof Array )) { |
139 |
// paras = [value] |
140 |
//} |
141 |
builder.call(Joose.Builder, paras) |
142 |
}) |
143 |
|
144 |
c.meta.validateClass() |
145 |
|
146 |
c.meta.buildComplete() |
147 |
} |
148 |
|
149 |
return c |
150 |
}, |
151 |
|
152 |
Type: function (name, props) { |
153 |
var t = Joose.TypeConstraint.newFromTypeBuilder(name, props); |
154 |
|
155 |
var m = joose.currentModule |
156 |
|
157 |
if(!m) { |
158 |
Module("TYPE") |
159 |
m = TYPE.meta; |
160 |
} |
161 |
|
162 |
m.addElement(t) |
163 |
m.getContainer()[name] = t; |
164 |
return t |
165 |
}, |
166 |
|
167 |
/** |
168 |
* Global function to turn a regular JavaScript constructor into a Joose.Class |
169 |
* @function |
170 |
* @param name {string} Name of the class |
171 |
* @param props {function} The constructor |
172 |
* @name joosify |
173 |
*/ |
174 |
/** @ignore */ |
175 |
joosify: function (standardClassName, standardClassObject) { |
176 |
var c = standardClassObject; |
177 |
var metaClass = new Joose.Class(); |
178 |
metaClass.builder = Joose.Class; |
179 |
|
180 |
c.toString = function () { return joose.cc.meta.className() } |
181 |
c = metaClass.createClass(standardClassName, c) |
182 |
|
183 |
var meta = c.meta; |
184 |
|
185 |
for(var name in standardClassObject.prototype) { |
186 |
if(name == "meta") { |
187 |
continue |
188 |
} |
189 |
var value = standardClassObject.prototype[name] |
190 |
if(typeof(value) == "function") { |
191 |
meta.addMethod(name, value) |
192 |
} else { |
193 |
var props = {}; |
194 |
if(typeof(value) != "undefined") { |
195 |
props.init = value |
196 |
} |
197 |
meta.addAttribute(name, props) |
198 |
} |
199 |
} |
200 |
|
201 |
return c |
202 |
}, |
203 |
|
204 |
/** @ignore */ |
205 |
rw: "rw", |
206 |
/** @ignore */ |
207 |
ro: "ro" |
208 |
}; |
209 |
Joose.Builder.Builders = { |
210 |
|
211 |
isAbstract: function (bool) { |
212 |
joose.cc.meta.isAbstract = bool |
213 |
}, |
214 |
|
215 |
/** |
216 |
* Tells a role that the method name must be implemented by all classes that implement joose.cc role |
217 |
* @function |
218 |
* @param methodName {string} Name of the required method name |
219 |
* @name requires |
220 |
* @memberof Joose.Builder |
221 |
*/ |
222 |
/** @ignore */ |
223 |
requires: function (methodName) { |
224 |
if(!joose.cc.meta.meta.isa(Joose.Role)) { // XXX should joose.cc be does? |
225 |
throw("Keyword 'requires' only available classes with a meta class of type Joose.Role") |
226 |
} |
227 |
if(methodName instanceof Array) { |
228 |
Joose.A.each(methodName, function (name) { |
229 |
joose.cc.meta.addRequirement(name) |
230 |
}) |
231 |
} else { |
232 |
joose.cc.meta.addRequirement(methodName) |
233 |
} |
234 |
}, |
235 |
|
236 |
/** |
237 |
* Class builder method |
238 |
* Defines the super class of the class |
239 |
* @function |
240 |
* @param classObject {Joose.Class} The super class |
241 |
* @name isa |
242 |
* @memberof Joose.Builder |
243 |
*/ |
244 |
/** @ignore */ |
245 |
isa: function (classObject) { |
246 |
joose.cc.meta.addSuperClass(classObject) |
247 |
}, |
248 |
/** |
249 |
* Class builder method |
250 |
* Defines a role for the class |
251 |
* @function |
252 |
* @param classObject {Joose.Role} The role |
253 |
* @name does |
254 |
* @memberof Joose.Builder |
255 |
*/ |
256 |
/** @ignore */ |
257 |
does: function (role) { |
258 |
if(role instanceof Array) { |
259 |
Joose.A.each(role, function (aRole) { |
260 |
joose.cc.meta.addRole(aRole) |
261 |
}) |
262 |
} else { |
263 |
joose.cc.meta.addRole(role) |
264 |
} |
265 |
|
266 |
}, |
267 |
|
268 |
/** |
269 |
* Class builder method |
270 |
* Defines attributes for the class |
271 |
* @function |
272 |
* @param classObject {object} Maps attribute names to properties (See Joose.Attribute) |
273 |
* @name has |
274 |
* @memberof Joose.Builder |
275 |
*/ |
276 |
/** @ignore */ |
277 |
has: function (map) { |
278 |
if(typeof map == "string") { |
279 |
var name = arguments[0]; |
280 |
var props = arguments[1]; |
281 |
joose.cc.meta.addAttribute(name, props) |
282 |
} else { // name is a map |
283 |
var me = joose.cc; |
284 |
Joose.O.each(map, function (props, name) { |
285 |
me.meta.addAttribute(name, props) |
286 |
}) |
287 |
} |
288 |
}, |
289 |
|
290 |
/** |
291 |
* @ignore |
292 |
*/ |
293 |
method: function (name, func, props) { |
294 |
joose.cc.meta.addMethod(name, func, props) |
295 |
}, |
296 |
|
297 |
/** |
298 |
* Class builder method |
299 |
* Defines methods for the class |
300 |
* @function |
301 |
* @param classObject {object} Maps method names to function bodies |
302 |
* @name methods |
303 |
* @memberof Joose.Builder |
304 |
*/ |
305 |
/** @ignore */ |
306 |
methods: function (map) { |
307 |
var me = joose.cc |
308 |
Joose.O.each(map, function (func, name) { |
309 |
me.meta.addMethod(name, func) |
310 |
}) |
311 |
}, |
312 |
|
313 |
/** |
314 |
* Class builder method |
315 |
* Defines class methods for the class |
316 |
* @function |
317 |
* @param classObject {object} Maps class method names to function bodies |
318 |
* @name classMethods |
319 |
* @memberof Joose.Builder |
320 |
*/ |
321 |
/** @ignore */ |
322 |
classMethods: function (map) { |
323 |
var me = joose.cc |
324 |
Joose.O.each(map, function (func, name2) { |
325 |
me.meta.addMethodObject(new Joose.ClassMethod(name2, func)) |
326 |
}) |
327 |
}, |
328 |
|
329 |
/** |
330 |
* Class builder method |
331 |
* Defines workers for the class (The class must have the meta class Joose.Gears) |
332 |
* @function |
333 |
* @param classObject {object} Maps method names to function bodies |
334 |
* @name workers |
335 |
* @memberof Joose.Builder |
336 |
*/ |
337 |
/** @ignore */ |
338 |
workers: function (map) { |
339 |
var me = joose.cc |
340 |
Joose.O.each(map, function (func, name) { |
341 |
me.meta.addWorker(name, func) |
342 |
}) |
343 |
}, |
344 |
|
345 |
/** |
346 |
* Class builder method |
347 |
* Defines before method modifieres for the class. |
348 |
* The defined method modifiers will be called before the method of the super class. |
349 |
* The return value of the method modifier will be ignored |
350 |
* @function |
351 |
* @param classObject {object} Maps method names to function bodies |
352 |
* @name before |
353 |
* @memberof Joose.Builder |
354 |
*/ |
355 |
/** @ignore */ |
356 |
before: function(map) { |
357 |
var me = joose.cc |
358 |
Joose.O.each(map, function (func, name) { |
359 |
me.meta.wrapMethod(name, "before", func); |
360 |
}) |
361 |
}, |
362 |
|
363 |
/** |
364 |
* Class builder method |
365 |
* Defines after method modifieres for the class. |
366 |
* The defined method modifiers will be called after the method of the super class. |
367 |
* The return value of the method modifier will be ignored |
368 |
* @function |
369 |
* @param classObject {object} Maps method names to function bodies |
370 |
* @name after |
371 |
* @memberof Joose.Builder |
372 |
*/ |
373 |
/** @ignore */ |
374 |
after: function(map) { |
375 |
var me = joose.cc |
376 |
Joose.O.each(map, function (func, name) { |
377 |
me.meta.wrapMethod(name, "after", func); |
378 |
}) |
379 |
}, |
380 |
|
381 |
/** |
382 |
* Class builder method |
383 |
* Defines around method modifieres for the class. |
384 |
* The defined method modifiers will be called instead of the method of the super class. |
385 |
* The orginial function is passed as an initial parameter to the new function |
386 |
* @function |
387 |
* @param classObject {object} Maps method names to function bodies |
388 |
* @name around |
389 |
* @memberof Joose.Builder |
390 |
*/ |
391 |
/** @ignore */ |
392 |
around: function(map) { |
393 |
var me = joose.cc |
394 |
Joose.O.each(map, function (func, name) { |
395 |
me.meta.wrapMethod(name, "around", func); |
396 |
}) |
397 |
}, |
398 |
|
399 |
/** |
400 |
* Class builder method |
401 |
* Defines override method modifieres for the class. |
402 |
* The defined method modifiers will be called instead the method of the super class. |
403 |
* You can call the method of the super class by calling joose.cc.SUPER(para1, para2) |
404 |
* @function |
405 |
* @param classObject {object} Maps method names to function bodies |
406 |
* @name override |
407 |
* @memberof Joose.Builder |
408 |
*/ |
409 |
/** @ignore */ |
410 |
override: function(map) { |
411 |
var me = joose.cc |
412 |
Joose.O.each(map, function (func, name) { |
413 |
me.meta.wrapMethod(name, "override", func); |
414 |
}) |
415 |
}, |
416 |
|
417 |
/** |
418 |
* Class builder method |
419 |
* Defines augment method modifieres for the class. |
420 |
* These method modifiers will be called in "most super first" order |
421 |
* The methods may call this.INNER() to call the augement method in it's sup class. |
422 |
* @function |
423 |
* @param classObject {object} Maps method names to function bodies |
424 |
* @name augment |
425 |
* @memberof Joose.Builder |
426 |
*/ |
427 |
/** @ignore */ |
428 |
augment: function(map) { |
429 |
var me = joose.cc |
430 |
Joose.O.each(map, function (func, name) { |
431 |
me.meta.wrapMethod(name, "augment", func, function () { |
432 |
me.meta.addMethod(name, func) |
433 |
}); |
434 |
}) |
435 |
}, |
436 |
|
437 |
/** |
438 |
* @ignore |
439 |
*/ |
440 |
decorates: function(map) { |
441 |
var me = joose.cc |
442 |
Joose.O.each(map, function (classObject, attributeName) { |
443 |
me.meta.decorate(classObject, attributeName) |
444 |
}) |
445 |
} |
446 |
|
447 |
}; |
448 |
|
449 |
joose.init(); |