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