Initial check-in
[dygraphs.git] / mochikit_v14 / MochiKit / Base.js
1 /***
2
3 MochiKit.Base 1.4
4
5 See <http://mochikit.com/> for documentation, downloads, license, etc.
6
7 (c) 2005 Bob Ippolito. All rights Reserved.
8
9 ***/
10
11 if (typeof(dojo) != 'undefined') {
12 dojo.provide("MochiKit.Base");
13 }
14 if (typeof(MochiKit) == 'undefined') {
15 MochiKit = {};
16 }
17 if (typeof(MochiKit.Base) == 'undefined') {
18 MochiKit.Base = {};
19 }
20 if (typeof(MochiKit.__export__) == "undefined") {
21 MochiKit.__export__ = (MochiKit.__compat__ ||
22 (typeof(JSAN) == 'undefined' && typeof(dojo) == 'undefined')
23 );
24 }
25
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;
42 };
43
44 MochiKit.Base.update(MochiKit.Base, {
45 __repr__: function () {
46 return "[" + this.NAME + " " + this.VERSION + "]";
47 },
48
49 toString: function () {
50 return this.__repr__();
51 },
52
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 },
63
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 },
117
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 },
149
150
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 },
171
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 },
187
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 },
205
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 },
221
222
223 _newNamedError: function (module, name, func) {
224 func.prototype = new MochiKit.Base.NamedError(module.NAME + "." + name);
225 module[name] = func;
226 },
227
228
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; },
238
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; },
244
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; },
256
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; },
270
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; },
284
285 // strict built-in comparators
286 seq: function (a, b) { return a === b; },
287 sne: function (a, b) { return a !== b; },
288
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; },
302
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 },
311
312 /** @id MochiKit.Base.forwardCall */
313 forwardCall: function (func) {
314 return function () {
315 return this[func].apply(this, arguments);
316 };
317 },
318
319 /** @id MochiKit.Base.itemgetter */
320 itemgetter: function (func) {
321 return function (arg) {
322 return arg[func];
323 };
324 },
325
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 },
342
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 },
352
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 },
363
364 /** @id MochiKit.Base.isEmpty */
365 isEmpty: function (obj) {
366 return !MochiKit.Base.isNotEmpty.apply(this, arguments);
367 },
368
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 },
379
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 },
396
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 },
408
409
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 },
421
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 },
488
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 },
503
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 },
539
540
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 },
557
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 },
577
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 },
645
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 },
656
657 /** @id MochiKit.Base.registerComparator */
658 registerComparator: function (name, check, comparator, /* optional */ override) {
659 MochiKit.Base.comparatorRegistry.register(name, check, comparator, override);
660 },
661
662 _primitives: {'boolean': true, 'string': true, 'number': true},
663
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 },
699
700 /** @id MochiKit.Base.compareDateLike */
701 compareDateLike: function (a, b) {
702 return MochiKit.Base.compare(a.getTime(), b.getTime());
703 },
704
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 },
724
725 /** @id MochiKit.Base.registerRepr */
726 registerRepr: function (name, check, wrap, /* optional */override) {
727 MochiKit.Base.reprRegistry.register(name, check, wrap, override);
728 },
729
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 if (typeof(o.NAME) == 'string' && (
746 o.toString == Function.prototype.toString ||
747 o.toString == Object.prototype.toString
748 )) {
749 return o.NAME;
750 }
751 }
752 try {
753 var ostring = (o + "");
754 } catch (e) {
755 return "[" + typeof(o) + "]";
756 }
757 if (typeof(o) == "function") {
758 o = ostring.replace(/^\s+/, "");
759 var idx = o.indexOf("{");
760 if (idx != -1) {
761 o = o.substr(0, idx) + "{...}";
762 }
763 }
764 return ostring;
765 },
766
767 /** @id MochiKit.Base.reprArrayLike */
768 reprArrayLike: function (o) {
769 var m = MochiKit.Base;
770 return "[" + m.map(m.repr, o).join(", ") + "]";
771 },
772
773 /** @id MochiKit.Base.reprString */
774 reprString: function (o) {
775 return ('"' + o.replace(/(["\\])/g, '\\$1') + '"'
776 ).replace(/[\f]/g, "\\f"
777 ).replace(/[\b]/g, "\\b"
778 ).replace(/[\n]/g, "\\n"
779 ).replace(/[\t]/g, "\\t"
780 ).replace(/[\r]/g, "\\r");
781 },
782
783 /** @id MochiKit.Base.reprNumber */
784 reprNumber: function (o) {
785 return o + "";
786 },
787
788 /** @id MochiKit.Base.registerJSON */
789 registerJSON: function (name, check, wrap, /* optional */override) {
790 MochiKit.Base.jsonRegistry.register(name, check, wrap, override);
791 },
792
793
794 /** @id MochiKit.Base.evalJSON */
795 evalJSON: function () {
796 return eval("(" + arguments[0] + ")");
797 },
798
799 /** @id MochiKit.Base.serializeJSON */
800 serializeJSON: function (o) {
801 var objtype = typeof(o);
802 if (objtype == "number" || objtype == "boolean") {
803 return o + "";
804 } else if (o === null) {
805 return "null";
806 }
807 var m = MochiKit.Base;
808 var reprString = m.reprString;
809 if (objtype == "string") {
810 return reprString(o);
811 }
812 // recurse
813 var me = arguments.callee;
814 // short-circuit for objects that support "json" serialization
815 // if they return "self" then just pass-through...
816 var newObj;
817 if (typeof(o.__json__) == "function") {
818 newObj = o.__json__();
819 if (o !== newObj) {
820 return me(newObj);
821 }
822 }
823 if (typeof(o.json) == "function") {
824 newObj = o.json();
825 if (o !== newObj) {
826 return me(newObj);
827 }
828 }
829 // array
830 if (objtype != "function" && typeof(o.length) == "number") {
831 var res = [];
832 for (var i = 0; i < o.length; i++) {
833 var val = me(o[i]);
834 if (typeof(val) != "string") {
835 val = "undefined";
836 }
837 res.push(val);
838 }
839 return "[" + res.join(", ") + "]";
840 }
841 // look in the registry
842 try {
843 newObj = m.jsonRegistry.match(o);
844 if (o !== newObj) {
845 return me(newObj);
846 }
847 } catch (e) {
848 if (e != m.NotFound) {
849 // something really bad happened
850 throw e;
851 }
852 }
853 // undefined is outside of the spec
854 if (objtype == "undefined") {
855 throw new TypeError("undefined can not be serialized as JSON");
856 }
857 // it's a function with no adapter, bad
858 if (objtype == "function") {
859 return null;
860 }
861 // generic object code path
862 res = [];
863 for (var k in o) {
864 var useKey;
865 if (typeof(k) == "number") {
866 useKey = '"' + k + '"';
867 } else if (typeof(k) == "string") {
868 useKey = reprString(k);
869 } else {
870 // skip non-string or number keys
871 continue;
872 }
873 val = me(o[k]);
874 if (typeof(val) != "string") {
875 // skip non-serializable values
876 continue;
877 }
878 res.push(useKey + ":" + val);
879 }
880 return "{" + res.join(", ") + "}";
881 },
882
883
884 /** @id MochiKit.Base.objEqual */
885 objEqual: function (a, b) {
886 return (MochiKit.Base.compare(a, b) === 0);
887 },
888
889 /** @id MochiKit.Base.arrayEqual */
890 arrayEqual: function (self, arr) {
891 if (self.length != arr.length) {
892 return false;
893 }
894 return (MochiKit.Base.compare(self, arr) === 0);
895 },
896
897 /** @id MochiKit.Base.concat */
898 concat: function (/* lst... */) {
899 var rval = [];
900 var extend = MochiKit.Base.extend;
901 for (var i = 0; i < arguments.length; i++) {
902 extend(rval, arguments[i]);
903 }
904 return rval;
905 },
906
907 /** @id MochiKit.Base.keyComparator */
908 keyComparator: function (key/* ... */) {
909 // fast-path for single key comparisons
910 var m = MochiKit.Base;
911 var compare = m.compare;
912 if (arguments.length == 1) {
913 return function (a, b) {
914 return compare(a[key], b[key]);
915 };
916 }
917 var compareKeys = m.extend(null, arguments);
918 return function (a, b) {
919 var rval = 0;
920 // keep comparing until something is inequal or we run out of
921 // keys to compare
922 for (var i = 0; (rval === 0) && (i < compareKeys.length); i++) {
923 var key = compareKeys[i];
924 rval = compare(a[key], b[key]);
925 }
926 return rval;
927 };
928 },
929
930 /** @id MochiKit.Base.reverseKeyComparator */
931 reverseKeyComparator: function (key) {
932 var comparator = MochiKit.Base.keyComparator.apply(this, arguments);
933 return function (a, b) {
934 return comparator(b, a);
935 };
936 },
937
938 /** @id MochiKit.Base.partial */
939 partial: function (func) {
940 var m = MochiKit.Base;
941 return m.bind.apply(this, m.extend([func, undefined], arguments, 1));
942 },
943
944 /** @id MochiKit.Base.listMinMax */
945 listMinMax: function (which, lst) {
946 if (lst.length === 0) {
947 return null;
948 }
949 var cur = lst[0];
950 var compare = MochiKit.Base.compare;
951 for (var i = 1; i < lst.length; i++) {
952 var o = lst[i];
953 if (compare(o, cur) == which) {
954 cur = o;
955 }
956 }
957 return cur;
958 },
959
960 /** @id MochiKit.Base.objMax */
961 objMax: function (/* obj... */) {
962 return MochiKit.Base.listMinMax(1, arguments);
963 },
964
965 /** @id MochiKit.Base.objMin */
966 objMin: function (/* obj... */) {
967 return MochiKit.Base.listMinMax(-1, arguments);
968 },
969
970 /** @id MochiKit.Base.findIdentical */
971 findIdentical: function (lst, value, start/* = 0 */, /* optional */end) {
972 if (typeof(end) == "undefined" || end === null) {
973 end = lst.length;
974 }
975 if (typeof(start) == "undefined" || start === null) {
976 start = 0;
977 }
978 for (var i = start; i < end; i++) {
979 if (lst[i] === value) {
980 return i;
981 }
982 }
983 return -1;
984 },
985
986 /** @id MochiKit.Base.mean */
987 mean: function(/* lst... */) {
988 /* http://www.nist.gov/dads/HTML/mean.html */
989 var sum = 0;
990
991 var m = MochiKit.Base;
992 var args = m.extend(null, arguments);
993 var count = args.length;
994
995 while (args.length) {
996 var o = args.shift();
997 if (o && typeof(o) == "object" && typeof(o.length) == "number") {
998 count += o.length - 1;
999 for (var i = o.length - 1; i >= 0; i--) {
1000 sum += o[i];
1001 }
1002 } else {
1003 sum += o;
1004 }
1005 }
1006
1007 if (count <= 0) {
1008 throw new TypeError('mean() requires at least one argument');
1009 }
1010
1011 return sum/count;
1012 },
1013
1014 /** @id MochiKit.Base.median */
1015 median: function(/* lst... */) {
1016 /* http://www.nist.gov/dads/HTML/median.html */
1017 var data = MochiKit.Base.flattenArguments(arguments);
1018 if (data.length === 0) {
1019 throw new TypeError('median() requires at least one argument');
1020 }
1021 data.sort(compare);
1022 if (data.length % 2 == 0) {
1023 var upper = data.length / 2;
1024 return (data[upper] + data[upper - 1]) / 2;
1025 } else {
1026 return data[(data.length - 1) / 2];
1027 }
1028 },
1029
1030 /** @id MochiKit.Base.findValue */
1031 findValue: function (lst, value, start/* = 0 */, /* optional */end) {
1032 if (typeof(end) == "undefined" || end === null) {
1033 end = lst.length;
1034 }
1035 if (typeof(start) == "undefined" || start === null) {
1036 start = 0;
1037 }
1038 var cmp = MochiKit.Base.compare;
1039 for (var i = start; i < end; i++) {
1040 if (cmp(lst[i], value) === 0) {
1041 return i;
1042 }
1043 }
1044 return -1;
1045 },
1046
1047 /** @id MochiKit.Base.nodeWalk */
1048 nodeWalk: function (node, visitor) {
1049 var nodes = [node];
1050 var extend = MochiKit.Base.extend;
1051 while (nodes.length) {
1052 var res = visitor(nodes.shift());
1053 if (res) {
1054 extend(nodes, res);
1055 }
1056 }
1057 },
1058
1059
1060 /** @id MochiKit.Base.nameFunctions */
1061 nameFunctions: function (namespace) {
1062 var base = namespace.NAME;
1063 if (typeof(base) == 'undefined') {
1064 base = '';
1065 } else {
1066 base = base + '.';
1067 }
1068 for (var name in namespace) {
1069 var o = namespace[name];
1070 if (typeof(o) == 'function' && typeof(o.NAME) == 'undefined') {
1071 try {
1072 o.NAME = base + name;
1073 } catch (e) {
1074 // pass
1075 }
1076 }
1077 }
1078 },
1079
1080
1081 /** @id MochiKit.Base.queryString */
1082 queryString: function (names, values) {
1083 // check to see if names is a string or a DOM element, and if
1084 // MochiKit.DOM is available. If so, drop it like it's a form
1085 // Ugliest conditional in MochiKit? Probably!
1086 if (typeof(MochiKit.DOM) != "undefined" && arguments.length == 1
1087 && (typeof(names) == "string" || (
1088 typeof(names.nodeType) != "undefined" && names.nodeType > 0
1089 ))
1090 ) {
1091 var kv = MochiKit.DOM.formContents(names);
1092 names = kv[0];
1093 values = kv[1];
1094 } else if (arguments.length == 1) {
1095 // Allow the return value of formContents to be passed directly
1096 if (typeof(names.length) == "number" && names.length == 2) {
1097 return arguments.callee(names[0], names[1]);
1098 }
1099 var o = names;
1100 names = [];
1101 values = [];
1102 for (var k in o) {
1103 var v = o[k];
1104 if (typeof(v) == "function") {
1105 continue;
1106 } else if (typeof(v) != "string" &&
1107 typeof(v.length) == "number") {
1108 for (var i = 0; i < v.length; i++) {
1109 names.push(k);
1110 values.push(v[i]);
1111 }
1112 } else {
1113 names.push(k);
1114 values.push(v);
1115 }
1116 }
1117 }
1118 var rval = [];
1119 var len = Math.min(names.length, values.length);
1120 var urlEncode = MochiKit.Base.urlEncode;
1121 for (var i = 0; i < len; i++) {
1122 v = values[i];
1123 if (typeof(v) != 'undefined' && v !== null) {
1124 rval.push(urlEncode(names[i]) + "=" + urlEncode(v));
1125 }
1126 }
1127 return rval.join("&");
1128 },
1129
1130
1131 /** @id MochiKit.Base.parseQueryString */
1132 parseQueryString: function (encodedString, useArrays) {
1133 // strip a leading '?' from the encoded string
1134 var qstr = (encodedString.charAt(0) == "?")
1135 ? encodedString.substring(1)
1136 : encodedString;
1137 var pairs = qstr.replace(/\+/g, "%20").split(/(\&amp\;|\&\#38\;|\&#x26;|\&)/);
1138 var o = {};
1139 var decode;
1140 if (typeof(decodeURIComponent) != "undefined") {
1141 decode = decodeURIComponent;
1142 } else {
1143 decode = unescape;
1144 }
1145 if (useArrays) {
1146 for (var i = 0; i < pairs.length; i++) {
1147 var pair = pairs[i].split("=");
1148 var name = decode(pair.shift());
1149 if (!name) {
1150 continue;
1151 }
1152 var arr = o[name];
1153 if (!(arr instanceof Array)) {
1154 arr = [];
1155 o[name] = arr;
1156 }
1157 arr.push(decode(pair.join("=")));
1158 }
1159 } else {
1160 for (i = 0; i < pairs.length; i++) {
1161 pair = pairs[i].split("=");
1162 var name = pair.shift();
1163 if (!name) {
1164 continue;
1165 }
1166 o[decode(name)] = decode(pair.join("="));
1167 }
1168 }
1169 return o;
1170 }
1171 });
1172
1173 /** @id MochiKit.Base.AdapterRegistry */
1174 MochiKit.Base.AdapterRegistry = function () {
1175 this.pairs = [];
1176 };
1177
1178 MochiKit.Base.AdapterRegistry.prototype = {
1179 /** @id MochiKit.Base.AdapterRegistry.prototype.register */
1180 register: function (name, check, wrap, /* optional */ override) {
1181 if (override) {
1182 this.pairs.unshift([name, check, wrap]);
1183 } else {
1184 this.pairs.push([name, check, wrap]);
1185 }
1186 },
1187
1188 /** @id MochiKit.Base.AdapterRegistry.prototype.match */
1189 match: function (/* ... */) {
1190 for (var i = 0; i < this.pairs.length; i++) {
1191 var pair = this.pairs[i];
1192 if (pair[1].apply(this, arguments)) {
1193 return pair[2].apply(this, arguments);
1194 }
1195 }
1196 throw MochiKit.Base.NotFound;
1197 },
1198
1199 /** @id MochiKit.Base.AdapterRegistry.prototype.unregister */
1200 unregister: function (name) {
1201 for (var i = 0; i < this.pairs.length; i++) {
1202 var pair = this.pairs[i];
1203 if (pair[0] == name) {
1204 this.pairs.splice(i, 1);
1205 return true;
1206 }
1207 }
1208 return false;
1209 }
1210 };
1211
1212
1213 MochiKit.Base.EXPORT = [
1214 "flattenArray",
1215 "noop",
1216 "camelize",
1217 "counter",
1218 "clone",
1219 "extend",
1220 "update",
1221 "updatetree",
1222 "setdefault",
1223 "keys",
1224 "values",
1225 "items",
1226 "NamedError",
1227 "operator",
1228 "forwardCall",
1229 "itemgetter",
1230 "typeMatcher",
1231 "isCallable",
1232 "isUndefined",
1233 "isUndefinedOrNull",
1234 "isNull",
1235 "isEmpty",
1236 "isNotEmpty",
1237 "isArrayLike",
1238 "isDateLike",
1239 "xmap",
1240 "map",
1241 "xfilter",
1242 "filter",
1243 "methodcaller",
1244 "compose",
1245 "bind",
1246 "bindMethods",
1247 "NotFound",
1248 "AdapterRegistry",
1249 "registerComparator",
1250 "compare",
1251 "registerRepr",
1252 "repr",
1253 "objEqual",
1254 "arrayEqual",
1255 "concat",
1256 "keyComparator",
1257 "reverseKeyComparator",
1258 "partial",
1259 "merge",
1260 "listMinMax",
1261 "listMax",
1262 "listMin",
1263 "objMax",
1264 "objMin",
1265 "nodeWalk",
1266 "zip",
1267 "urlEncode",
1268 "queryString",
1269 "serializeJSON",
1270 "registerJSON",
1271 "evalJSON",
1272 "parseQueryString",
1273 "findValue",
1274 "findIdentical",
1275 "flattenArguments",
1276 "method",
1277 "average",
1278 "mean",
1279 "median"
1280 ];
1281
1282 MochiKit.Base.EXPORT_OK = [
1283 "nameFunctions",
1284 "comparatorRegistry",
1285 "reprRegistry",
1286 "jsonRegistry",
1287 "compareDateLike",
1288 "compareArrayLike",
1289 "reprArrayLike",
1290 "reprString",
1291 "reprNumber"
1292 ];
1293
1294 MochiKit.Base._exportSymbols = function (globals, module) {
1295 if (!MochiKit.__export__) {
1296 return;
1297 }
1298 var all = module.EXPORT_TAGS[":all"];
1299 for (var i = 0; i < all.length; i++) {
1300 globals[all[i]] = module[all[i]];
1301 }
1302 };
1303
1304 MochiKit.Base.__new__ = function () {
1305 // A singleton raised when no suitable adapter is found
1306 var m = this;
1307
1308 // convenience
1309 /** @id MochiKit.Base.noop */
1310 m.noop = m.operator.identity;
1311
1312 // Backwards compat
1313 m.forward = m.forwardCall;
1314 m.find = m.findValue;
1315
1316 if (typeof(encodeURIComponent) != "undefined") {
1317 /** @id MochiKit.Base.urlEncode */
1318 m.urlEncode = function (unencoded) {
1319 return encodeURIComponent(unencoded).replace(/\'/g, '%27');
1320 };
1321 } else {
1322 m.urlEncode = function (unencoded) {
1323 return escape(unencoded
1324 ).replace(/\+/g, '%2B'
1325 ).replace(/\"/g,'%22'
1326 ).rval.replace(/\'/g, '%27');
1327 };
1328 }
1329
1330 /** @id MochiKit.Base.NamedError */
1331 m.NamedError = function (name) {
1332 this.message = name;
1333 this.name = name;
1334 };
1335 m.NamedError.prototype = new Error();
1336 m.update(m.NamedError.prototype, {
1337 repr: function () {
1338 if (this.message && this.message != this.name) {
1339 return this.name + "(" + m.repr(this.message) + ")";
1340 } else {
1341 return this.name + "()";
1342 }
1343 },
1344 toString: m.forwardCall("repr")
1345 });
1346
1347 /** @id MochiKit.Base.NotFound */
1348 m.NotFound = new m.NamedError("MochiKit.Base.NotFound");
1349
1350
1351 /** @id MochiKit.Base.listMax */
1352 m.listMax = m.partial(m.listMinMax, 1);
1353 /** @id MochiKit.Base.listMin */
1354 m.listMin = m.partial(m.listMinMax, -1);
1355
1356 /** @id MochiKit.Base.isCallable */
1357 m.isCallable = m.typeMatcher('function');
1358 /** @id MochiKit.Base.isUndefined */
1359 m.isUndefined = m.typeMatcher('undefined');
1360
1361 /** @id MochiKit.Base.merge */
1362 m.merge = m.partial(m.update, null);
1363 /** @id MochiKit.Base.zip */
1364 m.zip = m.partial(m.map, null);
1365
1366 /** @id MochiKit.Base.average */
1367 m.average = m.mean;
1368
1369 /** @id MochiKit.Base.comparatorRegistry */
1370 m.comparatorRegistry = new m.AdapterRegistry();
1371 m.registerComparator("dateLike", m.isDateLike, m.compareDateLike);
1372 m.registerComparator("arrayLike", m.isArrayLike, m.compareArrayLike);
1373
1374 /** @id MochiKit.Base.reprRegistry */
1375 m.reprRegistry = new m.AdapterRegistry();
1376 m.registerRepr("arrayLike", m.isArrayLike, m.reprArrayLike);
1377 m.registerRepr("string", m.typeMatcher("string"), m.reprString);
1378 m.registerRepr("numbers", m.typeMatcher("number", "boolean"), m.reprNumber);
1379
1380 /** @id MochiKit.Base.jsonRegistry */
1381 m.jsonRegistry = new m.AdapterRegistry();
1382
1383 var all = m.concat(m.EXPORT, m.EXPORT_OK);
1384 m.EXPORT_TAGS = {
1385 ":common": m.concat(m.EXPORT_OK),
1386 ":all": all
1387 };
1388
1389 m.nameFunctions(this);
1390
1391 };
1392
1393 MochiKit.Base.__new__();
1394
1395 //
1396 // XXX: Internet Explorer blows
1397 //
1398 if (MochiKit.__export__) {
1399 compare = MochiKit.Base.compare;
1400 compose = MochiKit.Base.compose;
1401 serializeJSON = MochiKit.Base.serializeJSON;
1402 }
1403
1404 MochiKit.Base._exportSymbols(this, MochiKit.Base);