Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release
[gecko.git] / testing / mochitest / MochiKit / Base.js
blob3a91e31e309d999d142da74eb5a777d75b8b4096
1 /***
3 MochiKit.Base 1.4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
7 (c) 2005 Bob Ippolito.  All rights Reserved.
9 ***/
11 if (typeof(dojo) != 'undefined') {
12     dojo.provide("MochiKit.Base");
14 if (typeof(MochiKit) == 'undefined') {
15     MochiKit = {};
17 if (typeof(MochiKit.Base) == 'undefined') {
18     MochiKit.Base = {};
20 if (typeof(MochiKit.__export__) == "undefined") {
21     MochiKit.__export__ = (MochiKit.__compat__  ||
22         (typeof(JSAN) == 'undefined' && typeof(dojo) == 'undefined')
23     );
26 MochiKit.Base.VERSION = "1.4";
27 MochiKit.Base.NAME = "MochiKit.Base";
28 /** @id MochiKit.Base.update */
29 MochiKit.Base.update = function (self, obj/*, ... */) {
30     if (self === null) {
31         self = {};
32     }
33     for (var i = 1; i < arguments.length; i++) {
34         var o = arguments[i];
35         if (typeof(o) != 'undefined' && o !== null) {
36             for (var k in o) {
37                 self[k] = o[k];
38             }
39         }
40     }
41     return self;
44 MochiKit.Base.update(MochiKit.Base, {
45     __repr__: function () {
46         return "[" + this.NAME + " " + this.VERSION + "]";
47     },
49     toString: function () {
50         return this.__repr__();
51     },
53     /** @id MochiKit.Base.camelize */
54     camelize: function (selector) {
55         /* from dojo.style.toCamelCase */
56         var arr = selector.split('-');
57         var cc = arr[0];
58         for (var i = 1; i < arr.length; i++) {
59             cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1);
60         }
61         return cc;
62     },
64     /** @id MochiKit.Base.counter */
65     counter: function (n/* = 1 */) {
66         if (arguments.length === 0) {
67             n = 1;
68         }
69         return function () {
70             return n++;
71         };
72     },
73         
74     /** @id MochiKit.Base.clone */
75     clone: function (obj) {
76         var me = arguments.callee;
77         if (arguments.length == 1) {
78             me.prototype = obj;
79             return new me();
80         }
81     },
82             
83     _flattenArray: function (res, lst) {
84         for (var i = 0; i < lst.length; i++) {
85             var o = lst[i];
86             if (o instanceof Array) {
87                 arguments.callee(res, o);
88             } else {
89                 res.push(o);
90             }
91         }
92         return res;
93     },
94     
95     /** @id MochiKit.Base.flattenArray */
96     flattenArray: function (lst) {
97         return MochiKit.Base._flattenArray([], lst);
98     },
99     
100     /** @id MochiKit.Base.flattenArguments */
101     flattenArguments: function (lst/* ...*/) {
102         var res = [];
103         var m = MochiKit.Base;
104         var args = m.extend(null, arguments);
105         while (args.length) {
106             var o = args.shift();
107             if (o && typeof(o) == "object" && typeof(o.length) == "number") {
108                 for (var i = o.length - 1; i >= 0; i--) {
109                     args.unshift(o[i]);
110                 }
111             } else {
112                 res.push(o);
113             }
114         }
115         return res;
116     },
118     /** @id MochiKit.Base.extend */
119     extend: function (self, obj, /* optional */skip) {        
120         // Extend an array with an array-like object starting
121         // from the skip index
122         if (!skip) {
123             skip = 0;
124         }
125         if (obj) {
126             // allow iterable fall-through, but skip the full isArrayLike
127             // check for speed, this is called often.
128             var l = obj.length;
129             if (typeof(l) != 'number' /* !isArrayLike(obj) */) {
130                 if (typeof(MochiKit.Iter) != "undefined") {
131                     obj = MochiKit.Iter.list(obj);
132                     l = obj.length;
133                 } else {
134                     throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
135                 }
136             }
137             if (!self) {
138                 self = [];
139             }
140             for (var i = skip; i < l; i++) {
141                 self.push(obj[i]);
142             }
143         }
144         // This mutates, but it's convenient to return because
145         // it's often used like a constructor when turning some
146         // ghetto array-like to a real array
147         return self;
148     },
151     /** @id MochiKit.Base.updatetree */
152     updatetree: function (self, obj/*, ...*/) {
153         if (self === null) {
154             self = {};
155         }
156         for (var i = 1; i < arguments.length; i++) {
157             var o = arguments[i];
158             if (typeof(o) != 'undefined' && o !== null) {
159                 for (var k in o) {
160                     var v = o[k];
161                     if (typeof(self[k]) == 'object' && typeof(v) == 'object') {
162                         arguments.callee(self[k], v);
163                     } else {
164                         self[k] = v;
165                     }
166                 }
167             }
168         }
169         return self;
170     },
172     /** @id MochiKit.Base.setdefault */
173     setdefault: function (self, obj/*, ...*/) {
174         if (self === null) {
175             self = {};
176         }
177         for (var i = 1; i < arguments.length; i++) {
178             var o = arguments[i];
179             for (var k in o) {
180                 if (!(k in self)) {
181                     self[k] = o[k];
182                 }
183             }
184         }
185         return self;
186     },
188     /** @id MochiKit.Base.keys */
189     keys: function (obj) {
190         var rval = [];
191         for (var prop in obj) {
192             rval.push(prop);
193         }
194         return rval;
195     },
196         
197     /** @id MochiKit.Base.values */
198     values: function (obj) {
199         var rval = [];
200         for (var prop in obj) {
201             rval.push(obj[prop]);
202         }
203         return rval;
204     },
206      /** @id MochiKit.Base.items */
207     items: function (obj) {
208         var rval = [];
209         var e;
210         for (var prop in obj) {
211             var v;
212             try {
213                 v = obj[prop];
214             } catch (e) {
215                 continue;
216             }
217             rval.push([prop, v]);
218         }
219         return rval;
220     },
223     _newNamedError: function (module, name, func) {
224         func.prototype = new MochiKit.Base.NamedError(module.NAME + "." + name);
225         module[name] = func;
226     },
229     /** @id MochiKit.Base.operator */
230     operator: {
231         // unary logic operators
232         /** @id MochiKit.Base.truth */
233         truth: function (a) { return !!a; }, 
234         /** @id MochiKit.Base.lognot */
235         lognot: function (a) { return !a; },
236         /** @id MochiKit.Base.identity */
237         identity: function (a) { return a; },
239         // bitwise unary operators
240         /** @id MochiKit.Base.not */
241         not: function (a) { return ~a; },
242         /** @id MochiKit.Base.neg */
243         neg: function (a) { return -a; },
245         // binary operators
246         /** @id MochiKit.Base.add */
247         add: function (a, b) { return a + b; },
248         /** @id MochiKit.Base.sub */
249         sub: function (a, b) { return a - b; },
250         /** @id MochiKit.Base.div */
251         div: function (a, b) { return a / b; },
252         /** @id MochiKit.Base.mod */
253         mod: function (a, b) { return a % b; },
254         /** @id MochiKit.Base.mul */
255         mul: function (a, b) { return a * b; },
257         // bitwise binary operators
258         /** @id MochiKit.Base.and */
259         and: function (a, b) { return a & b; },
260         /** @id MochiKit.Base.or */
261         or: function (a, b) { return a | b; },
262         /** @id MochiKit.Base.xor */
263         xor: function (a, b) { return a ^ b; },
264         /** @id MochiKit.Base.lshift */
265         lshift: function (a, b) { return a << b; },
266         /** @id MochiKit.Base.rshift */
267         rshift: function (a, b) { return a >> b; },
268         /** @id MochiKit.Base.zrshift */
269         zrshift: function (a, b) { return a >>> b; },
271         // near-worthless built-in comparators
272         /** @id MochiKit.Base.eq */
273         eq: function (a, b) { return a == b; },
274         /** @id MochiKit.Base.ne */
275         ne: function (a, b) { return a != b; },
276         /** @id MochiKit.Base.gt */
277         gt: function (a, b) { return a > b; },
278         /** @id MochiKit.Base.ge */
279         ge: function (a, b) { return a >= b; },
280         /** @id MochiKit.Base.lt */
281         lt: function (a, b) { return a < b; },
282         /** @id MochiKit.Base.le */
283         le: function (a, b) { return a <= b; },
285         // strict built-in comparators
286         seq: function (a, b) { return a === b; },
287         sne: function (a, b) { return a !== b; },
289         // compare comparators
290         /** @id MochiKit.Base.ceq */
291         ceq: function (a, b) { return MochiKit.Base.compare(a, b) === 0; },
292         /** @id MochiKit.Base.cne */
293         cne: function (a, b) { return MochiKit.Base.compare(a, b) !== 0; },
294         /** @id MochiKit.Base.cgt */
295         cgt: function (a, b) { return MochiKit.Base.compare(a, b) == 1; },
296         /** @id MochiKit.Base.cge */
297         cge: function (a, b) { return MochiKit.Base.compare(a, b) != -1; },
298         /** @id MochiKit.Base.clt */
299         clt: function (a, b) { return MochiKit.Base.compare(a, b) == -1; },
300         /** @id MochiKit.Base.cle */
301         cle: function (a, b) { return MochiKit.Base.compare(a, b) != 1; },
303         // binary logical operators
304         /** @id MochiKit.Base.logand */
305         logand: function (a, b) { return a && b; },
306         /** @id MochiKit.Base.logor */
307         logor: function (a, b) { return a || b; },
308         /** @id MochiKit.Base.contains */
309         contains: function (a, b) { return b in a; }
310     },
312     /** @id MochiKit.Base.forwardCall */
313     forwardCall: function (func) {
314         return function () {
315             return this[func].apply(this, arguments);
316         };
317     },
319     /** @id MochiKit.Base.itemgetter */
320     itemgetter: function (func) {
321         return function (arg) {
322             return arg[func];
323         };
324     },
326     /** @id MochiKit.Base.typeMatcher */
327     typeMatcher: function (/* typ */) {
328         var types = {};
329         for (var i = 0; i < arguments.length; i++) {
330             var typ = arguments[i];
331             types[typ] = typ;
332         }
333         return function () { 
334             for (var i = 0; i < arguments.length; i++) {
335                 if (!(typeof(arguments[i]) in types)) {
336                     return false;
337                 }
338             }
339             return true;
340         };
341     },
343     /** @id MochiKit.Base.isNull */
344     isNull: function (/* ... */) {
345         for (var i = 0; i < arguments.length; i++) {
346             if (arguments[i] !== null) {
347                 return false;
348             }
349         }
350         return true;
351     },
353     /** @id MochiKit.Base.isUndefinedOrNull */
354     isUndefinedOrNull: function (/* ... */) {
355         for (var i = 0; i < arguments.length; i++) {
356             var o = arguments[i];
357             if (!(typeof(o) == 'undefined' || o === null)) {
358                 return false;
359             }
360         }
361         return true;
362     },
364     /** @id MochiKit.Base.isEmpty */
365     isEmpty: function (obj) {
366         return !MochiKit.Base.isNotEmpty.apply(this, arguments);
367     },
369     /** @id MochiKit.Base.isNotEmpty */
370     isNotEmpty: function (obj) {
371         for (var i = 0; i < arguments.length; i++) {
372             var o = arguments[i];
373             if (!(o && o.length)) {
374                 return false;
375             }
376         }
377         return true;
378     },
380     /** @id MochiKit.Base.isArrayLike */
381     isArrayLike: function () {
382         for (var i = 0; i < arguments.length; i++) {
383             var o = arguments[i];
384             var typ = typeof(o);
385             if (
386                 (typ != 'object' && !(typ == 'function' && typeof(o.item) == 'function')) ||
387                 o === null ||
388                 typeof(o.length) != 'number' ||
389                 o.nodeType === 3
390             ) {
391                 return false;
392             }
393         }
394         return true;
395     },
397     /** @id MochiKit.Base.isDateLike */
398     isDateLike: function () {
399         for (var i = 0; i < arguments.length; i++) {
400             var o = arguments[i];
401             if (typeof(o) != "object" || o === null
402                     || typeof(o.getTime) != 'function') {
403                 return false;
404             }
405         }
406         return true;
407     },
410     /** @id MochiKit.Base.xmap */
411     xmap: function (fn/*, obj... */) {
412         if (fn === null) {
413             return MochiKit.Base.extend(null, arguments, 1);
414         }
415         var rval = [];
416         for (var i = 1; i < arguments.length; i++) {
417             rval.push(fn(arguments[i]));
418         }
419         return rval;
420     },
422     /** @id MochiKit.Base.map */
423     map: function (fn, lst/*, lst... */) {
424         var m = MochiKit.Base;
425         var itr = MochiKit.Iter;
426         var isArrayLike = m.isArrayLike;
427         if (arguments.length <= 2) {
428             // allow an iterable to be passed
429             if (!isArrayLike(lst)) {
430                 if (itr) {
431                     // fast path for map(null, iterable)
432                     lst = itr.list(lst);
433                     if (fn === null) {
434                         return lst;
435                     }
436                 } else {
437                     throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
438                 }
439             }
440             // fast path for map(null, lst)
441             if (fn === null) {
442                 return m.extend(null, lst);
443             }
444             // disabled fast path for map(fn, lst)
445             /*
446             if (false && typeof(Array.prototype.map) == 'function') {
447                 // Mozilla fast-path
448                 return Array.prototype.map.call(lst, fn);
449             }
450             */
451             var rval = [];
452             for (var i = 0; i < lst.length; i++) {
453                 rval.push(fn(lst[i]));
454             }
455             return rval;
456         } else {
457             // default for map(null, ...) is zip(...)
458             if (fn === null) {
459                 fn = Array;
460             }
461             var length = null;
462             for (i = 1; i < arguments.length; i++) {
463                 // allow iterables to be passed
464                 if (!isArrayLike(arguments[i])) {
465                     if (itr) {
466                         return itr.list(itr.imap.apply(null, arguments));
467                     } else {
468                         throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
469                     }
470                 }
471                 // find the minimum length
472                 var l = arguments[i].length;
473                 if (length === null || length > l) {
474                     length = l;
475                 }
476             }
477             rval = [];
478             for (i = 0; i < length; i++) {
479                 var args = [];
480                 for (var j = 1; j < arguments.length; j++) {
481                     args.push(arguments[j][i]);
482                 }
483                 rval.push(fn.apply(this, args));
484             }
485             return rval;
486         }
487     },
489     /** @id MochiKit.Base.xfilter */
490     xfilter: function (fn/*, obj... */) {
491         var rval = [];
492         if (fn === null) {
493             fn = MochiKit.Base.operator.truth;
494         }
495         for (var i = 1; i < arguments.length; i++) {
496             var o = arguments[i];
497             if (fn(o)) {
498                 rval.push(o);
499             }
500         }
501         return rval;
502     },
504     /** @id MochiKit.Base.filter */
505     filter: function (fn, lst, self) {
506         var rval = [];
507         // allow an iterable to be passed
508         var m = MochiKit.Base;
509         if (!m.isArrayLike(lst)) {
510             if (MochiKit.Iter) {
511                 lst = MochiKit.Iter.list(lst);
512             } else {
513                 throw new TypeError("Argument not an array-like and MochiKit.Iter not present");
514             }
515         }
516         if (fn === null) {
517             fn = m.operator.truth;
518         }
519         if (typeof(Array.prototype.filter) == 'function') {
520             // Mozilla fast-path
521             return Array.prototype.filter.call(lst, fn, self);
522         } else if (typeof(self) == 'undefined' || self === null) {
523             for (var i = 0; i < lst.length; i++) {
524                 var o = lst[i];
525                 if (fn(o)) {
526                     rval.push(o);
527                 }
528             }
529         } else {
530             for (i = 0; i < lst.length; i++) {
531                 o = lst[i];
532                 if (fn.call(self, o)) {
533                     rval.push(o);
534                 }
535             }
536         }
537         return rval;
538     },
541     _wrapDumbFunction: function (func) {
542         return function () {
543             // fast path!
544             switch (arguments.length) {
545                 case 0: return func();
546                 case 1: return func(arguments[0]);
547                 case 2: return func(arguments[0], arguments[1]);
548                 case 3: return func(arguments[0], arguments[1], arguments[2]);
549             }
550             var args = [];
551             for (var i = 0; i < arguments.length; i++) {
552                 args.push("arguments[" + i + "]");
553             }
554             return eval("(func(" + args.join(",") + "))");
555         };
556     },
558     /** @id MochiKit.Base.methodcaller */
559     methodcaller: function (func/*, args... */) {
560         var args = MochiKit.Base.extend(null, arguments, 1);
561         if (typeof(func) == "function") {
562             return function (obj) {
563                 return func.apply(obj, args);
564             };
565         } else {
566             return function (obj) {
567                 return obj[func].apply(obj, args);
568             };
569         }
570     },
571     
572     /** @id MochiKit.Base.method */
573     method: function (self, func) {
574         var m = MochiKit.Base;
575         return m.bind.apply(this, m.extend([func, self], arguments, 2));
576     },
578     /** @id MochiKit.Base.compose */
579     compose: function (f1, f2/*, f3, ... fN */) {
580         var fnlist = [];
581         var m = MochiKit.Base;
582         if (arguments.length === 0) {
583             throw new TypeError("compose() requires at least one argument");
584         }
585         for (var i = 0; i < arguments.length; i++) {
586             var fn = arguments[i];
587             if (typeof(fn) != "function") {
588                 throw new TypeError(m.repr(fn) + " is not a function");
589             }
590             fnlist.push(fn);
591         }
592         return function () {
593             var args = arguments;
594             for (var i = fnlist.length - 1; i >= 0; i--) {
595                 args = [fnlist[i].apply(this, args)];
596             }
597             return args[0];
598         };
599     },
600         
601     /** @id MochiKit.Base.bind */
602     bind: function (func, self/* args... */) {
603         if (typeof(func) == "string") {
604             func = self[func];
605         }
606         var im_func = func.im_func;
607         var im_preargs = func.im_preargs;
608         var im_self = func.im_self;
609         var m = MochiKit.Base;
610         if (typeof(func) == "function" && typeof(func.apply) == "undefined") {
611             // this is for cases where JavaScript sucks ass and gives you a
612             // really dumb built-in function like alert() that doesn't have
613             // an apply
614             func = m._wrapDumbFunction(func);
615         }
616         if (typeof(im_func) != 'function') {
617             im_func = func;
618         }
619         if (typeof(self) != 'undefined') {
620             im_self = self;
621         }
622         if (typeof(im_preargs) == 'undefined') {
623             im_preargs = [];
624         } else  {
625             im_preargs = im_preargs.slice();
626         }
627         m.extend(im_preargs, arguments, 2);
628         var newfunc = function () {
629             var args = arguments;
630             var me = arguments.callee;
631             if (me.im_preargs.length > 0) {
632                 args = m.concat(me.im_preargs, args);
633             }
634             var self = me.im_self;
635             if (!self) {
636                 self = this;
637             }
638             return me.im_func.apply(self, args);
639         };
640         newfunc.im_self = im_self;
641         newfunc.im_func = im_func;
642         newfunc.im_preargs = im_preargs;
643         return newfunc;
644     },
646     /** @id MochiKit.Base.bindMethods */
647     bindMethods: function (self) {
648         var bind = MochiKit.Base.bind;
649         for (var k in self) {
650             var func = self[k];
651             if (typeof(func) == 'function') {
652                 self[k] = bind(func, self);
653             }
654         }
655     },
657     /** @id MochiKit.Base.registerComparator */
658     registerComparator: function (name, check, comparator, /* optional */ override) {
659         MochiKit.Base.comparatorRegistry.register(name, check, comparator, override);
660     },
662     _primitives: {'boolean': true, 'string': true, 'number': true},
664     /** @id MochiKit.Base.compare */
665     compare: function (a, b) {
666         if (a == b) {
667             return 0;
668         }
669         var aIsNull = (typeof(a) == 'undefined' || a === null);
670         var bIsNull = (typeof(b) == 'undefined' || b === null);
671         if (aIsNull && bIsNull) {
672             return 0;
673         } else if (aIsNull) {
674             return -1;
675         } else if (bIsNull) {
676             return 1;
677         }
678         var m = MochiKit.Base;
679         // bool, number, string have meaningful comparisons
680         var prim = m._primitives;
681         if (!(typeof(a) in prim && typeof(b) in prim)) {
682             try {
683                 return m.comparatorRegistry.match(a, b);
684             } catch (e) {
685                 if (e != m.NotFound) {
686                     throw e;
687                 }
688             }
689         }
690         if (a < b) {
691             return -1;
692         } else if (a > b) {
693             return 1;
694         }
695         // These types can't be compared
696         var repr = m.repr;
697         throw new TypeError(repr(a) + " and " + repr(b) + " can not be compared");
698     },
700     /** @id MochiKit.Base.compareDateLike */
701     compareDateLike: function (a, b) {
702         return MochiKit.Base.compare(a.getTime(), b.getTime());
703     },
705     /** @id MochiKit.Base.compareArrayLike */
706     compareArrayLike: function (a, b) {
707         var compare = MochiKit.Base.compare;
708         var count = a.length;
709         var rval = 0;
710         if (count > b.length) {
711             rval = 1;
712             count = b.length;
713         } else if (count < b.length) {
714             rval = -1;
715         }
716         for (var i = 0; i < count; i++) {
717             var cmp = compare(a[i], b[i]);
718             if (cmp) {
719                 return cmp;
720             }
721         }
722         return rval;
723     },
725     /** @id MochiKit.Base.registerRepr */
726     registerRepr: function (name, check, wrap, /* optional */override) {
727         MochiKit.Base.reprRegistry.register(name, check, wrap, override);
728     },
730     /** @id MochiKit.Base.repr */
731     repr: function (o) {
732         if (typeof(o) == "undefined") {
733             return "undefined";
734         } else if (o === null) {
735             return "null";
736         }
737         try {
738             if (typeof(o.__repr__) == 'function') {
739                 return o.__repr__();
740             } else if (typeof(o.repr) == 'function' && o.repr != arguments.callee) {
741                 return o.repr();
742             }
743             return MochiKit.Base.reprRegistry.match(o);
744         } catch (e) {
745             try {
746                 if (typeof(o.NAME) == 'string' && (
747                         o.toString == Function.prototype.toString ||
748                         o.toString == Object.prototype.toString
749                     )) {
750                     return o.NAME;
751                 }
752             } catch (e) {
753             }
754         }
755         try {
756             var ostring = (o + "");
757         } catch (e) {
758             return "[" + typeof(o) + "]";
759         }
760         if (typeof(o) == "function") {
761             o = ostring.replace(/^\s+/, "");
762             var idx = o.indexOf("{");
763             if (idx != -1) {
764                 o = o.substr(0, idx) + "{...}";
765             }
766         }
767         return ostring;
768     },
770     /** @id MochiKit.Base.reprArrayLike */
771     reprArrayLike: function (o) {
772         var m = MochiKit.Base;
773         return "[" + m.map(m.repr, o).join(", ") + "]";
774     },
776     /** @id MochiKit.Base.reprString */
777     reprString: function (o) { 
778         return ('"' + o.replace(/(["\\])/g, '\\$1') + '"'
779             ).replace(/[\f]/g, "\\f"
780             ).replace(/[\b]/g, "\\b"
781             ).replace(/[\n]/g, "\\n"
782             ).replace(/[\t]/g, "\\t"
783             ).replace(/[\r]/g, "\\r");
784     },
786     /** @id MochiKit.Base.reprNumber */
787     reprNumber: function (o) {
788         return o + "";
789     },
791     /** @id MochiKit.Base.registerJSON */
792     registerJSON: function (name, check, wrap, /* optional */override) {
793         MochiKit.Base.jsonRegistry.register(name, check, wrap, override);
794     },
797     /** @id MochiKit.Base.evalJSON */
798     evalJSON: function () {
799         return eval("(" + arguments[0] + ")");
800     },
802     /** @id MochiKit.Base.serializeJSON */
803     serializeJSON: function (o) {
804         var objtype = typeof(o);
805         if (objtype == "number" || objtype == "boolean") {
806             return o + "";
807         } else if (o === null) {
808             return "null";
809         }
810         var m = MochiKit.Base;
811         var reprString = m.reprString;
812         if (objtype == "string") {
813             return reprString(o);
814         }
815         // recurse
816         var me = arguments.callee;
817         // short-circuit for objects that support "json" serialization
818         // if they return "self" then just pass-through...
819         var newObj;
820         if (typeof(o.__json__) == "function") {
821             newObj = o.__json__();
822             if (o !== newObj) {
823                 return me(newObj);
824             }
825         }
826         if (typeof(o.json) == "function") {
827             newObj = o.json();
828             if (o !== newObj) {
829                 return me(newObj);
830             }
831         }
832         // array
833         if (objtype != "function" && typeof(o.length) == "number") {
834             var res = [];
835             for (var i = 0; i < o.length; i++) {
836                 var val = me(o[i]);
837                 if (typeof(val) != "string") {
838                     val = "undefined";
839                 }
840                 res.push(val);
841             }
842             return "[" + res.join(", ") + "]";
843         }
844         // look in the registry
845         try {
846             newObj = m.jsonRegistry.match(o);
847             if (o !== newObj) {
848                 return me(newObj);
849             }
850         } catch (e) {
851             if (e != m.NotFound) {
852                 // something really bad happened
853                 throw e;
854             }
855         }
856         // undefined is outside of the spec
857         if (objtype == "undefined") {
858             throw new TypeError("undefined can not be serialized as JSON");
859         }
860         // it's a function with no adapter, bad
861         if (objtype == "function") {
862             return null;
863         }
864         // generic object code path
865         res = [];
866         for (var k in o) {
867             var useKey;
868             if (typeof(k) == "number") {
869                 useKey = '"' + k + '"';
870             } else if (typeof(k) == "string") {
871                 useKey = reprString(k);
872             } else {
873                 // skip non-string or number keys
874                 continue;
875             }
876             val = me(o[k]);
877             if (typeof(val) != "string") {
878                 // skip non-serializable values
879                 continue;
880             }
881             res.push(useKey + ":" + val);
882         }
883         return "{" + res.join(", ") + "}";
884     },
885             
887     /** @id MochiKit.Base.objEqual */
888     objEqual: function (a, b) {
889         return (MochiKit.Base.compare(a, b) === 0);
890     },
892     /** @id MochiKit.Base.arrayEqual */
893     arrayEqual: function (self, arr) {
894         if (self.length != arr.length) {
895             return false;
896         }
897         return (MochiKit.Base.compare(self, arr) === 0);
898     },
900     /** @id MochiKit.Base.concat */
901     concat: function (/* lst... */) {
902         var rval = [];
903         var extend = MochiKit.Base.extend;
904         for (var i = 0; i < arguments.length; i++) {
905             extend(rval, arguments[i]);
906         }
907         return rval;
908     },
910     /** @id MochiKit.Base.keyComparator */
911     keyComparator: function (key/* ... */) {
912         // fast-path for single key comparisons
913         var m = MochiKit.Base;
914         var compare = m.compare;
915         if (arguments.length == 1) {
916             return function (a, b) {
917                 return compare(a[key], b[key]);
918             };
919         }
920         var compareKeys = m.extend(null, arguments);
921         return function (a, b) {
922             var rval = 0;
923             // keep comparing until something is inequal or we run out of
924             // keys to compare
925             for (var i = 0; (rval === 0) && (i < compareKeys.length); i++) {
926                 var key = compareKeys[i];
927                 rval = compare(a[key], b[key]);
928             }
929             return rval;
930         };
931     },
933     /** @id MochiKit.Base.reverseKeyComparator */
934     reverseKeyComparator: function (key) {
935         var comparator = MochiKit.Base.keyComparator.apply(this, arguments);
936         return function (a, b) {
937             return comparator(b, a);
938         };
939     },
941     /** @id MochiKit.Base.partial */
942     partial: function (func) {
943         var m = MochiKit.Base;
944         return m.bind.apply(this, m.extend([func, undefined], arguments, 1));
945     },
946      
947     /** @id MochiKit.Base.listMinMax */
948     listMinMax: function (which, lst) {
949         if (lst.length === 0) {
950             return null;
951         }
952         var cur = lst[0];
953         var compare = MochiKit.Base.compare;
954         for (var i = 1; i < lst.length; i++) {
955             var o = lst[i];
956             if (compare(o, cur) == which) {
957                 cur = o;
958             }
959         }
960         return cur;
961     },
963     /** @id MochiKit.Base.objMax */
964     objMax: function (/* obj... */) {
965         return MochiKit.Base.listMinMax(1, arguments);
966     },
967             
968     /** @id MochiKit.Base.objMin */
969     objMin: function (/* obj... */) {
970         return MochiKit.Base.listMinMax(-1, arguments);
971     },
973     /** @id MochiKit.Base.findIdentical */
974     findIdentical: function (lst, value, start/* = 0 */, /* optional */end) {
975         if (typeof(end) == "undefined" || end === null) {
976             end = lst.length;
977         }
978         if (typeof(start) == "undefined" || start === null) {
979             start = 0;
980         }
981         for (var i = start; i < end; i++) {
982             if (lst[i] === value) {
983                 return i;
984             }
985         }
986         return -1;
987     },
989     /** @id MochiKit.Base.mean */
990     mean: function(/* lst... */) {
991         /* http://www.nist.gov/dads/HTML/mean.html */
992         var sum = 0;
994         var m = MochiKit.Base;
995         var args = m.extend(null, arguments);
996         var count = args.length;
998         while (args.length) {
999             var o = args.shift();
1000             if (o && typeof(o) == "object" && typeof(o.length) == "number") {
1001                 count += o.length - 1;
1002                 for (var i = o.length - 1; i >= 0; i--) {
1003                     sum += o[i];
1004                 }
1005             } else {
1006                 sum += o;
1007             }
1008         }
1010         if (count <= 0) {
1011             throw new TypeError('mean() requires at least one argument');
1012         }
1014         return sum/count;
1015     },
1016     
1017     /** @id MochiKit.Base.median */
1018     median: function(/* lst... */) {
1019         /* http://www.nist.gov/dads/HTML/median.html */
1020         var data = MochiKit.Base.flattenArguments(arguments);
1021         if (data.length === 0) {
1022             throw new TypeError('median() requires at least one argument');
1023         }
1024         data.sort(compare);
1025         if (data.length % 2 == 0) {
1026             var upper = data.length / 2;
1027             return (data[upper] + data[upper - 1]) / 2;
1028         } else {
1029             return data[(data.length - 1) / 2];
1030         }
1031     },
1033     /** @id MochiKit.Base.findValue */
1034     findValue: function (lst, value, start/* = 0 */, /* optional */end) {
1035         if (typeof(end) == "undefined" || end === null) {
1036             end = lst.length;
1037         }
1038         if (typeof(start) == "undefined" || start === null) {
1039             start = 0;
1040         }
1041         var cmp = MochiKit.Base.compare;
1042         for (var i = start; i < end; i++) {
1043             if (cmp(lst[i], value) === 0) {
1044                 return i;
1045             }
1046         }
1047         return -1;
1048     },
1049     
1050     /** @id MochiKit.Base.nodeWalk */
1051     nodeWalk: function (node, visitor) {
1052         var nodes = [node];
1053         var extend = MochiKit.Base.extend;
1054         while (nodes.length) {
1055             var res = visitor(nodes.shift());
1056             if (res) {
1057                 extend(nodes, res);
1058             }
1059         }
1060     },
1062        
1063     /** @id MochiKit.Base.nameFunctions */
1064     nameFunctions: function (namespace) {
1065         var base = namespace.NAME;
1066         if (typeof(base) == 'undefined') {
1067             base = '';
1068         } else {
1069             base = base + '.';
1070         }
1071         for (var name in namespace) {
1072             var o = namespace[name];
1073             if (typeof(o) == 'function' && typeof(o.NAME) == 'undefined') {
1074                 try {
1075                     o.NAME = base + name;
1076                 } catch (e) {
1077                     // pass
1078                 }
1079             }
1080         }
1081     },
1084     /** @id MochiKit.Base.queryString */
1085     queryString: function (names, values) {
1086         // check to see if names is a string or a DOM element, and if
1087         // MochiKit.DOM is available.  If so, drop it like it's a form
1088         // Ugliest conditional in MochiKit?  Probably!
1089         if (typeof(MochiKit.DOM) != "undefined" && arguments.length == 1
1090             && (typeof(names) == "string" || (
1091                 typeof(names.nodeType) != "undefined" && names.nodeType > 0
1092             ))
1093         ) {
1094             var kv = MochiKit.DOM.formContents(names);
1095             names = kv[0];
1096             values = kv[1];
1097         } else if (arguments.length == 1) {
1098             var o = names;
1099             names = [];
1100             values = [];
1101             for (var k in o) {
1102                 var v = o[k];
1103                 if (typeof(v) == "function") {
1104                     continue;
1105                 } else if (typeof(v) != "string" &&
1106                         typeof(v.length) == "number") {
1107                     for (var i = 0; i < v.length; i++) {
1108                         names.push(k);
1109                         values.push(v[i]);
1110                     }
1111                 } else {
1112                     names.push(k);
1113                     values.push(v);
1114                 }
1115             }
1116         }
1117         var rval = [];
1118         var len = Math.min(names.length, values.length);
1119         var urlEncode = MochiKit.Base.urlEncode;
1120         for (var i = 0; i < len; i++) {
1121             v = values[i];
1122             if (typeof(v) != 'undefined' && v !== null) {
1123                 rval.push(urlEncode(names[i]) + "=" + urlEncode(v));
1124             }
1125         }
1126         return rval.join("&");
1127     },
1130     /** @id MochiKit.Base.parseQueryString */
1131     parseQueryString: function (encodedString, useArrays) {
1132         // strip a leading '?' from the encoded string
1133         var qstr = (encodedString[0] == "?") ? encodedString.substring(1) : 
1134                                                encodedString;
1135         var pairs = qstr.replace(/\+/g, "%20").split(/(\&amp\;|\&\#38\;|\&#x26;|\&)/);
1136         var o = {};
1137         var decode;
1138         if (typeof(decodeURIComponent) != "undefined") {
1139             decode = decodeURIComponent;
1140         } else {
1141             decode = unescape;
1142         }
1143         if (useArrays) {
1144             for (var i = 0; i < pairs.length; i++) {
1145                 var pair = pairs[i].split("=");
1146                 if (pair.length !== 2) {
1147                     continue;
1148                 }
1149                 var name = decode(pair[0]);
1150                 var arr = o[name];
1151                 if (!(arr instanceof Array)) {
1152                     arr = [];
1153                     o[name] = arr;
1154                 }
1155                 arr.push(decode(pair[1]));
1156             }
1157         } else {
1158             for (i = 0; i < pairs.length; i++) {
1159                 pair = pairs[i].split("=");
1160                 if (pair.length !== 2) {
1161                     continue;
1162                 }
1163                 o[decode(pair[0])] = decode(pair[1]);
1164             }
1165         }
1166         return o;
1167     }
1169     
1170 /** @id MochiKit.Base.AdapterRegistry */
1171 MochiKit.Base.AdapterRegistry = function () {
1172     this.pairs = [];
1175 MochiKit.Base.AdapterRegistry.prototype = {
1176     /** @id MochiKit.Base.AdapterRegistry.prototype.register */
1177     register: function (name, check, wrap, /* optional */ override) {
1178         if (override) {
1179             this.pairs.unshift([name, check, wrap]);
1180         } else {
1181             this.pairs.push([name, check, wrap]);
1182         }
1183     },
1185     /** @id MochiKit.Base.AdapterRegistry.prototype.match */
1186     match: function (/* ... */) {
1187         for (var i = 0; i < this.pairs.length; i++) {
1188             var pair = this.pairs[i];
1189             if (pair[1].apply(this, arguments)) {
1190                 return pair[2].apply(this, arguments);
1191             }
1192         }
1193         throw MochiKit.Base.NotFound;
1194     },
1196     /** @id MochiKit.Base.AdapterRegistry.prototype.unregister */
1197     unregister: function (name) {
1198         for (var i = 0; i < this.pairs.length; i++) {
1199             var pair = this.pairs[i];
1200             if (pair[0] == name) {
1201                 this.pairs.splice(i, 1);
1202                 return true;
1203             }
1204         }
1205         return false;
1206     }
1210 MochiKit.Base.EXPORT = [
1211     "flattenArray",
1212     "noop",
1213     "camelize",
1214     "counter",
1215     "clone",
1216     "extend",
1217     "update",
1218     "updatetree",
1219     "setdefault",
1220     "keys",
1221     "values",
1222     "items",
1223     "NamedError",
1224     "operator",
1225     "forwardCall",
1226     "itemgetter",
1227     "typeMatcher",
1228     "isCallable",
1229     "isUndefined",
1230     "isUndefinedOrNull",
1231     "isNull",
1232     "isEmpty",
1233     "isNotEmpty",
1234     "isArrayLike",
1235     "isDateLike",
1236     "xmap",
1237     "map",
1238     "xfilter",
1239     "filter",
1240     "methodcaller",
1241     "compose",
1242     "bind",
1243     "bindMethods",
1244     "NotFound",
1245     "AdapterRegistry",
1246     "registerComparator",
1247     "compare",
1248     "registerRepr",
1249     "repr",
1250     "objEqual",
1251     "arrayEqual",
1252     "concat",
1253     "keyComparator",
1254     "reverseKeyComparator",
1255     "partial",
1256     "merge",
1257     "listMinMax",
1258     "listMax",
1259     "listMin",
1260     "objMax",
1261     "objMin",
1262     "nodeWalk",
1263     "zip",
1264     "urlEncode",
1265     "queryString",
1266     "serializeJSON",
1267     "registerJSON",
1268     "evalJSON",
1269     "parseQueryString",
1270     "findValue",
1271     "findIdentical",
1272     "flattenArguments",
1273     "method",
1274     "average",
1275     "mean",
1276     "median"
1279 MochiKit.Base.EXPORT_OK = [
1280     "nameFunctions",
1281     "comparatorRegistry",
1282     "reprRegistry",
1283     "jsonRegistry",
1284     "compareDateLike",
1285     "compareArrayLike",
1286     "reprArrayLike",
1287     "reprString",
1288     "reprNumber"
1291 MochiKit.Base._exportSymbols = function (globals, module) {
1292     if (!MochiKit.__export__) {
1293         return;
1294     }
1295     var all = module.EXPORT_TAGS[":all"];
1296     for (var i = 0; i < all.length; i++) {
1297         globals[all[i]] = module[all[i]];
1298     }
1301 MochiKit.Base.__new__ = function () {
1302     // A singleton raised when no suitable adapter is found
1303     var m = this;
1305     // convenience
1306     /** @id MochiKit.Base.noop */
1307     m.noop = m.operator.identity;
1308     
1309     // Backwards compat
1310     m.forward = m.forwardCall;
1311     m.find = m.findValue;
1313     if (typeof(encodeURIComponent) != "undefined") {
1314         /** @id MochiKit.Base.urlEncode */
1315         m.urlEncode = function (unencoded) {
1316             return encodeURIComponent(unencoded).replace(/\'/g, '%27');
1317         };
1318     } else {
1319         m.urlEncode = function (unencoded) {
1320             return escape(unencoded
1321                 ).replace(/\+/g, '%2B'
1322                 ).replace(/\"/g,'%22'
1323                 ).rval.replace(/\'/g, '%27');
1324         };
1325     }
1327     /** @id MochiKit.Base.NamedError */
1328     m.NamedError = function (name) {
1329         this.message = name;
1330         this.name = name;
1331     };
1332     m.NamedError.prototype = new Error();
1333     m.update(m.NamedError.prototype, {
1334         repr: function () {
1335             if (this.message && this.message != this.name) {
1336                 return this.name + "(" + m.repr(this.message) + ")";
1337             } else {
1338                 return this.name + "()";
1339             }
1340         },
1341         toString: m.forwardCall("repr")
1342     });
1344     /** @id MochiKit.Base.NotFound */
1345     m.NotFound = new m.NamedError("MochiKit.Base.NotFound");
1348     /** @id MochiKit.Base.listMax */
1349     m.listMax = m.partial(m.listMinMax, 1);
1350     /** @id MochiKit.Base.listMin */
1351     m.listMin = m.partial(m.listMinMax, -1);
1353     /** @id MochiKit.Base.isCallable */
1354     m.isCallable = m.typeMatcher('function');
1355     /** @id MochiKit.Base.isUndefined */
1356     m.isUndefined = m.typeMatcher('undefined');
1358     /** @id MochiKit.Base.merge */
1359     m.merge = m.partial(m.update, null);
1360     /** @id MochiKit.Base.zip */
1361     m.zip = m.partial(m.map, null);
1363     /** @id MochiKit.Base.average */    
1364     m.average = m.mean;
1366     /** @id MochiKit.Base.comparatorRegistry */
1367     m.comparatorRegistry = new m.AdapterRegistry();
1368     m.registerComparator("dateLike", m.isDateLike, m.compareDateLike);
1369     m.registerComparator("arrayLike", m.isArrayLike, m.compareArrayLike);
1371     /** @id MochiKit.Base.reprRegistry */
1372     m.reprRegistry = new m.AdapterRegistry();
1373     m.registerRepr("arrayLike", m.isArrayLike, m.reprArrayLike);
1374     m.registerRepr("string", m.typeMatcher("string"), m.reprString);
1375     m.registerRepr("numbers", m.typeMatcher("number", "boolean"), m.reprNumber);
1377     /** @id MochiKit.Base.jsonRegistry */
1378     m.jsonRegistry = new m.AdapterRegistry();
1380     var all = m.concat(m.EXPORT, m.EXPORT_OK);
1381     m.EXPORT_TAGS = {
1382         ":common": m.concat(m.EXPORT_OK),
1383         ":all": all
1384     };
1386     m.nameFunctions(this);
1390 MochiKit.Base.__new__();
1393 // XXX: Internet Explorer blows
1395 if (MochiKit.__export__) {
1396     compare = MochiKit.Base.compare;
1397     compose = MochiKit.Base.compose;
1398     serializeJSON = MochiKit.Base.serializeJSON;
1401 MochiKit.Base._exportSymbols(this, MochiKit.Base);