[483] | 1 | define(["./kernel", "../has", "../sniff"], function(dojo, has){ |
---|
| 2 | // module: |
---|
| 3 | // dojo/_base/lang |
---|
| 4 | |
---|
| 5 | has.add("bug-for-in-skips-shadowed", function(){ |
---|
| 6 | // if true, the for-in iterator skips object properties that exist in Object's prototype (IE 6 - ?) |
---|
| 7 | for(var i in {toString: 1}){ |
---|
| 8 | return 0; |
---|
| 9 | } |
---|
| 10 | return 1; |
---|
| 11 | }); |
---|
| 12 | |
---|
| 13 | // Helper methods |
---|
| 14 | var _extraNames = |
---|
| 15 | has("bug-for-in-skips-shadowed") ? |
---|
| 16 | "hasOwnProperty.valueOf.isPrototypeOf.propertyIsEnumerable.toLocaleString.toString.constructor".split(".") : [], |
---|
| 17 | |
---|
| 18 | _extraLen = _extraNames.length, |
---|
| 19 | |
---|
| 20 | getProp = function(/*Array*/parts, /*Boolean*/create, /*Object*/context){ |
---|
| 21 | var p, i = 0, dojoGlobal = dojo.global; |
---|
| 22 | if(!context){ |
---|
| 23 | if(!parts.length){ |
---|
| 24 | return dojoGlobal; |
---|
| 25 | }else{ |
---|
| 26 | p = parts[i++]; |
---|
| 27 | try{ |
---|
| 28 | context = dojo.scopeMap[p] && dojo.scopeMap[p][1]; |
---|
| 29 | }catch(e){} |
---|
| 30 | context = context || (p in dojoGlobal ? dojoGlobal[p] : (create ? dojoGlobal[p] = {} : undefined)); |
---|
| 31 | } |
---|
| 32 | } |
---|
| 33 | while(context && (p = parts[i++])){ |
---|
| 34 | context = (p in context ? context[p] : (create ? context[p] = {} : undefined)); |
---|
| 35 | } |
---|
| 36 | return context; // mixed |
---|
| 37 | }, |
---|
| 38 | |
---|
| 39 | opts = Object.prototype.toString, |
---|
| 40 | |
---|
| 41 | efficient = function(obj, offset, startWith){ |
---|
| 42 | return (startWith||[]).concat(Array.prototype.slice.call(obj, offset||0)); |
---|
| 43 | }, |
---|
| 44 | |
---|
| 45 | _pattern = /\{([^\}]+)\}/g; |
---|
| 46 | |
---|
| 47 | // Module export |
---|
| 48 | var lang = { |
---|
| 49 | // summary: |
---|
| 50 | // This module defines Javascript language extensions. |
---|
| 51 | |
---|
| 52 | // _extraNames: String[] |
---|
| 53 | // Lists property names that must be explicitly processed during for-in iteration |
---|
| 54 | // in environments that have has("bug-for-in-skips-shadowed") true. |
---|
| 55 | _extraNames:_extraNames, |
---|
| 56 | |
---|
| 57 | _mixin: function(dest, source, copyFunc){ |
---|
| 58 | // summary: |
---|
| 59 | // Copies/adds all properties of source to dest; returns dest. |
---|
| 60 | // dest: Object |
---|
| 61 | // The object to which to copy/add all properties contained in source. |
---|
| 62 | // source: Object |
---|
| 63 | // The object from which to draw all properties to copy into dest. |
---|
| 64 | // copyFunc: Function? |
---|
| 65 | // The process used to copy/add a property in source; defaults to the Javascript assignment operator. |
---|
| 66 | // returns: |
---|
| 67 | // dest, as modified |
---|
| 68 | // description: |
---|
| 69 | // All properties, including functions (sometimes termed "methods"), excluding any non-standard extensions |
---|
| 70 | // found in Object.prototype, are copied/added to dest. Copying/adding each particular property is |
---|
| 71 | // delegated to copyFunc (if any); copyFunc defaults to the Javascript assignment operator if not provided. |
---|
| 72 | // Notice that by default, _mixin executes a so-called "shallow copy" and aggregate types are copied/added by reference. |
---|
| 73 | var name, s, i, empty = {}; |
---|
| 74 | for(name in source){ |
---|
| 75 | // the (!(name in empty) || empty[name] !== s) condition avoids copying properties in "source" |
---|
| 76 | // inherited from Object.prototype. For example, if dest has a custom toString() method, |
---|
| 77 | // don't overwrite it with the toString() method that source inherited from Object.prototype |
---|
| 78 | s = source[name]; |
---|
| 79 | if(!(name in dest) || (dest[name] !== s && (!(name in empty) || empty[name] !== s))){ |
---|
| 80 | dest[name] = copyFunc ? copyFunc(s) : s; |
---|
| 81 | } |
---|
| 82 | } |
---|
| 83 | |
---|
| 84 | if(has("bug-for-in-skips-shadowed")){ |
---|
| 85 | if(source){ |
---|
| 86 | for(i = 0; i < _extraLen; ++i){ |
---|
| 87 | name = _extraNames[i]; |
---|
| 88 | s = source[name]; |
---|
| 89 | if(!(name in dest) || (dest[name] !== s && (!(name in empty) || empty[name] !== s))){ |
---|
| 90 | dest[name] = copyFunc ? copyFunc(s) : s; |
---|
| 91 | } |
---|
| 92 | } |
---|
| 93 | } |
---|
| 94 | } |
---|
| 95 | |
---|
| 96 | return dest; // Object |
---|
| 97 | }, |
---|
| 98 | |
---|
| 99 | mixin: function(dest, sources){ |
---|
| 100 | // summary: |
---|
| 101 | // Copies/adds all properties of one or more sources to dest; returns dest. |
---|
| 102 | // dest: Object |
---|
| 103 | // The object to which to copy/add all properties contained in source. If dest is falsy, then |
---|
| 104 | // a new object is manufactured before copying/adding properties begins. |
---|
| 105 | // sources: Object... |
---|
| 106 | // One of more objects from which to draw all properties to copy into dest. sources are processed |
---|
| 107 | // left-to-right and if more than one of these objects contain the same property name, the right-most |
---|
| 108 | // value "wins". |
---|
| 109 | // returns: Object |
---|
| 110 | // dest, as modified |
---|
| 111 | // description: |
---|
| 112 | // All properties, including functions (sometimes termed "methods"), excluding any non-standard extensions |
---|
| 113 | // found in Object.prototype, are copied/added from sources to dest. sources are processed left to right. |
---|
| 114 | // The Javascript assignment operator is used to copy/add each property; therefore, by default, mixin |
---|
| 115 | // executes a so-called "shallow copy" and aggregate types are copied/added by reference. |
---|
| 116 | // example: |
---|
| 117 | // make a shallow copy of an object |
---|
| 118 | // | var copy = lang.mixin({}, source); |
---|
| 119 | // example: |
---|
| 120 | // many class constructors often take an object which specifies |
---|
| 121 | // values to be configured on the object. In this case, it is |
---|
| 122 | // often simplest to call `lang.mixin` on the `this` object: |
---|
| 123 | // | declare("acme.Base", null, { |
---|
| 124 | // | constructor: function(properties){ |
---|
| 125 | // | // property configuration: |
---|
| 126 | // | lang.mixin(this, properties); |
---|
| 127 | // | |
---|
| 128 | // | console.log(this.quip); |
---|
| 129 | // | // ... |
---|
| 130 | // | }, |
---|
| 131 | // | quip: "I wasn't born yesterday, you know - I've seen movies.", |
---|
| 132 | // | // ... |
---|
| 133 | // | }); |
---|
| 134 | // | |
---|
| 135 | // | // create an instance of the class and configure it |
---|
| 136 | // | var b = new acme.Base({quip: "That's what it does!" }); |
---|
| 137 | // example: |
---|
| 138 | // copy in properties from multiple objects |
---|
| 139 | // | var flattened = lang.mixin( |
---|
| 140 | // | { |
---|
| 141 | // | name: "Frylock", |
---|
| 142 | // | braces: true |
---|
| 143 | // | }, |
---|
| 144 | // | { |
---|
| 145 | // | name: "Carl Brutanananadilewski" |
---|
| 146 | // | } |
---|
| 147 | // | ); |
---|
| 148 | // | |
---|
| 149 | // | // will print "Carl Brutanananadilewski" |
---|
| 150 | // | console.log(flattened.name); |
---|
| 151 | // | // will print "true" |
---|
| 152 | // | console.log(flattened.braces); |
---|
| 153 | |
---|
| 154 | if(!dest){ dest = {}; } |
---|
| 155 | for(var i = 1, l = arguments.length; i < l; i++){ |
---|
| 156 | lang._mixin(dest, arguments[i]); |
---|
| 157 | } |
---|
| 158 | return dest; // Object |
---|
| 159 | }, |
---|
| 160 | |
---|
| 161 | setObject: function(name, value, context){ |
---|
| 162 | // summary: |
---|
| 163 | // Set a property from a dot-separated string, such as "A.B.C" |
---|
| 164 | // description: |
---|
| 165 | // Useful for longer api chains where you have to test each object in |
---|
| 166 | // the chain, or when you have an object reference in string format. |
---|
| 167 | // Objects are created as needed along `path`. Returns the passed |
---|
| 168 | // value if setting is successful or `undefined` if not. |
---|
| 169 | // name: String |
---|
| 170 | // Path to a property, in the form "A.B.C". |
---|
| 171 | // value: anything |
---|
| 172 | // value or object to place at location given by name |
---|
| 173 | // context: Object? |
---|
| 174 | // Optional. Object to use as root of path. Defaults to |
---|
| 175 | // `dojo.global`. |
---|
| 176 | // example: |
---|
| 177 | // set the value of `foo.bar.baz`, regardless of whether |
---|
| 178 | // intermediate objects already exist: |
---|
| 179 | // | lang.setObject("foo.bar.baz", value); |
---|
| 180 | // example: |
---|
| 181 | // without `lang.setObject`, we often see code like this: |
---|
| 182 | // | // ensure that intermediate objects are available |
---|
| 183 | // | if(!obj["parent"]){ obj.parent = {}; } |
---|
| 184 | // | if(!obj.parent["child"]){ obj.parent.child = {}; } |
---|
| 185 | // | // now we can safely set the property |
---|
| 186 | // | obj.parent.child.prop = "some value"; |
---|
| 187 | // whereas with `lang.setObject`, we can shorten that to: |
---|
| 188 | // | lang.setObject("parent.child.prop", "some value", obj); |
---|
| 189 | |
---|
| 190 | var parts = name.split("."), p = parts.pop(), obj = getProp(parts, true, context); |
---|
| 191 | return obj && p ? (obj[p] = value) : undefined; // Object |
---|
| 192 | }, |
---|
| 193 | |
---|
| 194 | getObject: function(name, create, context){ |
---|
| 195 | // summary: |
---|
| 196 | // Get a property from a dot-separated string, such as "A.B.C" |
---|
| 197 | // description: |
---|
| 198 | // Useful for longer api chains where you have to test each object in |
---|
| 199 | // the chain, or when you have an object reference in string format. |
---|
| 200 | // name: String |
---|
| 201 | // Path to an property, in the form "A.B.C". |
---|
| 202 | // create: Boolean? |
---|
| 203 | // Optional. Defaults to `false`. If `true`, Objects will be |
---|
| 204 | // created at any point along the 'path' that is undefined. |
---|
| 205 | // context: Object? |
---|
| 206 | // Optional. Object to use as root of path. Defaults to |
---|
| 207 | // 'dojo.global'. Null may be passed. |
---|
| 208 | return getProp(name.split("."), create, context); // Object |
---|
| 209 | }, |
---|
| 210 | |
---|
| 211 | exists: function(name, obj){ |
---|
| 212 | // summary: |
---|
| 213 | // determine if an object supports a given method |
---|
| 214 | // description: |
---|
| 215 | // useful for longer api chains where you have to test each object in |
---|
| 216 | // the chain. Useful for object and method detection. |
---|
| 217 | // name: String |
---|
| 218 | // Path to an object, in the form "A.B.C". |
---|
| 219 | // obj: Object? |
---|
| 220 | // Object to use as root of path. Defaults to |
---|
| 221 | // 'dojo.global'. Null may be passed. |
---|
| 222 | // example: |
---|
| 223 | // | // define an object |
---|
| 224 | // | var foo = { |
---|
| 225 | // | bar: { } |
---|
| 226 | // | }; |
---|
| 227 | // | |
---|
| 228 | // | // search the global scope |
---|
| 229 | // | lang.exists("foo.bar"); // true |
---|
| 230 | // | lang.exists("foo.bar.baz"); // false |
---|
| 231 | // | |
---|
| 232 | // | // search from a particular scope |
---|
| 233 | // | lang.exists("bar", foo); // true |
---|
| 234 | // | lang.exists("bar.baz", foo); // false |
---|
| 235 | return lang.getObject(name, false, obj) !== undefined; // Boolean |
---|
| 236 | }, |
---|
| 237 | |
---|
| 238 | // Crockford (ish) functions |
---|
| 239 | |
---|
| 240 | isString: function(it){ |
---|
| 241 | // summary: |
---|
| 242 | // Return true if it is a String |
---|
| 243 | // it: anything |
---|
| 244 | // Item to test. |
---|
| 245 | return (typeof it == "string" || it instanceof String); // Boolean |
---|
| 246 | }, |
---|
| 247 | |
---|
| 248 | isArray: function(it){ |
---|
| 249 | // summary: |
---|
| 250 | // Return true if it is an Array. |
---|
| 251 | // Does not work on Arrays created in other windows. |
---|
| 252 | // it: anything |
---|
| 253 | // Item to test. |
---|
| 254 | return it && (it instanceof Array || typeof it == "array"); // Boolean |
---|
| 255 | }, |
---|
| 256 | |
---|
| 257 | isFunction: function(it){ |
---|
| 258 | // summary: |
---|
| 259 | // Return true if it is a Function |
---|
| 260 | // it: anything |
---|
| 261 | // Item to test. |
---|
| 262 | return opts.call(it) === "[object Function]"; |
---|
| 263 | }, |
---|
| 264 | |
---|
| 265 | isObject: function(it){ |
---|
| 266 | // summary: |
---|
| 267 | // Returns true if it is a JavaScript object (or an Array, a Function |
---|
| 268 | // or null) |
---|
| 269 | // it: anything |
---|
| 270 | // Item to test. |
---|
| 271 | return it !== undefined && |
---|
| 272 | (it === null || typeof it == "object" || lang.isArray(it) || lang.isFunction(it)); // Boolean |
---|
| 273 | }, |
---|
| 274 | |
---|
| 275 | isArrayLike: function(it){ |
---|
| 276 | // summary: |
---|
| 277 | // similar to isArray() but more permissive |
---|
| 278 | // it: anything |
---|
| 279 | // Item to test. |
---|
| 280 | // returns: |
---|
| 281 | // If it walks like a duck and quacks like a duck, return `true` |
---|
| 282 | // description: |
---|
| 283 | // Doesn't strongly test for "arrayness". Instead, settles for "isn't |
---|
| 284 | // a string or number and has a length property". Arguments objects |
---|
| 285 | // and DOM collections will return true when passed to |
---|
| 286 | // isArrayLike(), but will return false when passed to |
---|
| 287 | // isArray(). |
---|
| 288 | return it && it !== undefined && // Boolean |
---|
| 289 | // keep out built-in constructors (Number, String, ...) which have length |
---|
| 290 | // properties |
---|
| 291 | !lang.isString(it) && !lang.isFunction(it) && |
---|
| 292 | !(it.tagName && it.tagName.toLowerCase() == 'form') && |
---|
| 293 | (lang.isArray(it) || isFinite(it.length)); |
---|
| 294 | }, |
---|
| 295 | |
---|
| 296 | isAlien: function(it){ |
---|
| 297 | // summary: |
---|
| 298 | // Returns true if it is a built-in function or some other kind of |
---|
| 299 | // oddball that *should* report as a function but doesn't |
---|
| 300 | return it && !lang.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean |
---|
| 301 | }, |
---|
| 302 | |
---|
| 303 | extend: function(ctor, props){ |
---|
| 304 | // summary: |
---|
| 305 | // Adds all properties and methods of props to constructor's |
---|
| 306 | // prototype, making them available to all instances created with |
---|
| 307 | // constructor. |
---|
| 308 | // ctor: Object |
---|
| 309 | // Target constructor to extend. |
---|
| 310 | // props: Object |
---|
| 311 | // One or more objects to mix into ctor.prototype |
---|
| 312 | for(var i=1, l=arguments.length; i<l; i++){ |
---|
| 313 | lang._mixin(ctor.prototype, arguments[i]); |
---|
| 314 | } |
---|
| 315 | return ctor; // Object |
---|
| 316 | }, |
---|
| 317 | |
---|
| 318 | _hitchArgs: function(scope, method){ |
---|
| 319 | var pre = lang._toArray(arguments, 2); |
---|
| 320 | var named = lang.isString(method); |
---|
| 321 | return function(){ |
---|
| 322 | // arrayify arguments |
---|
| 323 | var args = lang._toArray(arguments); |
---|
| 324 | // locate our method |
---|
| 325 | var f = named ? (scope||dojo.global)[method] : method; |
---|
| 326 | // invoke with collected args |
---|
| 327 | return f && f.apply(scope || this, pre.concat(args)); // mixed |
---|
| 328 | }; // Function |
---|
| 329 | }, |
---|
| 330 | |
---|
| 331 | hitch: function(scope, method){ |
---|
| 332 | // summary: |
---|
| 333 | // Returns a function that will only ever execute in the a given scope. |
---|
| 334 | // This allows for easy use of object member functions |
---|
| 335 | // in callbacks and other places in which the "this" keyword may |
---|
| 336 | // otherwise not reference the expected scope. |
---|
| 337 | // Any number of default positional arguments may be passed as parameters |
---|
| 338 | // beyond "method". |
---|
| 339 | // Each of these values will be used to "placehold" (similar to curry) |
---|
| 340 | // for the hitched function. |
---|
| 341 | // scope: Object |
---|
| 342 | // The scope to use when method executes. If method is a string, |
---|
| 343 | // scope is also the object containing method. |
---|
| 344 | // method: Function|String... |
---|
| 345 | // A function to be hitched to scope, or the name of the method in |
---|
| 346 | // scope to be hitched. |
---|
| 347 | // example: |
---|
| 348 | // | lang.hitch(foo, "bar")(); |
---|
| 349 | // runs foo.bar() in the scope of foo |
---|
| 350 | // example: |
---|
| 351 | // | lang.hitch(foo, myFunction); |
---|
| 352 | // returns a function that runs myFunction in the scope of foo |
---|
| 353 | // example: |
---|
| 354 | // Expansion on the default positional arguments passed along from |
---|
| 355 | // hitch. Passed args are mixed first, additional args after. |
---|
| 356 | // | var foo = { bar: function(a, b, c){ console.log(a, b, c); } }; |
---|
| 357 | // | var fn = lang.hitch(foo, "bar", 1, 2); |
---|
| 358 | // | fn(3); // logs "1, 2, 3" |
---|
| 359 | // example: |
---|
| 360 | // | var foo = { bar: 2 }; |
---|
| 361 | // | lang.hitch(foo, function(){ this.bar = 10; })(); |
---|
| 362 | // execute an anonymous function in scope of foo |
---|
| 363 | if(arguments.length > 2){ |
---|
| 364 | return lang._hitchArgs.apply(dojo, arguments); // Function |
---|
| 365 | } |
---|
| 366 | if(!method){ |
---|
| 367 | method = scope; |
---|
| 368 | scope = null; |
---|
| 369 | } |
---|
| 370 | if(lang.isString(method)){ |
---|
| 371 | scope = scope || dojo.global; |
---|
| 372 | if(!scope[method]){ throw(['lang.hitch: scope["', method, '"] is null (scope="', scope, '")'].join('')); } |
---|
| 373 | return function(){ return scope[method].apply(scope, arguments || []); }; // Function |
---|
| 374 | } |
---|
| 375 | return !scope ? method : function(){ return method.apply(scope, arguments || []); }; // Function |
---|
| 376 | }, |
---|
| 377 | |
---|
| 378 | delegate: (function(){ |
---|
| 379 | // boodman/crockford delegation w/ cornford optimization |
---|
| 380 | function TMP(){} |
---|
| 381 | return function(obj, props){ |
---|
| 382 | TMP.prototype = obj; |
---|
| 383 | var tmp = new TMP(); |
---|
| 384 | TMP.prototype = null; |
---|
| 385 | if(props){ |
---|
| 386 | lang._mixin(tmp, props); |
---|
| 387 | } |
---|
| 388 | return tmp; // Object |
---|
| 389 | }; |
---|
| 390 | })(), |
---|
| 391 | /*===== |
---|
| 392 | delegate: function(obj, props){ |
---|
| 393 | // summary: |
---|
| 394 | // Returns a new object which "looks" to obj for properties which it |
---|
| 395 | // does not have a value for. Optionally takes a bag of properties to |
---|
| 396 | // seed the returned object with initially. |
---|
| 397 | // description: |
---|
| 398 | // This is a small implementation of the Boodman/Crockford delegation |
---|
| 399 | // pattern in JavaScript. An intermediate object constructor mediates |
---|
| 400 | // the prototype chain for the returned object, using it to delegate |
---|
| 401 | // down to obj for property lookup when object-local lookup fails. |
---|
| 402 | // This can be thought of similarly to ES4's "wrap", save that it does |
---|
| 403 | // not act on types but rather on pure objects. |
---|
| 404 | // obj: Object |
---|
| 405 | // The object to delegate to for properties not found directly on the |
---|
| 406 | // return object or in props. |
---|
| 407 | // props: Object... |
---|
| 408 | // an object containing properties to assign to the returned object |
---|
| 409 | // returns: |
---|
| 410 | // an Object of anonymous type |
---|
| 411 | // example: |
---|
| 412 | // | var foo = { bar: "baz" }; |
---|
| 413 | // | var thinger = lang.delegate(foo, { thud: "xyzzy"}); |
---|
| 414 | // | thinger.bar == "baz"; // delegated to foo |
---|
| 415 | // | foo.thud == undefined; // by definition |
---|
| 416 | // | thinger.thud == "xyzzy"; // mixed in from props |
---|
| 417 | // | foo.bar = "thonk"; |
---|
| 418 | // | thinger.bar == "thonk"; // still delegated to foo's bar |
---|
| 419 | }, |
---|
| 420 | =====*/ |
---|
| 421 | |
---|
| 422 | _toArray: has("ie") ? |
---|
| 423 | (function(){ |
---|
| 424 | function slow(obj, offset, startWith){ |
---|
| 425 | var arr = startWith||[]; |
---|
| 426 | for(var x = offset || 0; x < obj.length; x++){ |
---|
| 427 | arr.push(obj[x]); |
---|
| 428 | } |
---|
| 429 | return arr; |
---|
| 430 | } |
---|
| 431 | return function(obj){ |
---|
| 432 | return ((obj.item) ? slow : efficient).apply(this, arguments); |
---|
| 433 | }; |
---|
| 434 | })() : efficient, |
---|
| 435 | /*===== |
---|
| 436 | _toArray: function(obj, offset, startWith){ |
---|
| 437 | // summary: |
---|
| 438 | // Converts an array-like object (i.e. arguments, DOMCollection) to an |
---|
| 439 | // array. Returns a new Array with the elements of obj. |
---|
| 440 | // obj: Object |
---|
| 441 | // the object to "arrayify". We expect the object to have, at a |
---|
| 442 | // minimum, a length property which corresponds to integer-indexed |
---|
| 443 | // properties. |
---|
| 444 | // offset: Number? |
---|
| 445 | // the location in obj to start iterating from. Defaults to 0. |
---|
| 446 | // Optional. |
---|
| 447 | // startWith: Array? |
---|
| 448 | // An array to pack with the properties of obj. If provided, |
---|
| 449 | // properties in obj are appended at the end of startWith and |
---|
| 450 | // startWith is the returned array. |
---|
| 451 | }, |
---|
| 452 | =====*/ |
---|
| 453 | |
---|
| 454 | partial: function(/*Function|String*/ method /*, ...*/){ |
---|
| 455 | // summary: |
---|
| 456 | // similar to hitch() except that the scope object is left to be |
---|
| 457 | // whatever the execution context eventually becomes. |
---|
| 458 | // description: |
---|
| 459 | // Calling lang.partial is the functional equivalent of calling: |
---|
| 460 | // | lang.hitch(null, funcName, ...); |
---|
| 461 | // method: |
---|
| 462 | // The function to "wrap" |
---|
| 463 | var arr = [ null ]; |
---|
| 464 | return lang.hitch.apply(dojo, arr.concat(lang._toArray(arguments))); // Function |
---|
| 465 | }, |
---|
| 466 | |
---|
| 467 | clone: function(/*anything*/ src){ |
---|
| 468 | // summary: |
---|
| 469 | // Clones objects (including DOM nodes) and all children. |
---|
| 470 | // Warning: do not clone cyclic structures. |
---|
| 471 | // src: |
---|
| 472 | // The object to clone |
---|
| 473 | if(!src || typeof src != "object" || lang.isFunction(src)){ |
---|
| 474 | // null, undefined, any non-object, or function |
---|
| 475 | return src; // anything |
---|
| 476 | } |
---|
| 477 | if(src.nodeType && "cloneNode" in src){ |
---|
| 478 | // DOM Node |
---|
| 479 | return src.cloneNode(true); // Node |
---|
| 480 | } |
---|
| 481 | if(src instanceof Date){ |
---|
| 482 | // Date |
---|
| 483 | return new Date(src.getTime()); // Date |
---|
| 484 | } |
---|
| 485 | if(src instanceof RegExp){ |
---|
| 486 | // RegExp |
---|
| 487 | return new RegExp(src); // RegExp |
---|
| 488 | } |
---|
| 489 | var r, i, l; |
---|
| 490 | if(lang.isArray(src)){ |
---|
| 491 | // array |
---|
| 492 | r = []; |
---|
| 493 | for(i = 0, l = src.length; i < l; ++i){ |
---|
| 494 | if(i in src){ |
---|
| 495 | r.push(lang.clone(src[i])); |
---|
| 496 | } |
---|
| 497 | } |
---|
| 498 | // we don't clone functions for performance reasons |
---|
| 499 | // }else if(d.isFunction(src)){ |
---|
| 500 | // // function |
---|
| 501 | // r = function(){ return src.apply(this, arguments); }; |
---|
| 502 | }else{ |
---|
| 503 | // generic objects |
---|
| 504 | r = src.constructor ? new src.constructor() : {}; |
---|
| 505 | } |
---|
| 506 | return lang._mixin(r, src, lang.clone); |
---|
| 507 | }, |
---|
| 508 | |
---|
| 509 | |
---|
| 510 | trim: String.prototype.trim ? |
---|
| 511 | function(str){ return str.trim(); } : |
---|
| 512 | function(str){ return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); }, |
---|
| 513 | /*===== |
---|
| 514 | trim: function(str){ |
---|
| 515 | // summary: |
---|
| 516 | // Trims whitespace from both sides of the string |
---|
| 517 | // str: String |
---|
| 518 | // String to be trimmed |
---|
| 519 | // returns: String |
---|
| 520 | // Returns the trimmed string |
---|
| 521 | // description: |
---|
| 522 | // This version of trim() was selected for inclusion into the base due |
---|
| 523 | // to its compact size and relatively good performance |
---|
| 524 | // (see [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript) |
---|
| 525 | // Uses String.prototype.trim instead, if available. |
---|
| 526 | // The fastest but longest version of this function is located at |
---|
| 527 | // lang.string.trim() |
---|
| 528 | }, |
---|
| 529 | =====*/ |
---|
| 530 | |
---|
| 531 | replace: function(tmpl, map, pattern){ |
---|
| 532 | // summary: |
---|
| 533 | // Performs parameterized substitutions on a string. Throws an |
---|
| 534 | // exception if any parameter is unmatched. |
---|
| 535 | // tmpl: String |
---|
| 536 | // String to be used as a template. |
---|
| 537 | // map: Object|Function |
---|
| 538 | // If an object, it is used as a dictionary to look up substitutions. |
---|
| 539 | // If a function, it is called for every substitution with following parameters: |
---|
| 540 | // a whole match, a name, an offset, and the whole template |
---|
| 541 | // string (see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/replace |
---|
| 542 | // for more details). |
---|
| 543 | // pattern: RegEx? |
---|
| 544 | // Optional regular expression objects that overrides the default pattern. |
---|
| 545 | // Must be global and match one item. The default is: /\{([^\}]+)\}/g, |
---|
| 546 | // which matches patterns like that: "{xxx}", where "xxx" is any sequence |
---|
| 547 | // of characters, which doesn't include "}". |
---|
| 548 | // returns: String |
---|
| 549 | // Returns the substituted string. |
---|
| 550 | // example: |
---|
| 551 | // | // uses a dictionary for substitutions: |
---|
| 552 | // | lang.replace("Hello, {name.first} {name.last} AKA {nick}!", |
---|
| 553 | // | { |
---|
| 554 | // | nick: "Bob", |
---|
| 555 | // | name: { |
---|
| 556 | // | first: "Robert", |
---|
| 557 | // | middle: "X", |
---|
| 558 | // | last: "Cringely" |
---|
| 559 | // | } |
---|
| 560 | // | }); |
---|
| 561 | // | // returns: Hello, Robert Cringely AKA Bob! |
---|
| 562 | // example: |
---|
| 563 | // | // uses an array for substitutions: |
---|
| 564 | // | lang.replace("Hello, {0} {2}!", |
---|
| 565 | // | ["Robert", "X", "Cringely"]); |
---|
| 566 | // | // returns: Hello, Robert Cringely! |
---|
| 567 | // example: |
---|
| 568 | // | // uses a function for substitutions: |
---|
| 569 | // | function sum(a){ |
---|
| 570 | // | var t = 0; |
---|
| 571 | // | arrayforEach(a, function(x){ t += x; }); |
---|
| 572 | // | return t; |
---|
| 573 | // | } |
---|
| 574 | // | lang.replace( |
---|
| 575 | // | "{count} payments averaging {avg} USD per payment.", |
---|
| 576 | // | lang.hitch( |
---|
| 577 | // | { payments: [11, 16, 12] }, |
---|
| 578 | // | function(_, key){ |
---|
| 579 | // | switch(key){ |
---|
| 580 | // | case "count": return this.payments.length; |
---|
| 581 | // | case "min": return Math.min.apply(Math, this.payments); |
---|
| 582 | // | case "max": return Math.max.apply(Math, this.payments); |
---|
| 583 | // | case "sum": return sum(this.payments); |
---|
| 584 | // | case "avg": return sum(this.payments) / this.payments.length; |
---|
| 585 | // | } |
---|
| 586 | // | } |
---|
| 587 | // | ) |
---|
| 588 | // | ); |
---|
| 589 | // | // prints: 3 payments averaging 13 USD per payment. |
---|
| 590 | // example: |
---|
| 591 | // | // uses an alternative PHP-like pattern for substitutions: |
---|
| 592 | // | lang.replace("Hello, ${0} ${2}!", |
---|
| 593 | // | ["Robert", "X", "Cringely"], /\$\{([^\}]+)\}/g); |
---|
| 594 | // | // returns: Hello, Robert Cringely! |
---|
| 595 | |
---|
| 596 | return tmpl.replace(pattern || _pattern, lang.isFunction(map) ? |
---|
| 597 | map : function(_, k){ return lang.getObject(k, false, map); }); |
---|
| 598 | } |
---|
| 599 | }; |
---|
| 600 | |
---|
| 601 | has("extend-dojo") && lang.mixin(dojo, lang); |
---|
| 602 | |
---|
| 603 | return lang; |
---|
| 604 | }); |
---|
| 605 | |
---|