/[Frey]/trunk/static/lib/Joose.js
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/static/lib/Joose.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 46 - (show annotations)
Wed Jul 2 10:28:49 2008 UTC (15 years, 10 months ago) by dpavlin
File MIME type: application/javascript
File size: 25949 byte(s)
added upstream Joose r4755

http://code2.0beta.co.uk/moose/svn/Joose/trunk/lib
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

  ViewVC Help
Powered by ViewVC 1.1.26