1 |
dpavlin |
46 |
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 |
|
|
|