michael@0: /*** michael@0: michael@0: MochiKit.Base 1.4 michael@0: michael@0: See for documentation, downloads, license, etc. michael@0: michael@0: (c) 2005 Bob Ippolito. All rights Reserved. michael@0: michael@0: ***/ michael@0: michael@0: if (typeof(dojo) != 'undefined') { michael@0: dojo.provide("MochiKit.Base"); michael@0: } michael@0: if (typeof(MochiKit) == 'undefined') { michael@0: MochiKit = {}; michael@0: } michael@0: if (typeof(MochiKit.Base) == 'undefined') { michael@0: MochiKit.Base = {}; michael@0: } michael@0: if (typeof(MochiKit.__export__) == "undefined") { michael@0: MochiKit.__export__ = (MochiKit.__compat__ || michael@0: (typeof(JSAN) == 'undefined' && typeof(dojo) == 'undefined') michael@0: ); michael@0: } michael@0: michael@0: MochiKit.Base.VERSION = "1.4"; michael@0: MochiKit.Base.NAME = "MochiKit.Base"; michael@0: /** @id MochiKit.Base.update */ michael@0: MochiKit.Base.update = function (self, obj/*, ... */) { michael@0: if (self === null) { michael@0: self = {}; michael@0: } michael@0: for (var i = 1; i < arguments.length; i++) { michael@0: var o = arguments[i]; michael@0: if (typeof(o) != 'undefined' && o !== null) { michael@0: for (var k in o) { michael@0: self[k] = o[k]; michael@0: } michael@0: } michael@0: } michael@0: return self; michael@0: }; michael@0: michael@0: MochiKit.Base.update(MochiKit.Base, { michael@0: __repr__: function () { michael@0: return "[" + this.NAME + " " + this.VERSION + "]"; michael@0: }, michael@0: michael@0: toString: function () { michael@0: return this.__repr__(); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.camelize */ michael@0: camelize: function (selector) { michael@0: /* from dojo.style.toCamelCase */ michael@0: var arr = selector.split('-'); michael@0: var cc = arr[0]; michael@0: for (var i = 1; i < arr.length; i++) { michael@0: cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1); michael@0: } michael@0: return cc; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.counter */ michael@0: counter: function (n/* = 1 */) { michael@0: if (arguments.length === 0) { michael@0: n = 1; michael@0: } michael@0: return function () { michael@0: return n++; michael@0: }; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.clone */ michael@0: clone: function (obj) { michael@0: var me = arguments.callee; michael@0: if (arguments.length == 1) { michael@0: me.prototype = obj; michael@0: return new me(); michael@0: } michael@0: }, michael@0: michael@0: _flattenArray: function (res, lst) { michael@0: for (var i = 0; i < lst.length; i++) { michael@0: var o = lst[i]; michael@0: if (o instanceof Array) { michael@0: arguments.callee(res, o); michael@0: } else { michael@0: res.push(o); michael@0: } michael@0: } michael@0: return res; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.flattenArray */ michael@0: flattenArray: function (lst) { michael@0: return MochiKit.Base._flattenArray([], lst); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.flattenArguments */ michael@0: flattenArguments: function (lst/* ...*/) { michael@0: var res = []; michael@0: var m = MochiKit.Base; michael@0: var args = m.extend(null, arguments); michael@0: while (args.length) { michael@0: var o = args.shift(); michael@0: if (o && typeof(o) == "object" && typeof(o.length) == "number") { michael@0: for (var i = o.length - 1; i >= 0; i--) { michael@0: args.unshift(o[i]); michael@0: } michael@0: } else { michael@0: res.push(o); michael@0: } michael@0: } michael@0: return res; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.extend */ michael@0: extend: function (self, obj, /* optional */skip) { michael@0: // Extend an array with an array-like object starting michael@0: // from the skip index michael@0: if (!skip) { michael@0: skip = 0; michael@0: } michael@0: if (obj) { michael@0: // allow iterable fall-through, but skip the full isArrayLike michael@0: // check for speed, this is called often. michael@0: var l = obj.length; michael@0: if (typeof(l) != 'number' /* !isArrayLike(obj) */) { michael@0: if (typeof(MochiKit.Iter) != "undefined") { michael@0: obj = MochiKit.Iter.list(obj); michael@0: l = obj.length; michael@0: } else { michael@0: throw new TypeError("Argument not an array-like and MochiKit.Iter not present"); michael@0: } michael@0: } michael@0: if (!self) { michael@0: self = []; michael@0: } michael@0: for (var i = skip; i < l; i++) { michael@0: self.push(obj[i]); michael@0: } michael@0: } michael@0: // This mutates, but it's convenient to return because michael@0: // it's often used like a constructor when turning some michael@0: // ghetto array-like to a real array michael@0: return self; michael@0: }, michael@0: michael@0: michael@0: /** @id MochiKit.Base.updatetree */ michael@0: updatetree: function (self, obj/*, ...*/) { michael@0: if (self === null) { michael@0: self = {}; michael@0: } michael@0: for (var i = 1; i < arguments.length; i++) { michael@0: var o = arguments[i]; michael@0: if (typeof(o) != 'undefined' && o !== null) { michael@0: for (var k in o) { michael@0: var v = o[k]; michael@0: if (typeof(self[k]) == 'object' && typeof(v) == 'object') { michael@0: arguments.callee(self[k], v); michael@0: } else { michael@0: self[k] = v; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return self; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.setdefault */ michael@0: setdefault: function (self, obj/*, ...*/) { michael@0: if (self === null) { michael@0: self = {}; michael@0: } michael@0: for (var i = 1; i < arguments.length; i++) { michael@0: var o = arguments[i]; michael@0: for (var k in o) { michael@0: if (!(k in self)) { michael@0: self[k] = o[k]; michael@0: } michael@0: } michael@0: } michael@0: return self; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.keys */ michael@0: keys: function (obj) { michael@0: var rval = []; michael@0: for (var prop in obj) { michael@0: rval.push(prop); michael@0: } michael@0: return rval; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.values */ michael@0: values: function (obj) { michael@0: var rval = []; michael@0: for (var prop in obj) { michael@0: rval.push(obj[prop]); michael@0: } michael@0: return rval; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.items */ michael@0: items: function (obj) { michael@0: var rval = []; michael@0: var e; michael@0: for (var prop in obj) { michael@0: var v; michael@0: try { michael@0: v = obj[prop]; michael@0: } catch (e) { michael@0: continue; michael@0: } michael@0: rval.push([prop, v]); michael@0: } michael@0: return rval; michael@0: }, michael@0: michael@0: michael@0: _newNamedError: function (module, name, func) { michael@0: func.prototype = new MochiKit.Base.NamedError(module.NAME + "." + name); michael@0: module[name] = func; michael@0: }, michael@0: michael@0: michael@0: /** @id MochiKit.Base.operator */ michael@0: operator: { michael@0: // unary logic operators michael@0: /** @id MochiKit.Base.truth */ michael@0: truth: function (a) { return !!a; }, michael@0: /** @id MochiKit.Base.lognot */ michael@0: lognot: function (a) { return !a; }, michael@0: /** @id MochiKit.Base.identity */ michael@0: identity: function (a) { return a; }, michael@0: michael@0: // bitwise unary operators michael@0: /** @id MochiKit.Base.not */ michael@0: not: function (a) { return ~a; }, michael@0: /** @id MochiKit.Base.neg */ michael@0: neg: function (a) { return -a; }, michael@0: michael@0: // binary operators michael@0: /** @id MochiKit.Base.add */ michael@0: add: function (a, b) { return a + b; }, michael@0: /** @id MochiKit.Base.sub */ michael@0: sub: function (a, b) { return a - b; }, michael@0: /** @id MochiKit.Base.div */ michael@0: div: function (a, b) { return a / b; }, michael@0: /** @id MochiKit.Base.mod */ michael@0: mod: function (a, b) { return a % b; }, michael@0: /** @id MochiKit.Base.mul */ michael@0: mul: function (a, b) { return a * b; }, michael@0: michael@0: // bitwise binary operators michael@0: /** @id MochiKit.Base.and */ michael@0: and: function (a, b) { return a & b; }, michael@0: /** @id MochiKit.Base.or */ michael@0: or: function (a, b) { return a | b; }, michael@0: /** @id MochiKit.Base.xor */ michael@0: xor: function (a, b) { return a ^ b; }, michael@0: /** @id MochiKit.Base.lshift */ michael@0: lshift: function (a, b) { return a << b; }, michael@0: /** @id MochiKit.Base.rshift */ michael@0: rshift: function (a, b) { return a >> b; }, michael@0: /** @id MochiKit.Base.zrshift */ michael@0: zrshift: function (a, b) { return a >>> b; }, michael@0: michael@0: // near-worthless built-in comparators michael@0: /** @id MochiKit.Base.eq */ michael@0: eq: function (a, b) { return a == b; }, michael@0: /** @id MochiKit.Base.ne */ michael@0: ne: function (a, b) { return a != b; }, michael@0: /** @id MochiKit.Base.gt */ michael@0: gt: function (a, b) { return a > b; }, michael@0: /** @id MochiKit.Base.ge */ michael@0: ge: function (a, b) { return a >= b; }, michael@0: /** @id MochiKit.Base.lt */ michael@0: lt: function (a, b) { return a < b; }, michael@0: /** @id MochiKit.Base.le */ michael@0: le: function (a, b) { return a <= b; }, michael@0: michael@0: // strict built-in comparators michael@0: seq: function (a, b) { return a === b; }, michael@0: sne: function (a, b) { return a !== b; }, michael@0: michael@0: // compare comparators michael@0: /** @id MochiKit.Base.ceq */ michael@0: ceq: function (a, b) { return MochiKit.Base.compare(a, b) === 0; }, michael@0: /** @id MochiKit.Base.cne */ michael@0: cne: function (a, b) { return MochiKit.Base.compare(a, b) !== 0; }, michael@0: /** @id MochiKit.Base.cgt */ michael@0: cgt: function (a, b) { return MochiKit.Base.compare(a, b) == 1; }, michael@0: /** @id MochiKit.Base.cge */ michael@0: cge: function (a, b) { return MochiKit.Base.compare(a, b) != -1; }, michael@0: /** @id MochiKit.Base.clt */ michael@0: clt: function (a, b) { return MochiKit.Base.compare(a, b) == -1; }, michael@0: /** @id MochiKit.Base.cle */ michael@0: cle: function (a, b) { return MochiKit.Base.compare(a, b) != 1; }, michael@0: michael@0: // binary logical operators michael@0: /** @id MochiKit.Base.logand */ michael@0: logand: function (a, b) { return a && b; }, michael@0: /** @id MochiKit.Base.logor */ michael@0: logor: function (a, b) { return a || b; }, michael@0: /** @id MochiKit.Base.contains */ michael@0: contains: function (a, b) { return b in a; } michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.forwardCall */ michael@0: forwardCall: function (func) { michael@0: return function () { michael@0: return this[func].apply(this, arguments); michael@0: }; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.itemgetter */ michael@0: itemgetter: function (func) { michael@0: return function (arg) { michael@0: return arg[func]; michael@0: }; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.typeMatcher */ michael@0: typeMatcher: function (/* typ */) { michael@0: var types = {}; michael@0: for (var i = 0; i < arguments.length; i++) { michael@0: var typ = arguments[i]; michael@0: types[typ] = typ; michael@0: } michael@0: return function () { michael@0: for (var i = 0; i < arguments.length; i++) { michael@0: if (!(typeof(arguments[i]) in types)) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: }; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.isNull */ michael@0: isNull: function (/* ... */) { michael@0: for (var i = 0; i < arguments.length; i++) { michael@0: if (arguments[i] !== null) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.isUndefinedOrNull */ michael@0: isUndefinedOrNull: function (/* ... */) { michael@0: for (var i = 0; i < arguments.length; i++) { michael@0: var o = arguments[i]; michael@0: if (!(typeof(o) == 'undefined' || o === null)) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.isEmpty */ michael@0: isEmpty: function (obj) { michael@0: return !MochiKit.Base.isNotEmpty.apply(this, arguments); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.isNotEmpty */ michael@0: isNotEmpty: function (obj) { michael@0: for (var i = 0; i < arguments.length; i++) { michael@0: var o = arguments[i]; michael@0: if (!(o && o.length)) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.isArrayLike */ michael@0: isArrayLike: function () { michael@0: for (var i = 0; i < arguments.length; i++) { michael@0: var o = arguments[i]; michael@0: var typ = typeof(o); michael@0: if ( michael@0: (typ != 'object' && !(typ == 'function' && typeof(o.item) == 'function')) || michael@0: o === null || michael@0: typeof(o.length) != 'number' || michael@0: o.nodeType === 3 michael@0: ) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.isDateLike */ michael@0: isDateLike: function () { michael@0: for (var i = 0; i < arguments.length; i++) { michael@0: var o = arguments[i]; michael@0: if (typeof(o) != "object" || o === null michael@0: || typeof(o.getTime) != 'function') { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: }, michael@0: michael@0: michael@0: /** @id MochiKit.Base.xmap */ michael@0: xmap: function (fn/*, obj... */) { michael@0: if (fn === null) { michael@0: return MochiKit.Base.extend(null, arguments, 1); michael@0: } michael@0: var rval = []; michael@0: for (var i = 1; i < arguments.length; i++) { michael@0: rval.push(fn(arguments[i])); michael@0: } michael@0: return rval; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.map */ michael@0: map: function (fn, lst/*, lst... */) { michael@0: var m = MochiKit.Base; michael@0: var itr = MochiKit.Iter; michael@0: var isArrayLike = m.isArrayLike; michael@0: if (arguments.length <= 2) { michael@0: // allow an iterable to be passed michael@0: if (!isArrayLike(lst)) { michael@0: if (itr) { michael@0: // fast path for map(null, iterable) michael@0: lst = itr.list(lst); michael@0: if (fn === null) { michael@0: return lst; michael@0: } michael@0: } else { michael@0: throw new TypeError("Argument not an array-like and MochiKit.Iter not present"); michael@0: } michael@0: } michael@0: // fast path for map(null, lst) michael@0: if (fn === null) { michael@0: return m.extend(null, lst); michael@0: } michael@0: // disabled fast path for map(fn, lst) michael@0: /* michael@0: if (false && typeof(Array.prototype.map) == 'function') { michael@0: // Mozilla fast-path michael@0: return Array.prototype.map.call(lst, fn); michael@0: } michael@0: */ michael@0: var rval = []; michael@0: for (var i = 0; i < lst.length; i++) { michael@0: rval.push(fn(lst[i])); michael@0: } michael@0: return rval; michael@0: } else { michael@0: // default for map(null, ...) is zip(...) michael@0: if (fn === null) { michael@0: fn = Array; michael@0: } michael@0: var length = null; michael@0: for (i = 1; i < arguments.length; i++) { michael@0: // allow iterables to be passed michael@0: if (!isArrayLike(arguments[i])) { michael@0: if (itr) { michael@0: return itr.list(itr.imap.apply(null, arguments)); michael@0: } else { michael@0: throw new TypeError("Argument not an array-like and MochiKit.Iter not present"); michael@0: } michael@0: } michael@0: // find the minimum length michael@0: var l = arguments[i].length; michael@0: if (length === null || length > l) { michael@0: length = l; michael@0: } michael@0: } michael@0: rval = []; michael@0: for (i = 0; i < length; i++) { michael@0: var args = []; michael@0: for (var j = 1; j < arguments.length; j++) { michael@0: args.push(arguments[j][i]); michael@0: } michael@0: rval.push(fn.apply(this, args)); michael@0: } michael@0: return rval; michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.xfilter */ michael@0: xfilter: function (fn/*, obj... */) { michael@0: var rval = []; michael@0: if (fn === null) { michael@0: fn = MochiKit.Base.operator.truth; michael@0: } michael@0: for (var i = 1; i < arguments.length; i++) { michael@0: var o = arguments[i]; michael@0: if (fn(o)) { michael@0: rval.push(o); michael@0: } michael@0: } michael@0: return rval; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.filter */ michael@0: filter: function (fn, lst, self) { michael@0: var rval = []; michael@0: // allow an iterable to be passed michael@0: var m = MochiKit.Base; michael@0: if (!m.isArrayLike(lst)) { michael@0: if (MochiKit.Iter) { michael@0: lst = MochiKit.Iter.list(lst); michael@0: } else { michael@0: throw new TypeError("Argument not an array-like and MochiKit.Iter not present"); michael@0: } michael@0: } michael@0: if (fn === null) { michael@0: fn = m.operator.truth; michael@0: } michael@0: if (typeof(Array.prototype.filter) == 'function') { michael@0: // Mozilla fast-path michael@0: return Array.prototype.filter.call(lst, fn, self); michael@0: } else if (typeof(self) == 'undefined' || self === null) { michael@0: for (var i = 0; i < lst.length; i++) { michael@0: var o = lst[i]; michael@0: if (fn(o)) { michael@0: rval.push(o); michael@0: } michael@0: } michael@0: } else { michael@0: for (i = 0; i < lst.length; i++) { michael@0: o = lst[i]; michael@0: if (fn.call(self, o)) { michael@0: rval.push(o); michael@0: } michael@0: } michael@0: } michael@0: return rval; michael@0: }, michael@0: michael@0: michael@0: _wrapDumbFunction: function (func) { michael@0: return function () { michael@0: // fast path! michael@0: switch (arguments.length) { michael@0: case 0: return func(); michael@0: case 1: return func(arguments[0]); michael@0: case 2: return func(arguments[0], arguments[1]); michael@0: case 3: return func(arguments[0], arguments[1], arguments[2]); michael@0: } michael@0: var args = []; michael@0: for (var i = 0; i < arguments.length; i++) { michael@0: args.push("arguments[" + i + "]"); michael@0: } michael@0: return eval("(func(" + args.join(",") + "))"); michael@0: }; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.methodcaller */ michael@0: methodcaller: function (func/*, args... */) { michael@0: var args = MochiKit.Base.extend(null, arguments, 1); michael@0: if (typeof(func) == "function") { michael@0: return function (obj) { michael@0: return func.apply(obj, args); michael@0: }; michael@0: } else { michael@0: return function (obj) { michael@0: return obj[func].apply(obj, args); michael@0: }; michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.method */ michael@0: method: function (self, func) { michael@0: var m = MochiKit.Base; michael@0: return m.bind.apply(this, m.extend([func, self], arguments, 2)); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.compose */ michael@0: compose: function (f1, f2/*, f3, ... fN */) { michael@0: var fnlist = []; michael@0: var m = MochiKit.Base; michael@0: if (arguments.length === 0) { michael@0: throw new TypeError("compose() requires at least one argument"); michael@0: } michael@0: for (var i = 0; i < arguments.length; i++) { michael@0: var fn = arguments[i]; michael@0: if (typeof(fn) != "function") { michael@0: throw new TypeError(m.repr(fn) + " is not a function"); michael@0: } michael@0: fnlist.push(fn); michael@0: } michael@0: return function () { michael@0: var args = arguments; michael@0: for (var i = fnlist.length - 1; i >= 0; i--) { michael@0: args = [fnlist[i].apply(this, args)]; michael@0: } michael@0: return args[0]; michael@0: }; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.bind */ michael@0: bind: function (func, self/* args... */) { michael@0: if (typeof(func) == "string") { michael@0: func = self[func]; michael@0: } michael@0: var im_func = func.im_func; michael@0: var im_preargs = func.im_preargs; michael@0: var im_self = func.im_self; michael@0: var m = MochiKit.Base; michael@0: if (typeof(func) == "function" && typeof(func.apply) == "undefined") { michael@0: // this is for cases where JavaScript sucks ass and gives you a michael@0: // really dumb built-in function like alert() that doesn't have michael@0: // an apply michael@0: func = m._wrapDumbFunction(func); michael@0: } michael@0: if (typeof(im_func) != 'function') { michael@0: im_func = func; michael@0: } michael@0: if (typeof(self) != 'undefined') { michael@0: im_self = self; michael@0: } michael@0: if (typeof(im_preargs) == 'undefined') { michael@0: im_preargs = []; michael@0: } else { michael@0: im_preargs = im_preargs.slice(); michael@0: } michael@0: m.extend(im_preargs, arguments, 2); michael@0: var newfunc = function () { michael@0: var args = arguments; michael@0: var me = arguments.callee; michael@0: if (me.im_preargs.length > 0) { michael@0: args = m.concat(me.im_preargs, args); michael@0: } michael@0: var self = me.im_self; michael@0: if (!self) { michael@0: self = this; michael@0: } michael@0: return me.im_func.apply(self, args); michael@0: }; michael@0: newfunc.im_self = im_self; michael@0: newfunc.im_func = im_func; michael@0: newfunc.im_preargs = im_preargs; michael@0: return newfunc; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.bindMethods */ michael@0: bindMethods: function (self) { michael@0: var bind = MochiKit.Base.bind; michael@0: for (var k in self) { michael@0: var func = self[k]; michael@0: if (typeof(func) == 'function') { michael@0: self[k] = bind(func, self); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.registerComparator */ michael@0: registerComparator: function (name, check, comparator, /* optional */ override) { michael@0: MochiKit.Base.comparatorRegistry.register(name, check, comparator, override); michael@0: }, michael@0: michael@0: _primitives: {'boolean': true, 'string': true, 'number': true}, michael@0: michael@0: /** @id MochiKit.Base.compare */ michael@0: compare: function (a, b) { michael@0: if (a == b) { michael@0: return 0; michael@0: } michael@0: var aIsNull = (typeof(a) == 'undefined' || a === null); michael@0: var bIsNull = (typeof(b) == 'undefined' || b === null); michael@0: if (aIsNull && bIsNull) { michael@0: return 0; michael@0: } else if (aIsNull) { michael@0: return -1; michael@0: } else if (bIsNull) { michael@0: return 1; michael@0: } michael@0: var m = MochiKit.Base; michael@0: // bool, number, string have meaningful comparisons michael@0: var prim = m._primitives; michael@0: if (!(typeof(a) in prim && typeof(b) in prim)) { michael@0: try { michael@0: return m.comparatorRegistry.match(a, b); michael@0: } catch (e) { michael@0: if (e != m.NotFound) { michael@0: throw e; michael@0: } michael@0: } michael@0: } michael@0: if (a < b) { michael@0: return -1; michael@0: } else if (a > b) { michael@0: return 1; michael@0: } michael@0: // These types can't be compared michael@0: var repr = m.repr; michael@0: throw new TypeError(repr(a) + " and " + repr(b) + " can not be compared"); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.compareDateLike */ michael@0: compareDateLike: function (a, b) { michael@0: return MochiKit.Base.compare(a.getTime(), b.getTime()); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.compareArrayLike */ michael@0: compareArrayLike: function (a, b) { michael@0: var compare = MochiKit.Base.compare; michael@0: var count = a.length; michael@0: var rval = 0; michael@0: if (count > b.length) { michael@0: rval = 1; michael@0: count = b.length; michael@0: } else if (count < b.length) { michael@0: rval = -1; michael@0: } michael@0: for (var i = 0; i < count; i++) { michael@0: var cmp = compare(a[i], b[i]); michael@0: if (cmp) { michael@0: return cmp; michael@0: } michael@0: } michael@0: return rval; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.registerRepr */ michael@0: registerRepr: function (name, check, wrap, /* optional */override) { michael@0: MochiKit.Base.reprRegistry.register(name, check, wrap, override); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.repr */ michael@0: repr: function (o) { michael@0: if (typeof(o) == "undefined") { michael@0: return "undefined"; michael@0: } else if (o === null) { michael@0: return "null"; michael@0: } michael@0: try { michael@0: if (typeof(o.__repr__) == 'function') { michael@0: return o.__repr__(); michael@0: } else if (typeof(o.repr) == 'function' && o.repr != arguments.callee) { michael@0: return o.repr(); michael@0: } michael@0: return MochiKit.Base.reprRegistry.match(o); michael@0: } catch (e) { michael@0: try { michael@0: if (typeof(o.NAME) == 'string' && ( michael@0: o.toString == Function.prototype.toString || michael@0: o.toString == Object.prototype.toString michael@0: )) { michael@0: return o.NAME; michael@0: } michael@0: } catch (e) { michael@0: } michael@0: } michael@0: try { michael@0: var ostring = (o + ""); michael@0: } catch (e) { michael@0: return "[" + typeof(o) + "]"; michael@0: } michael@0: if (typeof(o) == "function") { michael@0: o = ostring.replace(/^\s+/, ""); michael@0: var idx = o.indexOf("{"); michael@0: if (idx != -1) { michael@0: o = o.substr(0, idx) + "{...}"; michael@0: } michael@0: } michael@0: return ostring; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.reprArrayLike */ michael@0: reprArrayLike: function (o) { michael@0: var m = MochiKit.Base; michael@0: return "[" + m.map(m.repr, o).join(", ") + "]"; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.reprString */ michael@0: reprString: function (o) { michael@0: return ('"' + o.replace(/(["\\])/g, '\\$1') + '"' michael@0: ).replace(/[\f]/g, "\\f" michael@0: ).replace(/[\b]/g, "\\b" michael@0: ).replace(/[\n]/g, "\\n" michael@0: ).replace(/[\t]/g, "\\t" michael@0: ).replace(/[\r]/g, "\\r"); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.reprNumber */ michael@0: reprNumber: function (o) { michael@0: return o + ""; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.registerJSON */ michael@0: registerJSON: function (name, check, wrap, /* optional */override) { michael@0: MochiKit.Base.jsonRegistry.register(name, check, wrap, override); michael@0: }, michael@0: michael@0: michael@0: /** @id MochiKit.Base.evalJSON */ michael@0: evalJSON: function () { michael@0: return eval("(" + arguments[0] + ")"); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.serializeJSON */ michael@0: serializeJSON: function (o) { michael@0: var objtype = typeof(o); michael@0: if (objtype == "number" || objtype == "boolean") { michael@0: return o + ""; michael@0: } else if (o === null) { michael@0: return "null"; michael@0: } michael@0: var m = MochiKit.Base; michael@0: var reprString = m.reprString; michael@0: if (objtype == "string") { michael@0: return reprString(o); michael@0: } michael@0: // recurse michael@0: var me = arguments.callee; michael@0: // short-circuit for objects that support "json" serialization michael@0: // if they return "self" then just pass-through... michael@0: var newObj; michael@0: if (typeof(o.__json__) == "function") { michael@0: newObj = o.__json__(); michael@0: if (o !== newObj) { michael@0: return me(newObj); michael@0: } michael@0: } michael@0: if (typeof(o.json) == "function") { michael@0: newObj = o.json(); michael@0: if (o !== newObj) { michael@0: return me(newObj); michael@0: } michael@0: } michael@0: // array michael@0: if (objtype != "function" && typeof(o.length) == "number") { michael@0: var res = []; michael@0: for (var i = 0; i < o.length; i++) { michael@0: var val = me(o[i]); michael@0: if (typeof(val) != "string") { michael@0: val = "undefined"; michael@0: } michael@0: res.push(val); michael@0: } michael@0: return "[" + res.join(", ") + "]"; michael@0: } michael@0: // look in the registry michael@0: try { michael@0: newObj = m.jsonRegistry.match(o); michael@0: if (o !== newObj) { michael@0: return me(newObj); michael@0: } michael@0: } catch (e) { michael@0: if (e != m.NotFound) { michael@0: // something really bad happened michael@0: throw e; michael@0: } michael@0: } michael@0: // undefined is outside of the spec michael@0: if (objtype == "undefined") { michael@0: throw new TypeError("undefined can not be serialized as JSON"); michael@0: } michael@0: // it's a function with no adapter, bad michael@0: if (objtype == "function") { michael@0: return null; michael@0: } michael@0: // generic object code path michael@0: res = []; michael@0: for (var k in o) { michael@0: var useKey; michael@0: if (typeof(k) == "number") { michael@0: useKey = '"' + k + '"'; michael@0: } else if (typeof(k) == "string") { michael@0: useKey = reprString(k); michael@0: } else { michael@0: // skip non-string or number keys michael@0: continue; michael@0: } michael@0: val = me(o[k]); michael@0: if (typeof(val) != "string") { michael@0: // skip non-serializable values michael@0: continue; michael@0: } michael@0: res.push(useKey + ":" + val); michael@0: } michael@0: return "{" + res.join(", ") + "}"; michael@0: }, michael@0: michael@0: michael@0: /** @id MochiKit.Base.objEqual */ michael@0: objEqual: function (a, b) { michael@0: return (MochiKit.Base.compare(a, b) === 0); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.arrayEqual */ michael@0: arrayEqual: function (self, arr) { michael@0: if (self.length != arr.length) { michael@0: return false; michael@0: } michael@0: return (MochiKit.Base.compare(self, arr) === 0); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.concat */ michael@0: concat: function (/* lst... */) { michael@0: var rval = []; michael@0: var extend = MochiKit.Base.extend; michael@0: for (var i = 0; i < arguments.length; i++) { michael@0: extend(rval, arguments[i]); michael@0: } michael@0: return rval; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.keyComparator */ michael@0: keyComparator: function (key/* ... */) { michael@0: // fast-path for single key comparisons michael@0: var m = MochiKit.Base; michael@0: var compare = m.compare; michael@0: if (arguments.length == 1) { michael@0: return function (a, b) { michael@0: return compare(a[key], b[key]); michael@0: }; michael@0: } michael@0: var compareKeys = m.extend(null, arguments); michael@0: return function (a, b) { michael@0: var rval = 0; michael@0: // keep comparing until something is inequal or we run out of michael@0: // keys to compare michael@0: for (var i = 0; (rval === 0) && (i < compareKeys.length); i++) { michael@0: var key = compareKeys[i]; michael@0: rval = compare(a[key], b[key]); michael@0: } michael@0: return rval; michael@0: }; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.reverseKeyComparator */ michael@0: reverseKeyComparator: function (key) { michael@0: var comparator = MochiKit.Base.keyComparator.apply(this, arguments); michael@0: return function (a, b) { michael@0: return comparator(b, a); michael@0: }; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.partial */ michael@0: partial: function (func) { michael@0: var m = MochiKit.Base; michael@0: return m.bind.apply(this, m.extend([func, undefined], arguments, 1)); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.listMinMax */ michael@0: listMinMax: function (which, lst) { michael@0: if (lst.length === 0) { michael@0: return null; michael@0: } michael@0: var cur = lst[0]; michael@0: var compare = MochiKit.Base.compare; michael@0: for (var i = 1; i < lst.length; i++) { michael@0: var o = lst[i]; michael@0: if (compare(o, cur) == which) { michael@0: cur = o; michael@0: } michael@0: } michael@0: return cur; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.objMax */ michael@0: objMax: function (/* obj... */) { michael@0: return MochiKit.Base.listMinMax(1, arguments); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.objMin */ michael@0: objMin: function (/* obj... */) { michael@0: return MochiKit.Base.listMinMax(-1, arguments); michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.findIdentical */ michael@0: findIdentical: function (lst, value, start/* = 0 */, /* optional */end) { michael@0: if (typeof(end) == "undefined" || end === null) { michael@0: end = lst.length; michael@0: } michael@0: if (typeof(start) == "undefined" || start === null) { michael@0: start = 0; michael@0: } michael@0: for (var i = start; i < end; i++) { michael@0: if (lst[i] === value) { michael@0: return i; michael@0: } michael@0: } michael@0: return -1; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.mean */ michael@0: mean: function(/* lst... */) { michael@0: /* http://www.nist.gov/dads/HTML/mean.html */ michael@0: var sum = 0; michael@0: michael@0: var m = MochiKit.Base; michael@0: var args = m.extend(null, arguments); michael@0: var count = args.length; michael@0: michael@0: while (args.length) { michael@0: var o = args.shift(); michael@0: if (o && typeof(o) == "object" && typeof(o.length) == "number") { michael@0: count += o.length - 1; michael@0: for (var i = o.length - 1; i >= 0; i--) { michael@0: sum += o[i]; michael@0: } michael@0: } else { michael@0: sum += o; michael@0: } michael@0: } michael@0: michael@0: if (count <= 0) { michael@0: throw new TypeError('mean() requires at least one argument'); michael@0: } michael@0: michael@0: return sum/count; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.median */ michael@0: median: function(/* lst... */) { michael@0: /* http://www.nist.gov/dads/HTML/median.html */ michael@0: var data = MochiKit.Base.flattenArguments(arguments); michael@0: if (data.length === 0) { michael@0: throw new TypeError('median() requires at least one argument'); michael@0: } michael@0: data.sort(compare); michael@0: if (data.length % 2 == 0) { michael@0: var upper = data.length / 2; michael@0: return (data[upper] + data[upper - 1]) / 2; michael@0: } else { michael@0: return data[(data.length - 1) / 2]; michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.findValue */ michael@0: findValue: function (lst, value, start/* = 0 */, /* optional */end) { michael@0: if (typeof(end) == "undefined" || end === null) { michael@0: end = lst.length; michael@0: } michael@0: if (typeof(start) == "undefined" || start === null) { michael@0: start = 0; michael@0: } michael@0: var cmp = MochiKit.Base.compare; michael@0: for (var i = start; i < end; i++) { michael@0: if (cmp(lst[i], value) === 0) { michael@0: return i; michael@0: } michael@0: } michael@0: return -1; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.nodeWalk */ michael@0: nodeWalk: function (node, visitor) { michael@0: var nodes = [node]; michael@0: var extend = MochiKit.Base.extend; michael@0: while (nodes.length) { michael@0: var res = visitor(nodes.shift()); michael@0: if (res) { michael@0: extend(nodes, res); michael@0: } michael@0: } michael@0: }, michael@0: michael@0: michael@0: /** @id MochiKit.Base.nameFunctions */ michael@0: nameFunctions: function (namespace) { michael@0: var base = namespace.NAME; michael@0: if (typeof(base) == 'undefined') { michael@0: base = ''; michael@0: } else { michael@0: base = base + '.'; michael@0: } michael@0: for (var name in namespace) { michael@0: var o = namespace[name]; michael@0: if (typeof(o) == 'function' && typeof(o.NAME) == 'undefined') { michael@0: try { michael@0: o.NAME = base + name; michael@0: } catch (e) { michael@0: // pass michael@0: } michael@0: } michael@0: } michael@0: }, michael@0: michael@0: michael@0: /** @id MochiKit.Base.queryString */ michael@0: queryString: function (names, values) { michael@0: // check to see if names is a string or a DOM element, and if michael@0: // MochiKit.DOM is available. If so, drop it like it's a form michael@0: // Ugliest conditional in MochiKit? Probably! michael@0: if (typeof(MochiKit.DOM) != "undefined" && arguments.length == 1 michael@0: && (typeof(names) == "string" || ( michael@0: typeof(names.nodeType) != "undefined" && names.nodeType > 0 michael@0: )) michael@0: ) { michael@0: var kv = MochiKit.DOM.formContents(names); michael@0: names = kv[0]; michael@0: values = kv[1]; michael@0: } else if (arguments.length == 1) { michael@0: var o = names; michael@0: names = []; michael@0: values = []; michael@0: for (var k in o) { michael@0: var v = o[k]; michael@0: if (typeof(v) == "function") { michael@0: continue; michael@0: } else if (typeof(v) != "string" && michael@0: typeof(v.length) == "number") { michael@0: for (var i = 0; i < v.length; i++) { michael@0: names.push(k); michael@0: values.push(v[i]); michael@0: } michael@0: } else { michael@0: names.push(k); michael@0: values.push(v); michael@0: } michael@0: } michael@0: } michael@0: var rval = []; michael@0: var len = Math.min(names.length, values.length); michael@0: var urlEncode = MochiKit.Base.urlEncode; michael@0: for (var i = 0; i < len; i++) { michael@0: v = values[i]; michael@0: if (typeof(v) != 'undefined' && v !== null) { michael@0: rval.push(urlEncode(names[i]) + "=" + urlEncode(v)); michael@0: } michael@0: } michael@0: return rval.join("&"); michael@0: }, michael@0: michael@0: michael@0: /** @id MochiKit.Base.parseQueryString */ michael@0: parseQueryString: function (encodedString, useArrays) { michael@0: // strip a leading '?' from the encoded string michael@0: var qstr = (encodedString[0] == "?") ? encodedString.substring(1) : michael@0: encodedString; michael@0: var pairs = qstr.replace(/\+/g, "%20").split(/(\&\;|\&\#38\;|\&|\&)/); michael@0: var o = {}; michael@0: var decode; michael@0: if (typeof(decodeURIComponent) != "undefined") { michael@0: decode = decodeURIComponent; michael@0: } else { michael@0: decode = unescape; michael@0: } michael@0: if (useArrays) { michael@0: for (var i = 0; i < pairs.length; i++) { michael@0: var pair = pairs[i].split("="); michael@0: if (pair.length !== 2) { michael@0: continue; michael@0: } michael@0: var name = decode(pair[0]); michael@0: var arr = o[name]; michael@0: if (!(arr instanceof Array)) { michael@0: arr = []; michael@0: o[name] = arr; michael@0: } michael@0: arr.push(decode(pair[1])); michael@0: } michael@0: } else { michael@0: for (i = 0; i < pairs.length; i++) { michael@0: pair = pairs[i].split("="); michael@0: if (pair.length !== 2) { michael@0: continue; michael@0: } michael@0: o[decode(pair[0])] = decode(pair[1]); michael@0: } michael@0: } michael@0: return o; michael@0: } michael@0: }); michael@0: michael@0: /** @id MochiKit.Base.AdapterRegistry */ michael@0: MochiKit.Base.AdapterRegistry = function () { michael@0: this.pairs = []; michael@0: }; michael@0: michael@0: MochiKit.Base.AdapterRegistry.prototype = { michael@0: /** @id MochiKit.Base.AdapterRegistry.prototype.register */ michael@0: register: function (name, check, wrap, /* optional */ override) { michael@0: if (override) { michael@0: this.pairs.unshift([name, check, wrap]); michael@0: } else { michael@0: this.pairs.push([name, check, wrap]); michael@0: } michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.AdapterRegistry.prototype.match */ michael@0: match: function (/* ... */) { michael@0: for (var i = 0; i < this.pairs.length; i++) { michael@0: var pair = this.pairs[i]; michael@0: if (pair[1].apply(this, arguments)) { michael@0: return pair[2].apply(this, arguments); michael@0: } michael@0: } michael@0: throw MochiKit.Base.NotFound; michael@0: }, michael@0: michael@0: /** @id MochiKit.Base.AdapterRegistry.prototype.unregister */ michael@0: unregister: function (name) { michael@0: for (var i = 0; i < this.pairs.length; i++) { michael@0: var pair = this.pairs[i]; michael@0: if (pair[0] == name) { michael@0: this.pairs.splice(i, 1); michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: }; michael@0: michael@0: michael@0: MochiKit.Base.EXPORT = [ michael@0: "flattenArray", michael@0: "noop", michael@0: "camelize", michael@0: "counter", michael@0: "clone", michael@0: "extend", michael@0: "update", michael@0: "updatetree", michael@0: "setdefault", michael@0: "keys", michael@0: "values", michael@0: "items", michael@0: "NamedError", michael@0: "operator", michael@0: "forwardCall", michael@0: "itemgetter", michael@0: "typeMatcher", michael@0: "isCallable", michael@0: "isUndefined", michael@0: "isUndefinedOrNull", michael@0: "isNull", michael@0: "isEmpty", michael@0: "isNotEmpty", michael@0: "isArrayLike", michael@0: "isDateLike", michael@0: "xmap", michael@0: "map", michael@0: "xfilter", michael@0: "filter", michael@0: "methodcaller", michael@0: "compose", michael@0: "bind", michael@0: "bindMethods", michael@0: "NotFound", michael@0: "AdapterRegistry", michael@0: "registerComparator", michael@0: "compare", michael@0: "registerRepr", michael@0: "repr", michael@0: "objEqual", michael@0: "arrayEqual", michael@0: "concat", michael@0: "keyComparator", michael@0: "reverseKeyComparator", michael@0: "partial", michael@0: "merge", michael@0: "listMinMax", michael@0: "listMax", michael@0: "listMin", michael@0: "objMax", michael@0: "objMin", michael@0: "nodeWalk", michael@0: "zip", michael@0: "urlEncode", michael@0: "queryString", michael@0: "serializeJSON", michael@0: "registerJSON", michael@0: "evalJSON", michael@0: "parseQueryString", michael@0: "findValue", michael@0: "findIdentical", michael@0: "flattenArguments", michael@0: "method", michael@0: "average", michael@0: "mean", michael@0: "median" michael@0: ]; michael@0: michael@0: MochiKit.Base.EXPORT_OK = [ michael@0: "nameFunctions", michael@0: "comparatorRegistry", michael@0: "reprRegistry", michael@0: "jsonRegistry", michael@0: "compareDateLike", michael@0: "compareArrayLike", michael@0: "reprArrayLike", michael@0: "reprString", michael@0: "reprNumber" michael@0: ]; michael@0: michael@0: MochiKit.Base._exportSymbols = function (globals, module) { michael@0: if (!MochiKit.__export__) { michael@0: return; michael@0: } michael@0: var all = module.EXPORT_TAGS[":all"]; michael@0: for (var i = 0; i < all.length; i++) { michael@0: globals[all[i]] = module[all[i]]; michael@0: } michael@0: }; michael@0: michael@0: MochiKit.Base.__new__ = function () { michael@0: // A singleton raised when no suitable adapter is found michael@0: var m = this; michael@0: michael@0: // convenience michael@0: /** @id MochiKit.Base.noop */ michael@0: m.noop = m.operator.identity; michael@0: michael@0: // Backwards compat michael@0: m.forward = m.forwardCall; michael@0: m.find = m.findValue; michael@0: michael@0: if (typeof(encodeURIComponent) != "undefined") { michael@0: /** @id MochiKit.Base.urlEncode */ michael@0: m.urlEncode = function (unencoded) { michael@0: return encodeURIComponent(unencoded).replace(/\'/g, '%27'); michael@0: }; michael@0: } else { michael@0: m.urlEncode = function (unencoded) { michael@0: return escape(unencoded michael@0: ).replace(/\+/g, '%2B' michael@0: ).replace(/\"/g,'%22' michael@0: ).rval.replace(/\'/g, '%27'); michael@0: }; michael@0: } michael@0: michael@0: /** @id MochiKit.Base.NamedError */ michael@0: m.NamedError = function (name) { michael@0: this.message = name; michael@0: this.name = name; michael@0: }; michael@0: m.NamedError.prototype = new Error(); michael@0: m.update(m.NamedError.prototype, { michael@0: repr: function () { michael@0: if (this.message && this.message != this.name) { michael@0: return this.name + "(" + m.repr(this.message) + ")"; michael@0: } else { michael@0: return this.name + "()"; michael@0: } michael@0: }, michael@0: toString: m.forwardCall("repr") michael@0: }); michael@0: michael@0: /** @id MochiKit.Base.NotFound */ michael@0: m.NotFound = new m.NamedError("MochiKit.Base.NotFound"); michael@0: michael@0: michael@0: /** @id MochiKit.Base.listMax */ michael@0: m.listMax = m.partial(m.listMinMax, 1); michael@0: /** @id MochiKit.Base.listMin */ michael@0: m.listMin = m.partial(m.listMinMax, -1); michael@0: michael@0: /** @id MochiKit.Base.isCallable */ michael@0: m.isCallable = m.typeMatcher('function'); michael@0: /** @id MochiKit.Base.isUndefined */ michael@0: m.isUndefined = m.typeMatcher('undefined'); michael@0: michael@0: /** @id MochiKit.Base.merge */ michael@0: m.merge = m.partial(m.update, null); michael@0: /** @id MochiKit.Base.zip */ michael@0: m.zip = m.partial(m.map, null); michael@0: michael@0: /** @id MochiKit.Base.average */ michael@0: m.average = m.mean; michael@0: michael@0: /** @id MochiKit.Base.comparatorRegistry */ michael@0: m.comparatorRegistry = new m.AdapterRegistry(); michael@0: m.registerComparator("dateLike", m.isDateLike, m.compareDateLike); michael@0: m.registerComparator("arrayLike", m.isArrayLike, m.compareArrayLike); michael@0: michael@0: /** @id MochiKit.Base.reprRegistry */ michael@0: m.reprRegistry = new m.AdapterRegistry(); michael@0: m.registerRepr("arrayLike", m.isArrayLike, m.reprArrayLike); michael@0: m.registerRepr("string", m.typeMatcher("string"), m.reprString); michael@0: m.registerRepr("numbers", m.typeMatcher("number", "boolean"), m.reprNumber); michael@0: michael@0: /** @id MochiKit.Base.jsonRegistry */ michael@0: m.jsonRegistry = new m.AdapterRegistry(); michael@0: michael@0: var all = m.concat(m.EXPORT, m.EXPORT_OK); michael@0: m.EXPORT_TAGS = { michael@0: ":common": m.concat(m.EXPORT_OK), michael@0: ":all": all michael@0: }; michael@0: michael@0: m.nameFunctions(this); michael@0: michael@0: }; michael@0: michael@0: MochiKit.Base.__new__(); michael@0: michael@0: // michael@0: // XXX: Internet Explorer blows michael@0: // michael@0: if (MochiKit.__export__) { michael@0: compare = MochiKit.Base.compare; michael@0: compose = MochiKit.Base.compose; michael@0: serializeJSON = MochiKit.Base.serializeJSON; michael@0: } michael@0: michael@0: MochiKit.Base._exportSymbols(this, MochiKit.Base);