/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 46 - (hide 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 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    

  ViewVC Help
Powered by ViewVC 1.1.26