]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/_base/lang.js.uncompressed.js
1 define("dojo/_base/lang", ["./kernel", "../has", "./sniff"], function(dojo
, has
){
5 // This module defines Javascript language extensions.
7 has
.add("bug-for-in-skips-shadowed", function(){
8 // if true, the for-in interator skips object properties that exist in Object's prototype (IE 6 - ?)
9 for(var i
in {toString
: 1}){
16 has("bug-for-in-skips-shadowed") ?
17 "hasOwnProperty.valueOf.isPrototypeOf.propertyIsEnumerable.toLocaleString.toString.constructor".split(".") : [],
19 _extraLen
= _extraNames
.length
,
21 _mixin = function(dest
, source
, copyFunc
){
22 var name
, s
, i
, empty
= {};
24 // the (!(name in empty) || empty[name] !== s) condition avoids copying properties in "source"
25 // inherited from Object.prototype. For example, if dest has a custom toString() method,
26 // don't overwrite it with the toString() method that source inherited from Object.prototype
28 if(!(name
in dest
) || (dest
[name
] !== s
&& (!(name
in empty
) || empty
[name
] !== s
))){
29 dest
[name
] = copyFunc
? copyFunc(s
) : s
;
33 if(has("bug-for-in-skips-shadowed")){
35 for(i
= 0; i
< _extraLen
; ++i
){
36 name
= _extraNames
[i
];
38 if(!(name
in dest
) || (dest
[name
] !== s
&& (!(name
in empty
) || empty
[name
] !== s
))){
39 dest
[name
] = copyFunc
? copyFunc(s
) : s
;
45 return dest
; // Object
48 mixin = function(dest
, sources
){
49 if(!dest
){ dest
= {}; }
50 for(var i
= 1, l
= arguments
.length
; i
< l
; i
++){
51 lang
._mixin(dest
, arguments
[i
]);
53 return dest
; // Object
56 getProp = function(/*Array*/parts
, /*Boolean*/create
, /*Object*/context
){
57 var p
, i
= 0, dojoGlobal
= dojo
.global
;
64 context
= dojo
.scopeMap
[p
] && dojo
.scopeMap
[p
][1];
66 context
= context
|| (p
in dojoGlobal
? dojoGlobal
[p
] : (create
? dojoGlobal
[p
] = {} : undefined));
69 while(context
&& (p
= parts
[i
++])){
70 context
= (p
in context
? context
[p
] : (create
? context
[p
] = {} : undefined));
72 return context
; // mixed
75 setObject = function(name
, value
, context
){
76 var parts
= name
.split("."), p
= parts
.pop(), obj
= getProp(parts
, true, context
);
77 return obj
&& p
? (obj
[p
] = value
) : undefined; // Object
80 getObject = function(name
, create
, context
){
81 return getProp(name
.split("."), create
, context
); // Object
84 exists = function(name
, obj
){
85 return lang
.getObject(name
, false, obj
) !== undefined; // Boolean
88 opts
= Object
.prototype.toString
,
90 // Crockford (ish) functions
92 isString = function(it
){
93 return (typeof it
== "string" || it
instanceof String
); // Boolean
96 isArray = function(it
){
97 return it
&& (it
instanceof Array
|| typeof it
== "array"); // Boolean
100 isFunction = function(it
){
101 return opts
.call(it
) === "[object Function]";
104 isObject = function(it
){
105 return it
!== undefined &&
106 (it
=== null || typeof it
== "object" || lang
.isArray(it
) || lang
.isFunction(it
)); // Boolean
109 isArrayLike = function(it
){
110 return it
&& it
!== undefined && // Boolean
111 // keep out built-in constructors (Number, String, ...) which have length
113 !lang
.isString(it
) && !lang
.isFunction(it
) &&
114 !(it
.tagName
&& it
.tagName
.toLowerCase() == 'form') &&
115 (lang
.isArray(it
) || isFinite(it
.length
));
118 isAlien = function(it
){
119 return it
&& !lang
.isFunction(it
) && /\{\s*\[native code\]\s*\}/.test(String(it
)); // Boolean
122 extend = function(constructor, props
){
123 for(var i
=1, l
=arguments
.length
; i
<l
; i
++){
124 lang
._mixin(constructor.prototype, arguments
[i
]);
126 return constructor; // Object
129 _hitchArgs = function(scope
, method
){
130 var pre
= _toArray(arguments
, 2);
131 var named
= lang
.isString(method
);
133 // arrayify arguments
134 var args
= _toArray(arguments
);
136 var f
= named
? (scope
||dojo
.global
)[method
] : method
;
137 // invoke with collected args
138 return f
&& f
.apply(scope
|| this, pre
.concat(args
)); // mixed
142 hitch = function(scope
, method
){
143 if(arguments
.length
> 2){
144 return lang
._hitchArgs
.apply(dojo
, arguments
); // Function
150 if(lang
.isString(method
)){
151 scope
= scope
|| dojo
.global
;
152 if(!scope
[method
]){ throw(['dojo.hitch: scope["', method
, '"] is null (scope="', scope
, '")'].join('')); }
153 return function(){ return scope
[method
].apply(scope
, arguments
|| []); }; // Function
155 return !scope
? method : function(){ return method
.apply(scope
, arguments
|| []); }; // Function
158 delegate
= (function(){
159 // boodman/crockford delegation w/ cornford optimization
161 return function(obj
, props
){
164 TMP
.prototype = null;
166 lang
._mixin(tmp
, props
);
168 return tmp
; // Object
172 efficient = function(obj
, offset
, startWith
){
173 return (startWith
||[]).concat(Array
.prototype.slice
.call(obj
, offset
||0));
179 function slow(obj
, offset
, startWith
){
180 var arr
= startWith
||[];
181 for(var x
= offset
|| 0; x
< obj
.length
; x
++){
186 return function(obj
){
187 return ((obj
.item
) ? slow
: efficient
).apply(this, arguments
);
191 partial = function(/*Function|String*/method
/*, ...*/){
193 return lang
.hitch
.apply(dojo
, arr
.concat(lang
._toArray(arguments
))); // Function
196 clone = function(/*anything*/ src
){
197 if(!src
|| typeof src
!= "object" || lang
.isFunction(src
)){
198 // null, undefined, any non-object, or function
199 return src
; // anything
201 if(src
.nodeType
&& "cloneNode" in src
){
203 return src
.cloneNode(true); // Node
205 if(src
instanceof Date
){
207 return new Date(src
.getTime()); // Date
209 if(src
instanceof RegExp
){
211 return new RegExp(src
); // RegExp
214 if(lang
.isArray(src
)){
217 for(i
= 0, l
= src
.length
; i
< l
; ++i
){
219 r
.push(clone(src
[i
]));
222 // we don't clone functions for performance reasons
223 // }else if(d.isFunction(src)){
225 // r = function(){ return src.apply(this, arguments); };
228 r
= src
.constructor ? new src
.constructor() : {};
230 return lang
._mixin(r
, src
, clone
);
234 trim
= String
.prototype.trim
?
235 function(str
){ return str
.trim(); } :
236 function(str
){ return str
.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); },
239 _pattern
= /\{([^\}]+)\}/g,
241 replace = function(tmpl
, map
, pattern
){
242 return tmpl
.replace(pattern
|| _pattern
, lang
.isFunction(map
) ?
243 map : function(_
, k
){ return getObject(k
, false, map
); });
247 _extraNames
:_extraNames
,
255 isFunction
:isFunction
,
257 isArrayLike
:isArrayLike
,
260 _hitchArgs
:_hitchArgs
,
270 1 && mixin(dojo
, lang
);
276 // Array of strings. Lists property names that must be explicitly processed during for-in interation
277 // in environments that have has("bug-for-in-skips-shadowed") true.
281 dojo._mixin = function(dest, source, copyFunc){
283 // Copies/adds all properties of source to dest; returns dest.
285 // The object to which to copy/add all properties contained in source.
287 // The object from which to draw all properties to copy into dest.
288 // copyFunc: Function?:
289 // The process used to copy/add a property in source; defaults to the Javascript assignment operator.
293 // All properties, including functions (sometimes termed "methods"), excluding any non-standard extensions
294 // found in Object.prototype, are copied/added to dest. Copying/adding each particular property is
295 // delegated to copyFunc (if any); copyFunc defaults to the Javascript assignment operator if not provided.
296 // Notice that by default, _mixin executes a so-called "shallow copy" and aggregate types are copied/added by reference.
301 dojo.mixin = function(dest, sources){
303 // Copies/adds all properties of one or more sources to dest; returns dest.
305 // The object to which to copy/add all properties contained in source. If dest is falsy, then
306 // a new object is manufactured before copying/adding properties begins.
307 // sources: Object...
308 // One of more objects from which to draw all properties to copy into dest. sources are processed
309 // left-to-right and if more than one of these objects contain the same property name, the right-most
314 // All properties, including functions (sometimes termed "methods"), excluding any non-standard extensions
315 // found in Object.prototype, are copied/added from sources to dest. sources are processed left to right.
316 // The Javascript assignment operator is used to copy/add each property; therefore, by default, mixin
317 // executes a so-called "shallow copy" and aggregate types are copied/added by reference.
319 // make a shallow copy of an object
320 // | var copy = lang.mixin({}, source);
322 // many class constructors often take an object which specifies
323 // values to be configured on the object. In this case, it is
324 // often simplest to call `lang.mixin` on the `this` object:
325 // | dojo.declare("acme.Base", null, {
326 // | constructor: function(properties){
327 // | // property configuration:
328 // | lang.mixin(this, properties);
330 // | console.log(this.quip);
333 // | quip: "I wasn't born yesterday, you know - I've seen movies.",
337 // | // create an instance of the class and configure it
338 // | var b = new acme.Base({quip: "That's what it does!" });
340 // copy in properties from multiple objects
341 // | var flattened = lang.mixin(
343 // | name: "Frylock",
347 // | name: "Carl Brutanananadilewski"
351 // | // will print "Carl Brutanananadilewski"
352 // | console.log(flattened.name);
353 // | // will print "true"
354 // | console.log(flattened.braces);
359 dojo.setObject = function(name, value, context){
361 // Set a property from a dot-separated string, such as "A.B.C"
363 // Useful for longer api chains where you have to test each object in
364 // the chain, or when you have an object reference in string format.
365 // Objects are created as needed along `path`. Returns the passed
366 // value if setting is successful or `undefined` if not.
368 // Path to a property, in the form "A.B.C".
370 // value or object to place at location given by name
372 // Optional. Object to use as root of path. Defaults to
375 // set the value of `foo.bar.baz`, regardless of whether
376 // intermediate objects already exist:
377 // | lang.setObject("foo.bar.baz", value);
379 // without `lang.setObject`, we often see code like this:
380 // | // ensure that intermediate objects are available
381 // | if(!obj["parent"]){ obj.parent = {}; }
382 // | if(!obj.parent["child"]){ obj.parent.child = {}; }
383 // | // now we can safely set the property
384 // | obj.parent.child.prop = "some value";
385 // whereas with `lang.setObject`, we can shorten that to:
386 // | lang.setObject("parent.child.prop", "some value", obj);
391 dojo.getObject = function(name, create, context){
393 // Get a property from a dot-separated string, such as "A.B.C"
395 // Useful for longer api chains where you have to test each object in
396 // the chain, or when you have an object reference in string format.
398 // Path to an property, in the form "A.B.C".
400 // Optional. Defaults to `false`. If `true`, Objects will be
401 // created at any point along the 'path' that is undefined.
403 // Optional. Object to use as root of path. Defaults to
404 // 'dojo.global'. Null may be passed.
409 dojo.exists = function(name, obj){
411 // determine if an object supports a given method
413 // useful for longer api chains where you have to test each object in
414 // the chain. Useful for object and method detection.
416 // Path to an object, in the form "A.B.C".
418 // Object to use as root of path. Defaults to
419 // 'dojo.global'. Null may be passed.
421 // | // define an object
426 // | // search the global scope
427 // | lang.exists("foo.bar"); // true
428 // | lang.exists("foo.bar.baz"); // false
430 // | // search from a particular scope
431 // | lang.exists("bar", foo); // true
432 // | lang.exists("bar.baz", foo); // false
437 dojo.isString = function(it){
439 // Return true if it is a String
446 dojo.isArray = function(it){
448 // Return true if it is an Array.
449 // Does not work on Arrays created in other windows.
456 dojo.isFunction = function(it){
458 // Return true if it is a Function
465 dojo.isObject = function(it){
467 // Returns true if it is a JavaScript object (or an Array, a Function
475 dojo.isArrayLike = function(it){
477 // similar to dojo.isArray() but more permissive
481 // If it walks like a duck and quacks like a duck, return `true`
483 // Doesn't strongly test for "arrayness". Instead, settles for "isn't
484 // a string or number and has a length property". Arguments objects
485 // and DOM collections will return true when passed to
486 // dojo.isArrayLike(), but will return false when passed to
492 dojo.isAlien = function(it){
494 // Returns true if it is a built-in function or some other kind of
495 // oddball that *should* report as a function but doesn't
500 dojo.extend = function(constructor, props){
502 // Adds all properties and methods of props to constructor's
503 // prototype, making them available to all instances created with
505 // constructor: Object
506 // Target constructor to extend.
508 // One or more objects to mix into constructor.prototype
513 dojo.hitch = function(scope, method){
515 // Returns a function that will only ever execute in the a given scope.
516 // This allows for easy use of object member functions
517 // in callbacks and other places in which the "this" keyword may
518 // otherwise not reference the expected scope.
519 // Any number of default positional arguments may be passed as parameters
521 // Each of these values will be used to "placehold" (similar to curry)
522 // for the hitched function.
524 // The scope to use when method executes. If method is a string,
525 // scope is also the object containing method.
526 // method: Function|String...
527 // A function to be hitched to scope, or the name of the method in
528 // scope to be hitched.
530 // | dojo.hitch(foo, "bar")();
531 // runs foo.bar() in the scope of foo
533 // | dojo.hitch(foo, myFunction);
534 // returns a function that runs myFunction in the scope of foo
536 // Expansion on the default positional arguments passed along from
537 // hitch. Passed args are mixed first, additional args after.
538 // | var foo = { bar: function(a, b, c){ console.log(a, b, c); } };
539 // | var fn = dojo.hitch(foo, "bar", 1, 2);
540 // | fn(3); // logs "1, 2, 3"
542 // | var foo = { bar: 2 };
543 // | dojo.hitch(foo, function(){ this.bar = 10; })();
544 // execute an anonymous function in scope of foo
549 dojo.delegate = function(obj, props){
551 // Returns a new object which "looks" to obj for properties which it
552 // does not have a value for. Optionally takes a bag of properties to
553 // seed the returned object with initially.
555 // This is a small implementaton of the Boodman/Crockford delegation
556 // pattern in JavaScript. An intermediate object constructor mediates
557 // the prototype chain for the returned object, using it to delegate
558 // down to obj for property lookup when object-local lookup fails.
559 // This can be thought of similarly to ES4's "wrap", save that it does
560 // not act on types but rather on pure objects.
562 // The object to delegate to for properties not found directly on the
563 // return object or in props.
565 // an object containing properties to assign to the returned object
567 // an Object of anonymous type
569 // | var foo = { bar: "baz" };
570 // | var thinger = dojo.delegate(foo, { thud: "xyzzy"});
571 // | thinger.bar == "baz"; // delegated to foo
572 // | foo.thud == undefined; // by definition
573 // | thinger.thud == "xyzzy"; // mixed in from props
574 // | foo.bar = "thonk";
575 // | thinger.bar == "thonk"; // still delegated to foo's bar
580 dojo.partial = function(method){
582 // similar to hitch() except that the scope object is left to be
583 // whatever the execution context eventually becomes.
584 // method: Function|String
586 // Calling dojo.partial is the functional equivalent of calling:
587 // | dojo.hitch(null, funcName, ...);
592 dojo.trim = function(str){
594 // Trims whitespace from both sides of the string
596 // String to be trimmed
598 // Returns the trimmed string
600 // This version of trim() was selected for inclusion into the base due
601 // to its compact size and relatively good performance
602 // (see [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript)
603 // Uses String.prototype.trim instead, if available.
604 // The fastest but longest version of this function is located at
605 // dojo.string.trim()
610 dojo.clone = function(src){
612 // Clones objects (including DOM nodes) and all children.
613 // Warning: do not clone cyclic structures.
615 // The object to clone
620 dojo._toArray = function(obj, offset, startWith){
622 // Converts an array-like object (i.e. arguments, DOMCollection) to an
623 // array. Returns a new Array with the elements of obj.
625 // the object to "arrayify". We expect the object to have, at a
626 // minimum, a length property which corresponds to integer-indexed
629 // the location in obj to start iterating from. Defaults to 0.
632 // An array to pack with the properties of obj. If provided,
633 // properties in obj are appended at the end of startWith and
634 // startWith is the returned array.
639 dojo.replace = function(tmpl, map, pattern){
641 // Performs parameterized substitutions on a string. Throws an
642 // exception if any parameter is unmatched.
644 // String to be used as a template.
645 // map: Object|Function
646 // If an object, it is used as a dictionary to look up substitutions.
647 // If a function, it is called for every substitution with following
648 // parameters: a whole match, a name, an offset, and the whole template
649 // string (see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/replace
650 // for more details).
652 // Optional regular expression objects that overrides the default pattern.
653 // Must be global and match one item. The default is: /\{([^\}]+)\}/g,
654 // which matches patterns like that: "{xxx}", where "xxx" is any sequence
655 // of characters, which doesn't include "}".
657 // Returns the substituted string.
659 // | // uses a dictionary for substitutions:
660 // | dojo.replace("Hello, {name.first} {name.last} AKA {nick}!",
664 // | first: "Robert",
666 // | last: "Cringely"
669 // | // returns: Hello, Robert Cringely AKA Bob!
671 // | // uses an array for substitutions:
672 // | dojo.replace("Hello, {0} {2}!",
673 // | ["Robert", "X", "Cringely"]);
674 // | // returns: Hello, Robert Cringely!
676 // | // uses a function for substitutions:
677 // | function sum(a){
679 // | dojo.forEach(a, function(x){ t += x; });
683 // | "{count} payments averaging {avg} USD per payment.",
685 // | { payments: [11, 16, 12] },
686 // | function(_, key){
688 // | case "count": return this.payments.length;
689 // | case "min": return Math.min.apply(Math, this.payments);
690 // | case "max": return Math.max.apply(Math, this.payments);
691 // | case "sum": return sum(this.payments);
692 // | case "avg": return sum(this.payments) / this.payments.length;
697 // | // prints: 3 payments averaging 13 USD per payment.
699 // | // uses an alternative PHP-like pattern for substitutions:
700 // | dojo.replace("Hello, ${0} ${2}!",
701 // | ["Robert", "X", "Cringely"], /\$\{([^\}]+)\}/g);
702 // | // returns: Hello, Robert Cringely!