| /* |
| Copyright (c) 2004-2005, The Dojo Foundation |
| All Rights Reserved. |
| |
| Licensed under the Academic Free License version 2.1 or above OR the |
| modified BSD license. For more information on Dojo licensing, see: |
| |
| http://dojotoolkit.org/community/licensing.shtml |
| */ |
| |
| dojo.provide("dojo.lang"); |
| dojo.provide("dojo.AdapterRegistry"); |
| dojo.provide("dojo.lang.Lang"); |
| |
| dojo.lang.mixin = function(obj, props){ |
| var tobj = {}; |
| for(var x in props){ |
| if(typeof tobj[x] == "undefined" || tobj[x] != props[x]) { |
| obj[x] = props[x]; |
| } |
| } |
| // IE doesn't recognize custom toStrings in for..in |
| if(dojo.render.html.ie && dojo.lang.isFunction(props["toString"]) && props["toString"] != obj["toString"]) { |
| obj.toString = props.toString; |
| } |
| return obj; |
| } |
| |
| dojo.lang.extend = function(ctor, props){ |
| this.mixin(ctor.prototype, props); |
| } |
| |
| dojo.lang.extendPrototype = function(obj, props){ |
| this.extend(obj.constructor, props); |
| } |
| |
| dojo.lang.anonCtr = 0; |
| dojo.lang.anon = {}; |
| dojo.lang.nameAnonFunc = function(anonFuncPtr, namespaceObj){ |
| var nso = (namespaceObj || dojo.lang.anon); |
| if((dj_global["djConfig"])&&(djConfig["slowAnonFuncLookups"] == true)){ |
| for(var x in nso){ |
| if(nso[x] === anonFuncPtr){ |
| return x; |
| } |
| } |
| } |
| var ret = "__"+dojo.lang.anonCtr++; |
| while(typeof nso[ret] != "undefined"){ |
| ret = "__"+dojo.lang.anonCtr++; |
| } |
| nso[ret] = anonFuncPtr; |
| return ret; |
| } |
| |
| /** |
| * Runs a function in a given scope (thisObject), can |
| * also be used to preserve scope. |
| * |
| * hitch(foo, "bar"); // runs foo.bar() in the scope of foo |
| * hitch(foo, myFunction); // runs myFunction in the scope of foo |
| */ |
| dojo.lang.hitch = function(thisObject, method) { |
| if(dojo.lang.isString(method)) { |
| var fcn = thisObject[method]; |
| } else { |
| var fcn = method; |
| } |
| |
| return function() { |
| return fcn.apply(thisObject, arguments); |
| } |
| } |
| |
| dojo.lang.forward = function(funcName){ |
| // Returns a function that forwards a method call to this.func(...) |
| return function(){ |
| return this[funcName].apply(this, arguments); |
| }; |
| } |
| |
| dojo.lang.curry = function(ns, func /* args ... */){ |
| var outerArgs = []; |
| ns = ns||dj_global; |
| if(dojo.lang.isString(func)){ |
| func = ns[func]; |
| } |
| for(var x=2; x<arguments.length; x++){ |
| outerArgs.push(arguments[x]); |
| } |
| var ecount = func.length - outerArgs.length; |
| // borrowed from svend tofte |
| function gather(nextArgs, innerArgs, expected){ |
| var texpected = expected; |
| var totalArgs = innerArgs.slice(0); // copy |
| for(var x=0; x<nextArgs.length; x++){ |
| totalArgs.push(nextArgs[x]); |
| } |
| // check the list of provided nextArgs to see if it, plus the |
| // number of innerArgs already supplied, meets the total |
| // expected. |
| expected = expected-nextArgs.length; |
| if(expected<=0){ |
| var res = func.apply(ns, totalArgs); |
| expected = texpected; |
| return res; |
| }else{ |
| return function(){ |
| return gather(arguments,// check to see if we've been run |
| // with enough args |
| totalArgs, // a copy |
| expected); // how many more do we need to run?; |
| } |
| } |
| } |
| return gather([], outerArgs, ecount); |
| } |
| |
| dojo.lang.curryArguments = function(ns, func, args, offset){ |
| var targs = []; |
| var x = offset||0; |
| for(x=offset; x<args.length; x++){ |
| targs.push(args[x]); // ensure that it's an arr |
| } |
| return dojo.lang.curry.apply(dojo.lang, [ns, func].concat(targs)); |
| } |
| |
| /** |
| * Sets a timeout in milliseconds to execute a function in a given context |
| * with optional arguments. |
| * |
| * setTimeout (Object context, function func, number delay[, arg1[, ...]]); |
| * setTimeout (function func, number delay[, arg1[, ...]]); |
| */ |
| dojo.lang.setTimeout = function(func, delay){ |
| var context = window, argsStart = 2; |
| if(!dojo.lang.isFunction(func)){ |
| context = func; |
| func = delay; |
| delay = arguments[2]; |
| argsStart++; |
| } |
| |
| if(dojo.lang.isString(func)){ |
| func = context[func]; |
| } |
| |
| var args = []; |
| for (var i = argsStart; i < arguments.length; i++) { |
| args.push(arguments[i]); |
| } |
| return setTimeout(function () { func.apply(context, args); }, delay); |
| } |
| |
| /** |
| * Partial implmentation of is* functions from |
| * http://www.crockford.com/javascript/recommend.html |
| * NOTE: some of these may not be the best thing to use in all situations |
| * as they aren't part of core JS and therefore can't work in every case. |
| * See WARNING messages inline for tips. |
| * |
| * The following is* functions are fairly "safe" |
| */ |
| |
| dojo.lang.isObject = function(wh) { |
| return typeof wh == "object" || dojo.lang.isArray(wh) || dojo.lang.isFunction(wh); |
| } |
| |
| dojo.lang.isArray = function(wh) { |
| return (wh instanceof Array || typeof wh == "array"); |
| } |
| |
| dojo.lang.isArrayLike = function(wh) { |
| if(dojo.lang.isString(wh)){ return false; } |
| if(dojo.lang.isArray(wh)){ return true; } |
| if(typeof wh != "undefined" && wh |
| && dojo.lang.isNumber(wh.length) && isFinite(wh.length)){ return true; } |
| return false; |
| } |
| |
| dojo.lang.isFunction = function(wh) { |
| return (wh instanceof Function || typeof wh == "function"); |
| } |
| |
| dojo.lang.isString = function(wh) { |
| return (wh instanceof String || typeof wh == "string"); |
| } |
| |
| dojo.lang.isAlien = function(wh) { |
| return !dojo.lang.isFunction() && /\{\s*\[native code\]\s*\}/.test(String(wh)); |
| } |
| |
| dojo.lang.isBoolean = function(wh) { |
| return (wh instanceof Boolean || typeof wh == "boolean"); |
| } |
| |
| /** |
| * The following is***() functions are somewhat "unsafe". Fortunately, |
| * there are workarounds the the language provides and are mentioned |
| * in the WARNING messages. |
| * |
| * WARNING: In most cases, isNaN(wh) is sufficient to determine whether or not |
| * something is a number or can be used as such. For example, a number or string |
| * can be used interchangably when accessing array items (arr["1"] is the same as |
| * arr[1]) and isNaN will return false for both values ("1" and 1). Should you |
| * use isNumber("1"), that will return false, which is generally not too useful. |
| * Also, isNumber(NaN) returns true, again, this isn't generally useful, but there |
| * are corner cases (like when you want to make sure that two things are really |
| * the same type of thing). That is really where isNumber "shines". |
| * |
| * RECOMMENDATION: Use isNaN(wh) when possible |
| */ |
| dojo.lang.isNumber = function(wh) { |
| return (wh instanceof Number || typeof wh == "number"); |
| } |
| |
| /** |
| * WARNING: In some cases, isUndefined will not behave as you |
| * might expect. If you do isUndefined(foo) and there is no earlier |
| * reference to foo, an error will be thrown before isUndefined is |
| * called. It behaves correctly if you scope yor object first, i.e. |
| * isUndefined(foo.bar) where foo is an object and bar isn't a |
| * property of the object. |
| * |
| * RECOMMENDATION: Use `typeof foo == "undefined"` when possible |
| * |
| * FIXME: Should isUndefined go away since it is error prone? |
| */ |
| dojo.lang.isUndefined = function(wh) { |
| return ((wh == undefined)&&(typeof wh == "undefined")); |
| } |
| |
| // end Crockford functions |
| |
| dojo.lang.whatAmI = function(wh) { |
| try { |
| if(dojo.lang.isArray(wh)) { return "array"; } |
| if(dojo.lang.isFunction(wh)) { return "function"; } |
| if(dojo.lang.isString(wh)) { return "string"; } |
| if(dojo.lang.isNumber(wh)) { return "number"; } |
| if(dojo.lang.isBoolean(wh)) { return "boolean"; } |
| if(dojo.lang.isAlien(wh)) { return "alien"; } |
| if(dojo.lang.isUndefined(wh)) { return "undefined"; } |
| // FIXME: should this go first? |
| for(var name in dojo.lang.whatAmI.custom) { |
| if(dojo.lang.whatAmI.custom[name](wh)) { |
| return name; |
| } |
| } |
| if(dojo.lang.isObject(wh)) { return "object"; } |
| } catch(E) {} |
| return "unknown"; |
| } |
| /* |
| * dojo.lang.whatAmI.custom[typeName] = someFunction |
| * will return typeName is someFunction(wh) returns true |
| */ |
| dojo.lang.whatAmI.custom = {}; |
| |
| /** |
| * See if val is in arr. Call signatures: |
| * find(array, value, identity) |
| * find(value, array, identity) |
| **/ |
| dojo.lang.find = function(arr, val, identity){ |
| // support both (arr, val) and (val, arr) |
| if(!dojo.lang.isArrayLike(arr) && dojo.lang.isArrayLike(val)) { |
| var a = arr; |
| arr = val; |
| val = a; |
| } |
| var isString = dojo.lang.isString(arr); |
| if(isString) { arr = arr.split(""); } |
| if(identity){ |
| for(var i=0;i<arr.length;++i){ |
| if(arr[i] === val){ return i; } |
| } |
| }else{ |
| for(var i=0;i<arr.length;++i){ |
| if(arr[i] == val){ return i; } |
| } |
| } |
| return -1; |
| } |
| |
| dojo.lang.indexOf = dojo.lang.find; |
| |
| dojo.lang.findLast = function(arr, val, identity) { |
| // support both (arr, val) and (val, arr) |
| if(!dojo.lang.isArrayLike(arr) && dojo.lang.isArrayLike(val)) { |
| var a = arr; |
| arr = val; |
| val = a; |
| } |
| var isString = dojo.lang.isString(arr); |
| if(isString) { arr = arr.split(""); } |
| if(identity){ |
| for(var i = arr.length-1; i >= 0; i--) { |
| if(arr[i] === val){ return i; } |
| } |
| }else{ |
| for(var i = arr.length-1; i >= 0; i--) { |
| if(arr[i] == val){ return i; } |
| } |
| } |
| return -1; |
| } |
| |
| dojo.lang.lastIndexOf = dojo.lang.findLast; |
| |
| dojo.lang.inArray = function(arr, val){ |
| return dojo.lang.find(arr, val) > -1; |
| } |
| |
| dojo.lang.getNameInObj = function(ns, item){ |
| if(!ns){ ns = dj_global; } |
| |
| for(var x in ns){ |
| if(ns[x] === item){ |
| return new String(x); |
| } |
| } |
| return null; |
| } |
| |
| // FIXME: Is this worthless since you can do: if(name in obj) |
| // is this the right place for this? |
| dojo.lang.has = function(obj, name){ |
| return (typeof obj[name] !== 'undefined'); |
| } |
| |
| dojo.lang.isEmpty = function(obj) { |
| if(dojo.lang.isObject(obj)) { |
| var tmp = {}; |
| var count = 0; |
| for(var x in obj){ |
| if(obj[x] && (!tmp[x])){ |
| count++; |
| break; |
| } |
| } |
| return (count == 0); |
| } else if(dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)) { |
| return obj.length == 0; |
| } |
| } |
| |
| dojo.lang.forEach = function(arr, unary_func, fix_length){ |
| var isString = dojo.lang.isString(arr); |
| if(isString) { arr = arr.split(""); } |
| var il = arr.length; |
| for(var i=0; i< ((fix_length) ? il : arr.length); i++){ |
| if(unary_func(arr[i], i, arr) == "break"){ |
| break; |
| } |
| } |
| } |
| |
| dojo.lang.map = function(arr, obj, unary_func){ |
| var isString = dojo.lang.isString(arr); |
| if(isString){ |
| arr = arr.split(""); |
| } |
| if(dojo.lang.isFunction(obj)&&(!unary_func)){ |
| unary_func = obj; |
| obj = dj_global; |
| }else if(dojo.lang.isFunction(obj) && unary_func){ |
| // ff 1.5 compat |
| var tmpObj = obj; |
| obj = unary_func; |
| unary_func = tmpObj; |
| } |
| |
| if(Array.map){ |
| var outArr = Array.map(arr, unary_func, obj); |
| }else{ |
| var outArr = []; |
| for(var i=0;i<arr.length;++i){ |
| outArr.push(unary_func.call(obj, arr[i])); |
| } |
| } |
| |
| if(isString) { |
| return outArr.join(""); |
| } else { |
| return outArr; |
| } |
| } |
| |
| dojo.lang.tryThese = function(){ |
| for(var x=0; x<arguments.length; x++){ |
| try{ |
| if(typeof arguments[x] == "function"){ |
| var ret = (arguments[x]()); |
| if(ret){ |
| return ret; |
| } |
| } |
| }catch(e){ |
| dojo.debug(e); |
| } |
| } |
| } |
| |
| dojo.lang.delayThese = function(farr, cb, delay, onend){ |
| /** |
| * alternate: (array funcArray, function callback, function onend) |
| * alternate: (array funcArray, function callback) |
| * alternate: (array funcArray) |
| */ |
| if(!farr.length){ |
| if(typeof onend == "function"){ |
| onend(); |
| } |
| return; |
| } |
| if((typeof delay == "undefined")&&(typeof cb == "number")){ |
| delay = cb; |
| cb = function(){}; |
| }else if(!cb){ |
| cb = function(){}; |
| if(!delay){ delay = 0; } |
| } |
| setTimeout(function(){ |
| (farr.shift())(); |
| cb(); |
| dojo.lang.delayThese(farr, cb, delay, onend); |
| }, delay); |
| } |
| |
| dojo.lang.shallowCopy = function(obj) { |
| var ret = {}, key; |
| for(key in obj) { |
| if(dojo.lang.isUndefined(ret[key])) { |
| ret[key] = obj[key]; |
| } |
| } |
| return ret; |
| } |
| |
| dojo.lang.every = function(arr, callback, thisObject) { |
| var isString = dojo.lang.isString(arr); |
| if(isString) { arr = arr.split(""); } |
| if(Array.every) { |
| return Array.every(arr, callback, thisObject); |
| } else { |
| if(!thisObject) { |
| if(arguments.length >= 3) { dojo.raise("thisObject doesn't exist!"); } |
| thisObject = dj_global; |
| } |
| |
| for(var i = 0; i < arr.length; i++) { |
| if(!callback.call(thisObject, arr[i], i, arr)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| |
| dojo.lang.some = function(arr, callback, thisObject) { |
| var isString = dojo.lang.isString(arr); |
| if(isString) { arr = arr.split(""); } |
| if(Array.some) { |
| return Array.some(arr, callback, thisObject); |
| } else { |
| if(!thisObject) { |
| if(arguments.length >= 3) { dojo.raise("thisObject doesn't exist!"); } |
| thisObject = dj_global; |
| } |
| |
| for(var i = 0; i < arr.length; i++) { |
| if(callback.call(thisObject, arr[i], i, arr)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| |
| dojo.lang.filter = function(arr, callback, thisObject) { |
| var isString = dojo.lang.isString(arr); |
| if(isString) { arr = arr.split(""); } |
| if(Array.filter) { |
| var outArr = Array.filter(arr, callback, thisObject); |
| } else { |
| if(!thisObject) { |
| if(arguments.length >= 3) { dojo.raise("thisObject doesn't exist!"); } |
| thisObject = dj_global; |
| } |
| |
| var outArr = []; |
| for(var i = 0; i < arr.length; i++) { |
| if(callback.call(thisObject, arr[i], i, arr)) { |
| outArr.push(arr[i]); |
| } |
| } |
| } |
| if(isString) { |
| return outArr.join(""); |
| } else { |
| return outArr; |
| } |
| } |
| |
| dojo.AdapterRegistry = function(){ |
| /*** |
| A registry to facilitate adaptation. |
| |
| Pairs is an array of [name, check, wrap] triples |
| |
| All check/wrap functions in this registry should be of the same arity. |
| ***/ |
| this.pairs = []; |
| } |
| |
| dojo.lang.extend(dojo.AdapterRegistry, { |
| register: function (name, check, wrap, /* optional */ override){ |
| /*** |
| The check function should return true if the given arguments are |
| appropriate for the wrap function. |
| |
| If override is given and true, the check function will be given |
| highest priority. Otherwise, it will be the lowest priority |
| adapter. |
| ***/ |
| |
| if (override) { |
| this.pairs.unshift([name, check, wrap]); |
| } else { |
| this.pairs.push([name, check, wrap]); |
| } |
| }, |
| |
| match: function (/* ... */) { |
| /*** |
| Find an adapter for the given arguments. |
| |
| If no suitable adapter is found, throws NotFound. |
| ***/ |
| for(var i = 0; i < this.pairs.length; i++){ |
| var pair = this.pairs[i]; |
| if(pair[1].apply(this, arguments)){ |
| return pair[2].apply(this, arguments); |
| } |
| } |
| throw new Error("No match found"); |
| // dojo.raise("No match found"); |
| }, |
| |
| unregister: function (name) { |
| /*** |
| Remove a named adapter from the registry |
| ***/ |
| for(var i = 0; i < this.pairs.length; i++){ |
| var pair = this.pairs[i]; |
| if(pair[0] == name){ |
| this.pairs.splice(i, 1); |
| return true; |
| } |
| } |
| return false; |
| } |
| }); |
| |
| dojo.lang.reprRegistry = new dojo.AdapterRegistry(); |
| dojo.lang.registerRepr = function(name, check, wrap, /*optional*/ override){ |
| /*** |
| Register a repr function. repr functions should take |
| one argument and return a string representation of it |
| suitable for developers, primarily used when debugging. |
| |
| If override is given, it is used as the highest priority |
| repr, otherwise it will be used as the lowest. |
| ***/ |
| dojo.lang.reprRegistry.register(name, check, wrap, override); |
| }; |
| |
| dojo.lang.repr = function(obj){ |
| /*** |
| Return a "programmer representation" for an object |
| ***/ |
| if(typeof(obj) == "undefined"){ |
| return "undefined"; |
| }else if(obj === null){ |
| return "null"; |
| } |
| |
| try{ |
| if(typeof(obj["__repr__"]) == 'function'){ |
| return obj["__repr__"](); |
| }else if((typeof(obj["repr"]) == 'function')&&(obj.repr != arguments.callee)){ |
| return obj["repr"](); |
| } |
| return dojo.lang.reprRegistry.match(obj); |
| }catch(e){ |
| if(typeof(obj.NAME) == 'string' && ( |
| obj.toString == Function.prototype.toString || |
| obj.toString == Object.prototype.toString |
| )){ |
| return o.NAME; |
| } |
| } |
| |
| if(typeof(obj) == "function"){ |
| obj = (obj + "").replace(/^\s+/, ""); |
| var idx = obj.indexOf("{"); |
| if(idx != -1){ |
| obj = obj.substr(0, idx) + "{...}"; |
| } |
| } |
| return obj + ""; |
| } |
| |
| dojo.lang.reprArrayLike = function(arr){ |
| try{ |
| var na = dojo.lang.map(arr, dojo.lang.repr); |
| return "[" + na.join(", ") + "]"; |
| }catch(e){ } |
| }; |
| |
| dojo.lang.reprString = function(str){ |
| return ('"' + str.replace(/(["\\])/g, '\\$1') + '"' |
| ).replace(/[\f]/g, "\\f" |
| ).replace(/[\b]/g, "\\b" |
| ).replace(/[\n]/g, "\\n" |
| ).replace(/[\t]/g, "\\t" |
| ).replace(/[\r]/g, "\\r"); |
| }; |
| |
| dojo.lang.reprNumber = function(num){ |
| return num + ""; |
| }; |
| |
| (function(){ |
| var m = dojo.lang; |
| m.registerRepr("arrayLike", m.isArrayLike, m.reprArrayLike); |
| m.registerRepr("string", m.isString, m.reprString); |
| m.registerRepr("numbers", m.isNumber, m.reprNumber); |
| m.registerRepr("boolean", m.isBoolean, m.reprNumber); |
| // m.registerRepr("numbers", m.typeMatcher("number", "boolean"), m.reprNumber); |
| })(); |
| |
| /** |
| * Creates a 1-D array out of all the arguments passed, |
| * unravelling any array-like objects in the process |
| * |
| * Ex: |
| * unnest(1, 2, 3) ==> [1, 2, 3] |
| * unnest(1, [2, [3], [[[4]]]]) ==> [1, 2, 3, 4] |
| */ |
| dojo.lang.unnest = function(/* ... */) { |
| var out = []; |
| for(var i = 0; i < arguments.length; i++) { |
| if(dojo.lang.isArrayLike(arguments[i])) { |
| var add = dojo.lang.unnest.apply(this, arguments[i]); |
| out = out.concat(add); |
| } else { |
| out.push(arguments[i]); |
| } |
| } |
| return out; |
| } |
| |
| /** |
| * Return the first argument that isn't undefined |
| */ |
| dojo.lang.firstValued = function(/* ... */) { |
| for(var i = 0; i < arguments.length; i++) { |
| if(typeof arguments[i] != "undefined") { |
| return arguments[i]; |
| } |
| } |
| return undefined; |
| } |
| |
| /** |
| * Converts an array-like object (i.e. arguments, DOMCollection) |
| * to an array |
| **/ |
| dojo.lang.toArray = function(arrayLike, startOffset) { |
| var array = []; |
| for(var i = startOffset||0; i < arrayLike.length; i++) { |
| array.push(arrayLike[i]); |
| } |
| return array; |
| } |