1 |
Joose = function () { |
2 |
this.cc = null; // the current class |
3 |
this.currentModule = null |
4 |
this.top = window; |
5 |
this.globalObjects = []; |
6 |
|
7 |
this.anonymouseClassCounter = 0; |
8 |
}; |
9 |
|
10 |
|
11 |
Joose.A = {}; |
12 |
Joose.A.each = function (array, func) { |
13 |
for(var i = 0; i < array.length; i++) { |
14 |
func(array[i], i) |
15 |
} |
16 |
} |
17 |
Joose.A.exists = function (array, value) { |
18 |
for(var i = 0; i < array.length; i++) { |
19 |
if(array[i] == value) { |
20 |
return true |
21 |
} |
22 |
} |
23 |
return false |
24 |
} |
25 |
Joose.A.concat = function (source, array) { |
26 |
source.push.apply(source, array) |
27 |
return source |
28 |
} |
29 |
|
30 |
Joose.A.grep = function (array, func) { |
31 |
var a = []; |
32 |
Joose.A.each(array, function (t) { |
33 |
if(func(t)) { |
34 |
a.push(t) |
35 |
} |
36 |
}) |
37 |
return a |
38 |
} |
39 |
Joose.S = {}; |
40 |
Joose.S.uppercaseFirst = function (string) { |
41 |
var first = string.substr(0,1); |
42 |
var rest = string.substr(1,string.length-1); |
43 |
first = first.toUpperCase() |
44 |
return first + rest; |
45 |
} |
46 |
|
47 |
Joose.S.isString = function (thing) { |
48 |
if(typeof thing == "string") { |
49 |
return true |
50 |
} |
51 |
return false |
52 |
} |
53 |
|
54 |
Joose.O = {}; |
55 |
Joose.O.each = function (object, func) { |
56 |
for(var i in object) { |
57 |
func(object[i], i) |
58 |
} |
59 |
} |
60 |
|
61 |
|
62 |
Joose.prototype = { |
63 |
|
64 |
/* |
65 |
* Differentiates between instances and classes |
66 |
*/ |
67 |
isInstance: function(obj) { |
68 |
if(!obj.meta) { |
69 |
throw "isInstance only works with Joose objects and classes." |
70 |
} |
71 |
if(obj.constructor === obj.meta.c) { |
72 |
return true |
73 |
} |
74 |
return false |
75 |
}, |
76 |
|
77 |
init: function () { |
78 |
this.builder = new Joose.Builder(); |
79 |
this.builder.globalize() |
80 |
}, |
81 |
// this needs to be updated in release.pl too, if files are added |
82 |
components: function () { |
83 |
return [ |
84 |
"Joose.Builder", |
85 |
"Joose.Class", |
86 |
"Joose.Method", |
87 |
"Joose.ClassMethod", |
88 |
"Joose.Method", |
89 |
"Joose.Attribute", |
90 |
"Joose.Role", |
91 |
"Joose.SimpleRequest", |
92 |
"Joose.Gears", |
93 |
"Joose.Storage", |
94 |
"Joose.Storage.Unpacker", |
95 |
"Joose.Decorator", |
96 |
"Joose.Module", |
97 |
"Joose.Prototype", |
98 |
"Joose.TypeConstraint", |
99 |
"Joose.TypeCoercion" |
100 |
] |
101 |
}, |
102 |
|
103 |
loadComponents: function (basePath) { |
104 |
var html = ""; |
105 |
Joose.A.each(this.components(), function (name) { |
106 |
var url = ""+basePath + "/" + name.split(".").join("/") + ".js"; |
107 |
|
108 |
html += '<script type="text/javascript" src="'+url+'"></script>' |
109 |
}) |
110 |
document.write(html) |
111 |
} |
112 |
} |
113 |
|
114 |
Joose.copyObject = function (source, target) { |
115 |
var keys = ""; |
116 |
Joose.O.each(source, function (value, name) { keys+=", "+name; target[name] = value }) |
117 |
return target |
118 |
}; |
119 |
|
120 |
|
121 |
|
122 |
Joose.emptyFunction = function () {}; |
123 |
|
124 |
var joose = new Joose(); |
125 |
|
126 |
|
127 |
Joose.bootstrap = function () { |
128 |
// Bootstrap |
129 |
var BOOT = new Joose.MetaClassBootstrap(); |
130 |
|
131 |
BOOT.builder = Joose.MetaClassBootstrap; |
132 |
|
133 |
Joose.MetaClass = BOOT.createClass("Joose.MetaClass"); |
134 |
|
135 |
Joose.MetaClass.meta.addNonJooseSuperClass("Joose.MetaClassBootstrap", BOOT) |
136 |
|
137 |
Joose.MetaClass.meta.addMethod("initialize", function () { this._name = "Joose.MetaClass" }) |
138 |
|
139 |
var META = new Joose.MetaClass(); |
140 |
|
141 |
META.builder = Joose.MetaClass; |
142 |
|
143 |
Joose.Class = META.createClass("Joose.Class") |
144 |
Joose.Class.meta.addSuperClass(Joose.MetaClass); |
145 |
Joose.MetaClass.meta.addMethod("initialize", function () { this._name = "Joose.Class" }) |
146 |
} |
147 |
|
148 |
Joose.bootstrap2 = function () { |
149 |
// Turn Joose.Method into a Joose.Class object |
150 |
Joose.Builder.Globals.joosify("Joose.Method", Joose.Method) |
151 |
Joose.Builder.Globals.joosify("Joose.Attribute", Joose.Attribute) |
152 |
} |
153 |
|
154 |
Joose.bootstrap3 = function () { |
155 |
// make the .meta object circular |
156 |
} |
157 |
|
158 |
/** |
159 |
* @name Joose.Class |
160 |
* @constructor |
161 |
*/ |
162 |
/* |
163 |
* Joose.MetaClassBootstrap is used to bootstrap the Joose.Class with a regular JS constructor |
164 |
*/ |
165 |
/** ignore */ // Do not display the Bootstrap classes in the docs |
166 |
Joose.MetaClassBootstrap = function () { |
167 |
this._name = "Joose.MetaClassBootstrap"; |
168 |
this.methodNames = []; |
169 |
this.attributeNames = ["_name", "isAbstract", "methodNames", "attributeNames", "methods", "parentClasses", "roles", "c"]; |
170 |
this.attributes = {}, |
171 |
this.methods = {}; |
172 |
this.parentClasses = []; |
173 |
this.roles = []; |
174 |
this.isAbstract = false; |
175 |
} |
176 |
/** @ignore */ |
177 |
Joose.MetaClassBootstrap.prototype = { |
178 |
|
179 |
toString: function () { |
180 |
if(this.meta) { |
181 |
return "a "+this.meta.className(); |
182 |
} |
183 |
return "NoMeta" |
184 |
}, |
185 |
|
186 |
/** |
187 |
* Returns the name of the class |
188 |
* @name className |
189 |
* @function |
190 |
* @memberof Joose.Class |
191 |
*/ |
192 |
/** @ignore */ |
193 |
className: function () { |
194 |
return this._name |
195 |
}, |
196 |
|
197 |
/** |
198 |
* Returns the name of the class (alias to className()) |
199 |
* @name getName |
200 |
* @function |
201 |
* @memberof Joose.Class |
202 |
*/ |
203 |
/** @ignore */ |
204 |
getName: function () { |
205 |
return this.className() |
206 |
}, |
207 |
|
208 |
/** |
209 |
* Creates a new empty meta class object |
210 |
* @function |
211 |
* @name newMetaClass |
212 |
* @memberof Joose.Class |
213 |
*/ |
214 |
/** @ignore */ |
215 |
newMetaClass: function () { |
216 |
|
217 |
var me = this; |
218 |
|
219 |
var metaClassClass = this.builder; |
220 |
|
221 |
var c = new metaClassClass(); |
222 |
c.builder = metaClassClass; |
223 |
c._name = this._name |
224 |
|
225 |
c.methodNames = [] |
226 |
c.attributeNames = [] |
227 |
c.methods = {} |
228 |
c.parentClasses = [] |
229 |
c.roles = [] |
230 |
c.attributes = {} |
231 |
|
232 |
var myMeta = this.meta; |
233 |
if(!myMeta) { |
234 |
myMeta = this; |
235 |
} |
236 |
|
237 |
c.meta = myMeta |
238 |
|
239 |
return c |
240 |
}, |
241 |
|
242 |
/** |
243 |
* Creates a new class object |
244 |
* @function |
245 |
* @name createClass |
246 |
* @param {function} optionalConstructor If provided will be used as the class constructor (You should not need this) |
247 |
* @param {Joose.Module} optionalModuleObject If provided the Module's name will be prepended to the class name |
248 |
* @memberof Joose.Class |
249 |
*/ |
250 |
/** @ignore */ |
251 |
createClass: function (name, optionalConstructor, optionalModuleObject) { |
252 |
var meta = this.newMetaClass(); |
253 |
|
254 |
var c; |
255 |
|
256 |
if(optionalConstructor) { |
257 |
c = optionalConstructor |
258 |
} else { |
259 |
c = this.defaultClassFunctionBody() |
260 |
|
261 |
if(optionalModuleObject) { |
262 |
optionalModuleObject.addElement(c) |
263 |
// meta.setModule(optionalModuleObject) |
264 |
} |
265 |
} |
266 |
|
267 |
c.prototype.meta = meta |
268 |
c.meta = meta; |
269 |
if(name == null) { |
270 |
meta._name = "__anonymous__" |
271 |
} else { |
272 |
var className = name; |
273 |
if(optionalModuleObject) { |
274 |
className = optionalModuleObject.getName() + "." + name |
275 |
} |
276 |
meta._name = className; |
277 |
} |
278 |
meta.c = c; |
279 |
|
280 |
// store them in the global object if they have no namespace |
281 |
// They will end up in the Module __JOOSE_GLOBAL__ |
282 |
if(!optionalModuleObject) { |
283 |
// Because the class Joose.Module might not exist yet, we use this temp store |
284 |
// that will later be in the global module |
285 |
joose.globalObjects.push(c) |
286 |
} |
287 |
|
288 |
meta.addInitializer(); |
289 |
meta.addToString(); |
290 |
meta.addDetacher(); |
291 |
|
292 |
meta.validateClass(); |
293 |
|
294 |
return c; |
295 |
}, |
296 |
|
297 |
buildComplete: function () { |
298 |
// may be overriden in sublass |
299 |
}, |
300 |
|
301 |
/** |
302 |
* Returns a new instance of the class that this meta class instance is representing |
303 |
* @function |
304 |
* @name instantiate |
305 |
* @memberof Joose.Class |
306 |
*/ |
307 |
instantiate: function () { |
308 |
//var o = new this.c.apply(this, arguments); |
309 |
|
310 |
// Ough! Calling a constructor with arbitrary arguments hack |
311 |
var f = function () {}; |
312 |
f.prototype = this.c.prototype; |
313 |
f.prototype.constructor = this.c; |
314 |
var obj = new f(); |
315 |
this.c.apply(obj, arguments); |
316 |
return obj; |
317 |
}, |
318 |
|
319 |
/** |
320 |
* Returns the default constructor function for new classes. You might want to override this in derived meta classes |
321 |
* Default calls initialize on a new object upon construction. |
322 |
* The class object will stringify to it's name |
323 |
* @function |
324 |
* @name defaultClassFunctionBody |
325 |
* @memberof Joose.Class |
326 |
*/ |
327 |
/** @ignore */ |
328 |
defaultClassFunctionBody: function () { |
329 |
var f = function () { |
330 |
this.initialize.apply(this, arguments); |
331 |
}; |
332 |
f.toString = function () { |
333 |
return this.meta.className() |
334 |
} |
335 |
return f; |
336 |
}, |
337 |
|
338 |
/** |
339 |
* Adds a toString method to a class |
340 |
* The default toString method will call the method stringify if available. |
341 |
* This make overriding stringification easier because toString cannot |
342 |
* be reliably overriden in some JS implementations. |
343 |
* @function |
344 |
* @name addToString |
345 |
* @memberof Joose.Class |
346 |
*/ |
347 |
/** @ignore */ |
348 |
addToString: function () { |
349 |
this.addMethod("toString", function () { |
350 |
if(this.stringify) { |
351 |
return this.stringify() |
352 |
} |
353 |
return "a "+ this.meta.className() |
354 |
}) |
355 |
}, |
356 |
|
357 |
/** |
358 |
* Adds the method returned by the initializer method to the class |
359 |
* @function |
360 |
* @name addInitializer |
361 |
* @memberof Joose.Class |
362 |
*/ |
363 |
/** @ignore */ |
364 |
addInitializer: function () { |
365 |
if(!this.c.prototype.initialize) { |
366 |
this.addMethod("initialize", this.initializer()) |
367 |
} |
368 |
}, |
369 |
|
370 |
/** |
371 |
* Adds a toString method to a class |
372 |
* @function |
373 |
* @name initializer |
374 |
* @memberof Joose.Class |
375 |
*/ |
376 |
/** @ignore */ |
377 |
initializer: function () { |
378 |
return function (paras) { |
379 |
var me = this; |
380 |
if(this.meta.isAbstract) { |
381 |
var name = this.meta.className(); |
382 |
throw ""+name+" is an abstract class and may not instantiated." |
383 |
} |
384 |
Joose.O.each(this.meta.getAttributes(), function (attr) { |
385 |
attr.doInitialization(me, paras); |
386 |
}) |
387 |
} |
388 |
}, |
389 |
|
390 |
dieIfString: function (thing) { |
391 |
if(Joose.S.isString(thing)) { |
392 |
throw new TypeError("Parameter must not be a string.") |
393 |
} |
394 |
}, |
395 |
|
396 |
addRole: function (roleClass) { |
397 |
this.dieIfString(roleClass); |
398 |
this.roles.push(roleClass); |
399 |
roleClass.meta.apply(this.getClassObject()) |
400 |
}, |
401 |
|
402 |
getClassObject: function () { |
403 |
return this.c |
404 |
}, |
405 |
|
406 |
classNameToClassObject: function (className) { |
407 |
var top = joose.top; |
408 |
var parts = className.split("."); |
409 |
var object = top; |
410 |
for(var i = 0; i < parts.length; i++) { |
411 |
var part = parts[i]; |
412 |
object = object[part]; |
413 |
if(!object) { |
414 |
throw "Unable to find class "+className |
415 |
} |
416 |
} |
417 |
return object |
418 |
}, |
419 |
|
420 |
addNonJooseSuperClass: function (name, object) { |
421 |
|
422 |
var pseudoMeta = new Joose.MetaClassBootstrap(); |
423 |
pseudoMeta.builder = Joose.MetaClassBootstrap; |
424 |
var pseudoClass = pseudoMeta.createClass(name) |
425 |
|
426 |
Joose.O.each(object, function(value, name) { |
427 |
if(typeof(value) == "function") { |
428 |
pseudoClass.meta.addMethod(name, value) |
429 |
} else { |
430 |
pseudoClass.meta.addAttribute(name, {init: value}) |
431 |
} |
432 |
}) |
433 |
|
434 |
this.addSuperClass(pseudoClass); |
435 |
}, |
436 |
|
437 |
importMethods: function (classObject) { |
438 |
var me = this; |
439 |
var names = classObject.meta.getMethodNames(); |
440 |
|
441 |
//alert("Super"+me.name + " -> "+classObject.meta.name +"->" + names) |
442 |
|
443 |
Joose.A.each(names, function (name) { |
444 |
var m = classObject.meta.dispatch(name); |
445 |
me.addMethodObject(m.meta.copy()) |
446 |
}) |
447 |
}, |
448 |
|
449 |
addSuperClass: function (classObject) { |
450 |
this.dieIfString(classObject); |
451 |
var me = this; |
452 |
|
453 |
//this._fixMetaclassIncompatability(classObject) |
454 |
|
455 |
var names = classObject.meta.getMethodNames(); |
456 |
Joose.A.each(names, function (name) { |
457 |
var m = classObject.meta.dispatch(name); |
458 |
var o = m.meta.copy(); |
459 |
o.setIsFromSuperClass(true) |
460 |
me.addMethodObject(o) |
461 |
}) |
462 |
|
463 |
Joose.O.each(classObject.meta.attributes, function (attr, name) { |
464 |
// XXX We should not override attributes |
465 |
//if(!this.getAttribute(name)) { |
466 |
me.addAttribute(name, attr.getProps()) |
467 |
//} |
468 |
}) |
469 |
|
470 |
this.parentClasses.unshift(classObject) |
471 |
}, |
472 |
|
473 |
_fixMetaclassIncompatability: function (superClass) { |
474 |
|
475 |
var superMeta = superClass.meta; |
476 |
var superMetaName = superMeta.meta.className(); |
477 |
|
478 |
if( |
479 |
superMetaName == "Joose.Class" || |
480 |
superMetaName == "Joose.MetaClass" || |
481 |
superMetaName == "Joose.MetaClassBootstrap") { |
482 |
return |
483 |
} |
484 |
|
485 |
// we are compatible |
486 |
if(this.meta.meta.isa(superMeta)) { |
487 |
return |
488 |
} |
489 |
|
490 |
// fix this into becoming a superMeta |
491 |
var patched = superMeta.meta.instantiate(this); |
492 |
|
493 |
for(var i in patched) { |
494 |
this[i] = patched[i] |
495 |
} |
496 |
}, |
497 |
|
498 |
isa: function (classObject) { |
499 |
this.dieIfString(classObject); |
500 |
var name = classObject.meta.className() |
501 |
// Same type |
502 |
if(this.className() == name) { |
503 |
return true |
504 |
} |
505 |
// Look up into parent classes |
506 |
for(var i = 0; i < this.parentClasses.length; i++) { |
507 |
var parent = this.parentClasses[i].meta |
508 |
if(parent.className() == name) { |
509 |
return true |
510 |
} |
511 |
if(parent.isa(classObject)) { |
512 |
return true |
513 |
} |
514 |
} |
515 |
return false |
516 |
}, |
517 |
|
518 |
wrapMethod: function (name, wrappingStyle, func, notPresentCB) { |
519 |
|
520 |
var orig = this.getMethodObject(name); |
521 |
if(orig) { |
522 |
this.addMethodObject( orig[wrappingStyle](func) ) |
523 |
} else { |
524 |
if(notPresentCB) { |
525 |
notPresentCB() |
526 |
} else { |
527 |
throw new Error("Unable to apply "+wrappingStyle+" method modifier because method "+name+" does not exist"); |
528 |
} |
529 |
} |
530 |
}, |
531 |
|
532 |
dispatch: function (name) { |
533 |
return this.getMethodObject(name).asFunction() |
534 |
}, |
535 |
|
536 |
hasMethod: function (name) { |
537 |
return this.methods[name] != null |
538 |
}, |
539 |
|
540 |
addMethod: function (name, func, props) { |
541 |
var m = new Joose.Method(name, func, props); |
542 |
|
543 |
this.addMethodObject(m) |
544 |
}, |
545 |
|
546 |
addClassMethod: function (name, func, props) { |
547 |
var m = new Joose.ClassMethod(name, func, props); |
548 |
|
549 |
this.addMethodObject(m) |
550 |
}, |
551 |
|
552 |
addMethodObject: function (method) { |
553 |
var m = method; |
554 |
var name = m.getName(); |
555 |
if(!Joose.A.exists(this.methodNames, name)) { |
556 |
this.methodNames.push(name); |
557 |
} |
558 |
this.methods[name] = m; |
559 |
|
560 |
method.addToClass(this.c) |
561 |
}, |
562 |
|
563 |
attributeMetaclass: function () { |
564 |
return Joose.Attribute |
565 |
}, |
566 |
|
567 |
addAttribute: function (name, props) { |
568 |
|
569 |
var metaclass = this.attributeMetaclass(); |
570 |
|
571 |
if(props && props.metaclass) { |
572 |
metaclass = props.metaclass |
573 |
} |
574 |
|
575 |
var at = new metaclass(name, props); |
576 |
|
577 |
at.apply(this.c) |
578 |
}, |
579 |
|
580 |
getAttributes: function () { |
581 |
return this.attributes |
582 |
}, |
583 |
|
584 |
getAttribute: function (name) { |
585 |
return this.attributes[name] |
586 |
}, |
587 |
|
588 |
setAttribute: function (name, attributeObject) { |
589 |
return this.attributes[name] = attributeObject |
590 |
}, |
591 |
|
592 |
getMethodObject: function (name) { |
593 |
return this.methods[name] |
594 |
}, |
595 |
|
596 |
getAttributeNames: function () { |
597 |
return this.attributeNames; |
598 |
}, |
599 |
|
600 |
getInstanceMethods: function () { |
601 |
var a = []; |
602 |
Joose.O.each(this.methods, function (m) { |
603 |
if(!m.isClassMethod()) { |
604 |
a.push(m) |
605 |
} |
606 |
}) |
607 |
return a |
608 |
}, |
609 |
|
610 |
getClassMethods: function () { |
611 |
var a = []; |
612 |
Joose.O.each(this.methods, function (m) { |
613 |
if(m.isClassMethod()) { |
614 |
a.push(m) |
615 |
} |
616 |
}) |
617 |
return a |
618 |
}, |
619 |
|
620 |
getSuperClasses: function () { |
621 |
return this.parentClasses; |
622 |
}, |
623 |
|
624 |
getRoles: function () { |
625 |
return this.roles; |
626 |
}, |
627 |
|
628 |
getMethodNames: function () { |
629 |
return this.methodNames; |
630 |
}, |
631 |
|
632 |
addDetacher: function () { |
633 |
this.addMethod("detach", function () { |
634 |
var meta = this.meta; |
635 |
|
636 |
var c = meta.createClass(meta.className()+"__anon__"+joose.anonymouseClassCounter++); |
637 |
c.meta.addSuperClass(meta.getClassObject()); |
638 |
|
639 |
// appy the role to the anonymous class |
640 |
// swap meta class of object with new instance |
641 |
this.meta = c.meta; |
642 |
// swap __proto__ chain of object to its new class |
643 |
// unfortunately this is not available in IE :( |
644 |
// object.__proto__ = c.prototype |
645 |
|
646 |
this.constructor = c; |
647 |
|
648 |
c.prototype = this; |
649 |
return |
650 |
|
651 |
if(this.__proto__) { |
652 |
this.__proto__ = c.prototype |
653 |
} else { // Workaround for IE: |
654 |
for(var i in c.prototype) { |
655 |
if(this[i] == null) { |
656 |
this[i] = c.prototype[i] |
657 |
} |
658 |
} |
659 |
} |
660 |
}) |
661 |
}, |
662 |
|
663 |
/** |
664 |
* Throws an exception if the class does not implement all methods required by it's roles |
665 |
* @function |
666 |
* @name validateClass |
667 |
* @memberof Joose.Class |
668 |
*/ |
669 |
validateClass: function () { |
670 |
var c = this.getClassObject(); |
671 |
var me = this; |
672 |
var throwException = true; |
673 |
Joose.A.each(this.roles, function(role) { |
674 |
role.meta.isImplementedBy(c, throwException) |
675 |
}) |
676 |
}, |
677 |
|
678 |
/** |
679 |
* Returns true if the class implements the method |
680 |
* @function |
681 |
* @name can |
682 |
* @param {string} methodName The method |
683 |
* @memberof Joose.Class |
684 |
*/ |
685 |
can: function (methodName) { |
686 |
var method = this.methods[methodName]; |
687 |
if(!method || method.isClassMethod()) { |
688 |
return false |
689 |
} |
690 |
return true |
691 |
}, |
692 |
|
693 |
classCan: function (methodName) { |
694 |
var method = this.methods[methodName]; |
695 |
if(!method || !method.isClassMethod()) { |
696 |
return false |
697 |
} |
698 |
return true |
699 |
}, |
700 |
|
701 |
|
702 |
/** |
703 |
* Returns true if the class implements a Role |
704 |
* @function |
705 |
* @name does |
706 |
* @param {Joose.Class} methodName The class object |
707 |
* @memberof Joose.Class |
708 |
*/ |
709 |
does: function (roleObject) { |
710 |
|
711 |
for(var i = 0; i < this.roles.length; i++) { |
712 |
if(roleObject === this.roles[i]) { |
713 |
return true |
714 |
} |
715 |
} |
716 |
|
717 |
// dive into roles to find roles implemented by my roles |
718 |
for(var i = 0; i < this.roles.length; i++) { |
719 |
if(this.roles[i].meta.does(roleObject)) { |
720 |
return true |
721 |
} |
722 |
} |
723 |
|
724 |
return false |
725 |
// return classObject.meta.implementsMyMethods(this.getClassObject()) |
726 |
}, |
727 |
|
728 |
/** |
729 |
* Returns true if the given class implements all methods of the class |
730 |
* @function |
731 |
* @name does |
732 |
* @param {Joose.Class} methodName The class object |
733 |
* @memberof Joose.Class |
734 |
*/ |
735 |
implementsMyMethods: function (classObject) { |
736 |
var complete = true |
737 |
Joose.A.each(this.getMethodNames(), function (value) { |
738 |
var found = classObject.meta.can(value) |
739 |
if(!found) { |
740 |
complete = false |
741 |
} |
742 |
}) |
743 |
return complete |
744 |
} |
745 |
}; |
746 |
|
747 |
Joose.Attribute = function (name, props) { |
748 |
this.initialize(name, props) |
749 |
} |
750 |
|
751 |
Joose.Attribute.prototype = { |
752 |
|
753 |
_name: null, |
754 |
_props: null, |
755 |
|
756 |
getName: function () { return this._name }, |
757 |
getProps: function () { return this._props }, |
758 |
|
759 |
initialize: function (name, props) { |
760 |
this._name = name; |
761 |
this.setProps(props); |
762 |
}, |
763 |
|
764 |
setProps: function (props) { |
765 |
if(props) { |
766 |
this._props = props |
767 |
} else { |
768 |
this._props = {}; |
769 |
} |
770 |
}, |
771 |
|
772 |
getIsa: function () { |
773 |
var props = this.getProps(); |
774 |
if(props.isa) { |
775 |
if(!props.isa.meta) { |
776 |
return props.isa() |
777 |
} |
778 |
return props.isa |
779 |
} |
780 |
return |
781 |
}, |
782 |
|
783 |
addSetter: function (classObject) { |
784 |
var meta = classObject.meta; |
785 |
var name = this.getName(); |
786 |
var props = this.getProps(); |
787 |
|
788 |
var isa = this.getIsa(); |
789 |
|
790 |
var func; |
791 |
if(isa) { |
792 |
func = function (value) { |
793 |
if(!value || !value.meta) { |
794 |
throw "The attribute "+name+" only accepts values that have a meta object." |
795 |
} |
796 |
// TODO add does |
797 |
if(!value.meta.isa(isa)) { |
798 |
throw "The attribute "+name+" only accepts values that are objects of type "+isa.meta.className()+"." |
799 |
} |
800 |
this[name] = value |
801 |
return this; |
802 |
} |
803 |
} else { |
804 |
func = function (value) { |
805 |
this[name] = value |
806 |
return this; |
807 |
} |
808 |
} |
809 |
meta.addMethod(this.setterName(), func); |
810 |
}, |
811 |
|
812 |
|
813 |
addGetter: function (classObject) { |
814 |
var meta = classObject.meta; |
815 |
var name = this.getName(); |
816 |
var props = this.getProps(); |
817 |
|
818 |
var func = function () { |
819 |
return this[name] |
820 |
} |
821 |
|
822 |
var init = props.init; |
823 |
|
824 |
if(props.lazy) { |
825 |
func = function () { |
826 |
var val = this[name]; |
827 |
if(typeof val == "function" && val === init) { |
828 |
this[name] = val.apply(this) |
829 |
} |
830 |
return this[name] |
831 |
} |
832 |
} |
833 |
|
834 |
meta.addMethod(this.getterName(), func); |
835 |
}, |
836 |
|
837 |
initializerName: function () { |
838 |
return this.toPublicName() |
839 |
}, |
840 |
|
841 |
getterName: function () { |
842 |
return "get"+Joose.S.uppercaseFirst(this.toPublicName()) |
843 |
}, |
844 |
|
845 |
setterName: function () { |
846 |
return "set"+Joose.S.uppercaseFirst(this.toPublicName()) |
847 |
}, |
848 |
|
849 |
isPrivate: function () { |
850 |
return this.getName().charAt(0) == "_" |
851 |
}, |
852 |
|
853 |
toPublicName: function () { |
854 |
var name = this.getName(); |
855 |
if(this.isPrivate()) { |
856 |
return name.substr(1) |
857 |
} |
858 |
return name |
859 |
}, |
860 |
|
861 |
handleIs: function (classObject) { |
862 |
var meta = classObject.meta; |
863 |
var name = this.getName(); |
864 |
var props = this.getProps(); |
865 |
|
866 |
var is = props.is; |
867 |
|
868 |
if(is == "rw" || is == "ro") { |
869 |
this.addGetter(classObject); |
870 |
} |
871 |
if(is == "rw") { |
872 |
this.addSetter(classObject) |
873 |
} |
874 |
}, |
875 |
|
876 |
handleInit: function (classObject) { |
877 |
var props = this.getProps(); |
878 |
var name = this.getName(); |
879 |
|
880 |
classObject.prototype[name] = null; |
881 |
if(typeof props.init != "undefined") { |
882 |
var val = props.init; |
883 |
var type = typeof val; |
884 |
|
885 |
classObject.prototype[name] = val; |
886 |
} |
887 |
}, |
888 |
|
889 |
handleProps: function (classObject) { |
890 |
this.handleIs(classObject); |
891 |
this.handleInit(classObject) |
892 |
}, |
893 |
|
894 |
apply: function (classObject) { |
895 |
|
896 |
var meta = classObject.meta; |
897 |
var name = this.getName(); |
898 |
|
899 |
this.handleProps(classObject) |
900 |
|
901 |
meta.attributeNames.push(name) |
902 |
|
903 |
meta.setAttribute(name, this) |
904 |
meta.attributes[name] = this; |
905 |
} |
906 |
|
907 |
|
908 |
} |
909 |
|
910 |
Joose.Method = function (name, func, props) { |
911 |
this.initialize(name, func, props) |
912 |
} |
913 |
|
914 |
Joose.Method.prototype = { |
915 |
|
916 |
_name: null, |
917 |
_body: null, |
918 |
_props: null, |
919 |
_isFromSuperClass: false, |
920 |
|
921 |
getName: function () { return this._name }, |
922 |
getBody: function () { return this._body }, |
923 |
getProps: function () { return this._props }, |
924 |
|
925 |
isFromSuperClass: function () { |
926 |
return this._isFromSuperClass |
927 |
}, |
928 |
|
929 |
setIsFromSuperClass: function (bool) { |
930 |
this._isFromSuperClass = bool |
931 |
}, |
932 |
|
933 |
copy: function () { |
934 |
return new Joose.Method(this.getName(), this.getBody(), this.getProps()) |
935 |
}, |
936 |
|
937 |
initialize: function (name, func, props) { |
938 |
this._name = name; |
939 |
this._body = func; |
940 |
this._props = props; |
941 |
|
942 |
func.name = "test"+name |
943 |
|
944 |
func.meta = this |
945 |
}, |
946 |
|
947 |
isClassMethod: function () { return false }, |
948 |
|
949 |
apply: function (thisObject, args) { |
950 |
return this._body.apply(thisObject, args) |
951 |
}, |
952 |
|
953 |
// allows wrapping |
954 |
asFunction: function () { |
955 |
var me = this |
956 |
return function () { |
957 |
var args = arguments; |
958 |
return me.apply(this, args) |
959 |
} |
960 |
}, |
961 |
|
962 |
addToClass: function (c) { |
963 |
c.prototype[this.getName()] = this.asFunction() |
964 |
}, |
965 |
|
966 |
// direct call |
967 |
asFunction: function () { |
968 |
return this._body |
969 |
} |
970 |
} |
971 |
Joose.bootstrap() |
972 |
|
973 |
|
974 |
|
975 |
|
976 |
|
977 |
|
978 |
|
979 |
|
980 |
|