2 Copyright (c) 2004-2008, The Dojo Foundation
5 Licensed under the Academic Free License version 2.1 or above OR the
6 modified BSD license. For more information on Dojo licensing, see:
8 http://dojotoolkit.org/book/dojo-book-0-9/introduction/licensing
12 This is a compiled version of Dojo, built for deployment and not for
13 development. To get an editable version, please visit:
15 http://dojotoolkit.org
17 for documentation and information on getting the source.
23 dojo, dijit, and dojox must always be the first three, and in that order.
32 /**Build will replace this comment with a scoped djConfig **/
34 //The null below can be relaced by a build-time value used instead of djConfig.scopeMap.
37 //See if new scopes need to be defined.
38 if((sMap || (typeof djConfig != "undefined" && djConfig.scopeMap)) && (typeof window != "undefined")){
39 var scopeDef = "", scopePrefix = "", scopeSuffix = "", scopeMap = {}, scopeMapRev = {};
40 sMap = sMap || djConfig.scopeMap;
41 for(var i = 0; i < sMap.length; i++){
42 //Make local variables, then global variables that use the locals.
43 var newScope = sMap[i];
44 scopeDef += "var " + newScope[0] + " = {}; " + newScope[1] + " = " + newScope[0] + ";" + newScope[1] + "._scopeName = '" + newScope[1] + "';";
45 scopePrefix += (i == 0 ? "" : ",") + newScope[0];
46 scopeSuffix += (i == 0 ? "" : ",") + newScope[1];
47 scopeMap[newScope[0]] = newScope[1];
48 scopeMapRev[newScope[1]] = newScope[0];
51 eval(scopeDef + "dojo._scopeArgs = [" + scopeSuffix + "];");
53 dojo._scopePrefixArgs = scopePrefix;
54 dojo._scopePrefix = "(function(" + scopePrefix + "){";
55 dojo._scopeSuffix = "})(" + scopeSuffix + ")";
56 dojo._scopeMap = scopeMap;
57 dojo._scopeMapRev = scopeMapRev;
62 // 'djConfig' does not exist under 'dojo.*' so that it can be set before the
63 // 'dojo' variable exists.
65 // Setting any of these variables *after* the library has loaded does
70 // Application code can set the global 'djConfig' prior to loading
71 // the library to override certain global settings for how dojo works.
74 // Defaults to `false`. If set to `true`, ensures that Dojo provides
75 // extende debugging feedback via Firebug. If Firebug is not available
76 // on your platform, setting `isDebug` to `true` will force Dojo to
77 // pull in (and display) the version of Firebug Lite which is
78 // integrated into the Dojo distribution, thereby always providing a
79 // debugging/logging console when `isDebug` is enabled. Note that
80 // Firebug's `console.*` methods are ALWAYS defined by Dojo. If
81 // `isDebug` is false and you are on a platform without Firebug, these
82 // methods will be defined as no-ops.
84 // debugAtAllCosts: Boolean
85 // Defaults to `false`. If set to `true`, this triggers an alternate
86 // mode of the package system in which dependencies are detected and
87 // only then are resources evaluated in dependency order via
88 // `<script>` tag inclusion. This may double-request resources and
89 // cause problems with scripts which expect `dojo.require()` to
90 // preform synchronously. `debugAtAllCosts` can be an invaluable
91 // debugging aid, but when using it, ensure that all code which
92 // depends on Dojo modules is wrapped in `dojo.addOnLoad()` handlers.
93 // Due to the somewhat unpredictable side-effects of using
94 // `debugAtAllCosts`, it is strongly recommended that you enable this
95 // flag as a last resort. `debugAtAllCosts` has no effect when loading
96 // resources across domains. For usage information, see the
97 // [Dojo Book](http://dojotoolkit.org/book/book-dojo/part-4-meta-dojo-making-your-dojo-code-run-faster-and-better/debugging-facilities/deb)
98 debugAtAllCosts: false,
100 // The locale to assume for loading localized resources in this page,
101 // specified according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt).
102 // Must be specified entirely in lowercase, e.g. `en-us` and `zh-cn`.
103 // See the documentation for `dojo.i18n` and `dojo.requireLocalization`
104 // for details on loading localized resources. If no locale is specified,
105 // Dojo assumes the locale of the user agent, according to `navigator.userLanguage`
106 // or `navigator.language` properties.
108 // extraLocale: Array
109 // No default value. Specifies additional locales whose
110 // resources should also be loaded alongside the default locale when
111 // calls to `dojo.requireLocalization()` are processed.
112 extraLocale: undefined,
114 // The directory in which `dojo.js` is located. Under normal
115 // conditions, Dojo auto-detects the correct location from which it
116 // was loaded. You may need to manually configure `baseUrl` in cases
117 // where you have renamed `dojo.js` or in which `<base>` tags confuse
118 // some browsers (e.g., IE 6). The variable `dojo.baseUrl` is assigned
119 // either the value of `djConfig.baseUrl` if one is provided or the
120 // auto-detected root if not. Other modules are located relative to
123 // modulePaths: Object
124 // A map of module names to paths relative to `dojo.baseUrl`. The
125 // key/value pairs correspond directly to the arguments which
126 // `dojo.registerModulePath` accepts. Specifiying
127 // `djConfig.modulePaths = { "foo": "../../bar" }` is the equivalent
128 // of calling `dojo.registerModulePath("foo", "../../bar");`. Multiple
129 // modules may be configured via `djConfig.modulePaths`.
137 // if((!this["console"])||(!console["firebug"])){
139 if(!this["console"]){
141 log: function(){} // no-op
146 "assert", "count", "debug", "dir", "dirxml", "error", "group",
147 "groupEnd", "info", "profile", "profileEnd", "time", "timeEnd",
148 "trace", "warn", "log"
155 console[tcn] = function(){
156 var a = Array.apply({}, arguments);
158 console.log(a.join(" "));
164 //TODOC: HOW TO DOC THIS?
165 // dojo is the root variable of (almost all) our public symbols -- make sure it is defined.
166 if(typeof dojo == "undefined"){
170 _scopePrefixArgs: "",
179 //Need placeholders for dijit and dojox for scoping code.
180 if(typeof dijit == "undefined"){
181 this.dijit = {_scopeName: "dijit"};
183 if(typeof dojox == "undefined"){
184 this.dojox = {_scopeName: "dojox"};
188 d._scopeArgs = [dojo, dijit, dojox];
194 // Alias for the global scope
195 // (e.g. the window object in a browser).
197 // Refer to 'dojo.global' rather than referring to window to ensure your
198 // code runs correctly in contexts other than web browsers (e.g. Rhino on a server).
203 d.config =/*===== djConfig = =====*/{
205 debugAtAllCosts: false
208 if(typeof djConfig != "undefined"){
209 for(var opt in djConfig){
210 d.config[opt] = djConfig[opt];
214 var _platforms = ["Browser", "Rhino", "Spidermonkey", "Mobile"];
216 while((t=_platforms.shift())){
221 // Override locale setting, if specified
223 // summary: the locale as defined by Dojo (read-only)
226 dojo.locale = d.config.locale;
228 var rev = "$Rev: 13707 $".match(/\d+/);
232 // version number of dojo
234 // Major version. If total version is "1.2.0beta1", will be 1
236 // Minor version. If total version is "1.2.0beta1", will be 2
238 // Patch version. If total version is "1.2.0beta1", will be 0
240 // Descriptor flag. If total version is "1.2.0beta1", will be "beta1"
242 // The SVN rev from which dojo was pulled
243 major: 1, minor: 1, patch: 1, flag: "",
244 revision: rev ? +rev[0] : 999999, //FIXME: use NaN?
245 toString: function(){
247 return major + "." + minor + "." + patch + flag + " (" + revision + ")"; // String
252 // Register with the OpenAjax hub
253 if(typeof OpenAjax != "undefined"){
254 OpenAjax.hub.registerLibrary(dojo._scopeName, "http://dojotoolkit.org", d.version.toString());
257 dojo._mixin = function(/*Object*/ obj, /*Object*/ props){
259 // Adds all properties and methods of props to obj. This addition
260 // is "prototype extension safe", so that instances of objects
261 // will not pass along prototype defaults.
264 // the "tobj" condition avoid copying properties in "props"
265 // inherited from Object.prototype. For example, if obj has a custom
266 // toString() method, don't overwrite it with the toString() method
267 // that props inherited from Object.prototype
268 if(tobj[x] === undefined || tobj[x] != props[x]){
272 // IE doesn't recognize custom toStrings in for..in
273 if(d["isIE"] && props){
274 var p = props.toString;
275 if(typeof p == "function" && p != obj.toString && p != tobj.toString &&
276 p != "\nfunction toString() {\n [native code]\n}\n"){
277 obj.toString = props.toString;
280 return obj; // Object
283 dojo.mixin = function(/*Object*/obj, /*Object...*/props){
285 // Adds all properties and methods of props to obj and returns the
286 // (now modified) obj.
288 // `dojo.mixin` can mix multiple source objects into a
289 // destionation object which is then returned. Unlike regular
290 // `for...in` iteration, `dojo.mixin` is also smart about avoiding
291 // extensions which other toolkits may unwisely add to the root
294 // The object to mix properties into. Also the return value.
296 // One or more objects whose values are successively copied into
297 // obj. If more than one of these objects contain the same value,
298 // the one specified last in the function call will "win".
300 // make a shallow copy of an object
301 // | var copy = dojo.mixin({}, source);
303 // many class constructors often take an object which specifies
304 // values to be configured on the object. In this case, it is
305 // often simplest to call `dojo.mixin` on the `this` object:
306 // | dojo.declare("acme.Base", null, {
307 // | constructor: function(properties){
308 // | // property configuration:
309 // | dojo.mixin(this, properties);
311 // | console.debug(this.quip);
314 // | quip: "I wasn't born yesterday, you know - I've seen movies.",
318 // | // create an instance of the class and configure it
319 // | var b = new acme.Base({quip: "That's what it does!" });
321 // copy in properties from multiple objects
322 // | var flattened = dojo.mixin(
324 // | name: "Frylock",
328 // | name: "Carl Brutanananadilewski"
332 // | // will print "Carl Brutanananadilewski"
333 // | console.debug(flattened.name);
334 // | // will print "true"
335 // | console.debug(flattened.braces);
336 for(var i=1, l=arguments.length; i<l; i++){
337 d._mixin(obj, arguments[i]);
339 return obj; // Object
342 dojo._getProp = function(/*Array*/parts, /*Boolean*/create, /*Object*/context){
343 var obj=context || d.global;
344 for(var i=0, p; obj && (p=parts[i]); i++){
345 if(i == 0 && this._scopeMap[p]){
346 p = this._scopeMap[p];
348 obj = (p in obj ? obj[p] : (create ? obj[p]={} : undefined));
353 dojo.setObject = function(/*String*/name, /*Object*/value, /*Object?*/context){
355 // Set a property from a dot-separated string, such as "A.B.C"
357 // Useful for longer api chains where you have to test each object in
358 // the chain, or when you have an object reference in string format.
359 // Objects are created as needed along `path`. Returns the passed
360 // value if setting is successful or `undefined` if not.
362 // Path to a property, in the form "A.B.C".
364 // Optional. Object to use as root of path. Defaults to
367 // set the value of `foo.bar.baz`, regardless of whether
368 // intermediate objects already exist:
369 // | dojo.setObject("foo.bar.baz", value);
371 // without `dojo.setObject`, we often see code like this:
372 // | // ensure that intermediate objects are available
373 // | if(!obj["parent"]){ obj.parent = {}; }
374 // | if(!obj.parent["child"]){ obj.parent.child= {}; }
375 // | // now we can safely set the property
376 // | obj.parent.child.prop = "some value";
377 // wheras with `dojo.setObject`, we can shorten that to:
378 // | dojo.setObject("parent.child.prop", "some value", obj);
379 var parts=name.split("."), p=parts.pop(), obj=d._getProp(parts, true, context);
380 return obj && p ? (obj[p]=value) : undefined; // Object
383 dojo.getObject = function(/*String*/name, /*Boolean*/create, /*Object*/context){
385 // Get a property from a dot-separated string, such as "A.B.C"
387 // Useful for longer api chains where you have to test each object in
388 // the chain, or when you have an object reference in string format.
390 // Path to an property, in the form "A.B.C".
392 // Optional. Object to use as root of path. Defaults to
393 // 'dojo.global'. Null may be passed.
395 // Optional. Defaults to `false`. If `true`, Objects will be
396 // created at any point along the 'path' that is undefined.
397 return d._getProp(name.split("."), create, context); // Object
400 dojo.exists = function(/*String*/name, /*Object?*/obj){
402 // determine if an object supports a given method
404 // useful for longer api chains where you have to test each object in
407 // Path to an object, in the form "A.B.C".
409 // Object to use as root of path. Defaults to
410 // 'dojo.global'. Null may be passed.
412 // | // define an object
417 // | // search the global scope
418 // | dojo.exists("foo.bar"); // true
419 // | dojo.exists("foo.bar.baz"); // false
421 // | // search from a particular scope
422 // | dojo.exists("bar", foo); // true
423 // | dojo.exists("bar.baz", foo); // false
424 return !!d.getObject(name, false, obj); // Boolean
428 dojo["eval"] = function(/*String*/ scriptFragment){
430 // Perform an evaluation in the global scope. Use this rather than
431 // calling 'eval()' directly.
433 // Placed in a separate function to minimize size of trapped
434 // exceptions. Calling eval() directly from some other scope may
435 // complicate tracebacks on some platforms.
437 // The result of the evaluation. Often `undefined`
441 // - JSC eval() takes an optional second argument which can be 'unsafe'.
442 // - Mozilla/SpiderMonkey eval() takes an optional second argument which is the
443 // scope object for new symbols.
445 // FIXME: investigate Joseph Smarr's technique for IE:
446 // http://josephsmarr.com/2007/01/31/fixing-eval-to-use-global-scope-in-ie/
448 // http://trac.dojotoolkit.org/ticket/744
449 return d.global.eval ? d.global.eval(scriptFragment) : eval(scriptFragment); // Object
453 dojo.deprecated = function(behaviour, extra, removal){
455 // Log a debug message to indicate that a behavior has been
458 // The API or behavior being deprecated. Usually in the form
459 // of "myApp.someFunction()".
461 // Text to append to the message. Often provides advice on a
462 // new function or facility to achieve the same goal during
463 // the deprecation period.
465 // Text to indicate when in the future the behavior will be
466 // removed. Usually a version number.
468 // | dojo.deprecated("myApp.getTemp()", "use myApp.getLocaleTemp() instead", "1.0");
471 dojo.experimental = function(moduleName, extra){
472 // summary: Marks code as experimental.
474 // This can be used to mark a function, file, or module as
475 // experimental. Experimental code is not ready to be used, and the
476 // APIs are subject to change without notice. Experimental code may be
477 // completed deleted without going through the normal deprecation
479 // moduleName: String
480 // The name of a module, or the name of a module file or a specific
483 // some additional message for the user
485 // | dojo.experimental("dojo.data.Result");
487 // | dojo.experimental("dojo.weather.toKelvin()", "PENDING approval from NOAA");
491 //Real functions declared in dojo._firebug.firebug.
492 d.deprecated = d.experimental = function(){};
498 * loader.js - A bootstrap module. Runs before the hostenv_*.js file. Contains
499 * all of the package loading methods.
511 dojo: { name: "dojo", value: "." },
512 // dojox: { name: "dojox", value: "../dojox" },
513 // dijit: { name: "dijit", value: "../dijit" },
514 doh: { name: "doh", value: "../util/doh" },
515 tests: { name: "tests", value: "tests" }
518 _moduleHasPrefix: function(/*String*/module){
519 // summary: checks to see if module has been established
520 var mp = this._modulePrefixes;
521 return !!(mp[module] && mp[module].value); // Boolean
524 _getModulePrefix: function(/*String*/module){
525 // summary: gets the prefix associated with module
526 var mp = this._modulePrefixes;
527 if(this._moduleHasPrefix(module)){
528 return mp[module].value; // String
530 return module; // String
536 // This variable is referenced by packages outside of bootstrap:
537 // FloatingPane.js and undo/browser.js
540 //Egad! Lots of test files push on this directly instead of using dojo.addOnLoad.
543 _loadNotifying: false
547 dojo._loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){
549 // Load a Javascript module given a relative path
552 // Loads and interprets the script located at relpath, which is
553 // relative to the script root directory. If the script is found but
554 // its interpretation causes a runtime exception, that exception is
555 // not caught by us, so the caller will see it. We return a true
556 // value if and only if the script is found.
559 // A relative path to a script (no leading '/', and typically ending
562 // A module whose existance to check for after loading a path. Can be
563 // used to determine success or failure of the load.
565 // a callback function to pass the result of evaluating the script
567 var uri = ((relpath.charAt(0) == '/' || relpath.match(/^\w+:/)) ? "" : this.baseUrl) + relpath;
569 return !module ? this._loadUri(uri, cb) : this._loadUriAndCheck(uri, module, cb); // Boolean
572 return false; // Boolean
576 dojo._loadUri = function(/*String*/uri, /*Function?*/cb){
578 // Loads JavaScript from a URI
580 // Reads the contents of the URI, and evaluates the contents. This is
581 // used to load modules as well as resource bundles. Returns true if
582 // it succeeded. Returns false if the URI reading failed. Throws if
583 // the evaluation throws.
584 // uri: a uri which points at the script to be loaded
586 // a callback function to process the result of evaluating the script
587 // as an expression, typically used by the resource bundle loader to
588 // load JSON-style resources
590 if(this._loadedUrls[uri]){
591 return true; // Boolean
593 var contents = this._getText(uri, true);
594 if(!contents){ return false; } // Boolean
595 this._loadedUrls[uri] = true;
596 this._loadedUrls.push(uri);
598 contents = '('+contents+')';
600 //Only do the scoping if no callback. If a callback is specified,
601 //it is most likely the i18n bundle stuff.
602 contents = this._scopePrefix + contents + this._scopeSuffix;
604 if(d.isMoz){ contents += "\r\n//@ sourceURL=" + uri; } // debugging assist for Firebug
605 var value = d["eval"](contents);
607 return true; // Boolean
610 // FIXME: probably need to add logging to this method
611 dojo._loadUriAndCheck = function(/*String*/uri, /*String*/moduleName, /*Function?*/cb){
612 // summary: calls loadUri then findModule and returns true if both succeed
615 ok = this._loadUri(uri, cb);
617 console.error("failed loading " + uri + " with error: " + e);
619 return !!(ok && this._loadedModules[moduleName]); // Boolean
622 dojo.loaded = function(){
624 // signal fired when initial environment and package loading is
625 // complete. You may use dojo.addOnLoad() or dojo.connect() to
626 // this method in order to handle initialization tasks that
627 // require the environment to be initialized. In a browser host,
628 // declarative widgets will be constructed when this function
630 this._loadNotifying = true;
631 this._postLoad = true;
632 var mll = d._loaders;
634 //Clear listeners so new ones can be added
635 //For other xdomain package loads after the initial load.
638 for(var x = 0; x < mll.length; x++){
643 console.error("dojo.addOnLoad callback failed: " + e, e); /* let other load events fire, like the parser, but report the error */
647 this._loadNotifying = false;
649 //Make sure nothing else got added to the onload queue
650 //after this first run. If something did, and we are not waiting for any
651 //more inflight resources, run again.
652 if(d._postLoad && d._inFlightCount == 0 && mll.length){
657 dojo.unloaded = function(){
659 // signal fired by impending environment destruction. You may use
660 // dojo.addOnUnload() or dojo.connect() to this method to perform
661 // page/application cleanup methods.
662 var mll = this._unloaders;
668 var onto = function(arr, obj, fn){
672 var func = (typeof fn == "string") ? obj[fn] : fn;
673 arr.push(function(){ func.call(obj); });
677 dojo.addOnLoad = function(/*Object?*/obj, /*String|Function*/functionName){
679 // Registers a function to be triggered after the DOM has finished
680 // loading and widgets declared in markup have been instantiated.
681 // Images and CSS files may or may not have finished downloading when
682 // the specified function is called. (Note that widgets' CSS and HTML
683 // code is guaranteed to be downloaded before said widgets are
686 // | dojo.addOnLoad(functionPointer);
687 // | dojo.addOnLoad(object, "functionName");
688 // | dojo.addOnLoad(object, function(){ /* ... */});
690 onto(d._loaders, obj, functionName);
692 //Added for xdomain loading. dojo.addOnLoad is used to
693 //indicate callbacks after doing some dojo.require() statements.
694 //In the xdomain case, if all the requires are loaded (after initial
695 //page load), then immediately call any listeners.
696 if(d._postLoad && d._inFlightCount == 0 && !d._loadNotifying){
701 dojo.addOnUnload = function(/*Object?*/obj, /*String|Function?*/functionName){
703 // registers a function to be triggered when the page unloads
705 // | dojo.addOnUnload(functionPointer)
706 // | dojo.addOnUnload(object, "functionName")
707 // | dojo.addOnUnload(object, function(){ /* ... */});
709 onto(d._unloaders, obj, functionName);
712 dojo._modulesLoaded = function(){
713 if(d._postLoad){ return; }
714 if(d._inFlightCount > 0){
715 console.warn("files still in flight!");
721 dojo._callLoaded = function(){
723 // The "object" check is for IE, and the other opera check fixes an
724 // issue in Opera where it could not find the body element in some
725 // widget test cases. For 0.9, maybe route all browsers through the
726 // setTimeout (need protection still for non-browser environments
727 // though). This might also help the issue with FF 2.0 and freezing
728 // issues where we try to do sync xhr while background css images are
729 // being loaded (trac #2572)? Consider for 0.9.
730 if(typeof setTimeout == "object" || (dojo.config.useXDomain && d.isOpera)){
732 setTimeout(function(){dojo.loaded();}, 0);
734 setTimeout(dojo._scopeName + ".loaded();", 0);
741 dojo._getModuleSymbols = function(/*String*/modulename){
743 // Converts a module name in dotted JS notation to an array
744 // representing the path in the source tree
745 var syms = modulename.split(".");
746 for(var i = syms.length; i>0; i--){
747 var parentModule = syms.slice(0, i).join(".");
748 if((i==1) && !this._moduleHasPrefix(parentModule)){
749 // Support default module directory (sibling of dojo) for top-level modules
750 syms[0] = "../" + syms[0];
752 var parentModulePath = this._getModulePrefix(parentModule);
753 if(parentModulePath != parentModule){
754 syms.splice(0, i, parentModulePath);
759 // console.debug(syms);
760 return syms; // Array
763 dojo._global_omit_module_check = false;
765 dojo._loadModule = dojo.require = function(/*String*/moduleName, /*Boolean?*/omitModuleCheck){
767 // loads a Javascript module from the appropriate URI
769 // module name to load. Module paths are de-referenced by dojo's
770 // internal mapping of locations to names and are disambiguated by
771 // longest prefix. See `dojo.registerModulePath()` for details on
772 // registering new modules.
774 // if `true`, omitModuleCheck skips the step of ensuring that the
775 // loaded file actually defines the symbol it is referenced by.
776 // For example if it called as `dojo._loadModule("a.b.c")` and the
777 // file located at `a/b/c.js` does not define an object `a.b.c`,
778 // and exception will be throws whereas no exception is raised
779 // when called as `dojo._loadModule("a.b.c", true)`
781 // `dojo._loadModule("A.B")` first checks to see if symbol A.B is
782 // defined. If it is, it is simply returned (nothing to do).
784 // If it is not defined, it will look for `A/B.js` in the script root
787 // `dojo._loadModule` throws an excpetion if it cannot find a file
788 // to load, or if the symbol `A.B` is not defined after loading.
790 // It returns the object `A.B`.
792 // `dojo._loadModule()` does nothing about importing symbols into
793 // the current namespace. It is presumed that the caller will
794 // take care of that. For example, to import all symbols into a
795 // local block, you might write:
797 // | with (dojo._loadModule("A.B")) {
801 // And to import just the leaf symbol to a local variable:
803 // | var B = dojo._loadModule("A.B");
805 // returns: the required namespace object
806 omitModuleCheck = this._global_omit_module_check || omitModuleCheck;
808 //Check if it is already loaded.
809 var module = this._loadedModules[moduleName];
814 // convert periods to slashes
815 var relpath = this._getModuleSymbols(moduleName).join("/") + '.js';
817 var modArg = (!omitModuleCheck) ? moduleName : null;
818 var ok = this._loadPath(relpath, modArg);
820 if(!ok && !omitModuleCheck){
821 throw new Error("Could not load '" + moduleName + "'; last tried '" + relpath + "'");
824 // check that the symbol was defined
825 // Don't bother if we're doing xdomain (asynchronous) loading.
826 if(!omitModuleCheck && !this._isXDomain){
827 // pass in false so we can give better error
828 module = this._loadedModules[moduleName];
830 throw new Error("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'");
837 dojo.provide = function(/*String*/ resourceName){
839 // Each javascript source file must have at least one
840 // `dojo.provide()` call at the top of the file, corresponding to
841 // the file name. For example, `js/dojo/foo.js` must have
842 // `dojo.provide("dojo.foo");` before any calls to
843 // `dojo.require()` are made.
845 // Each javascript source file is called a resource. When a
846 // resource is loaded by the browser, `dojo.provide()` registers
847 // that it has been loaded.
849 // For backwards compatibility reasons, in addition to registering
850 // the resource, `dojo.provide()` also ensures that the javascript
851 // object for the module exists. For example,
852 // `dojo.provide("dojox.data.FlickrStore")`, in addition to
853 // registering that `FlickrStore.js` is a resource for the
854 // `dojox.data` module, will ensure that the `dojox.data`
855 // javascript object exists, so that calls like
856 // `dojo.data.foo = function(){ ... }` don't fail.
858 // In the case of a build where multiple javascript source files
859 // are combined into one bigger file (similar to a .lib or .jar
860 // file), that file may contain multiple dojo.provide() calls, to
861 // note that it includes multiple resources.
863 //Make sure we have a string.
864 resourceName = resourceName + "";
865 return (d._loadedModules[resourceName] = d.getObject(resourceName, true)); // Object
868 //Start of old bootstrap2:
870 dojo.platformRequire = function(/*Object*/modMap){
872 // require one or more modules based on which host environment
873 // Dojo is currently operating in
875 // This method takes a "map" of arrays which one can use to
876 // optionally load dojo modules. The map is indexed by the
877 // possible dojo.name_ values, with two additional values:
878 // "default" and "common". The items in the "default" array will
879 // be loaded if none of the other items have been choosen based on
880 // dojo.name_, set by your host environment. The items in the
881 // "common" array will *always* be loaded, regardless of which
884 // | dojo.platformRequire({
886 // | "foo.sample", // simple module
888 // | ["foo.bar.baz", true] // skip object check in _loadModule
890 // | default: [ "foo.sample._base" ],
891 // | common: [ "important.module.common" ]
894 var common = modMap.common || [];
895 var result = common.concat(modMap[d._name] || modMap["default"] || []);
897 for(var x=0; x<result.length; x++){
898 var curr = result[x];
899 if(curr.constructor == Array){
900 d._loadModule.apply(d, curr);
907 dojo.requireIf = function(/*Boolean*/ condition, /*String*/ resourceName){
909 // If the condition is true then call dojo.require() for the specified
911 if(condition === true){
912 // FIXME: why do we support chained require()'s here? does the build system?
914 for(var i = 1; i < arguments.length; i++){
915 args.push(arguments[i]);
917 d.require.apply(d, args);
921 dojo.requireAfterIf = d.requireIf;
923 dojo.registerModulePath = function(/*String*/module, /*String*/prefix){
925 // maps a module name to a path
927 // An unregistered module is given the default path of ../[module],
928 // relative to Dojo root. For example, module acme is mapped to
929 // ../acme. If you want to use a different module name, use
930 // dojo.registerModulePath.
932 // If your dojo.js is located at this location in the web root:
933 // | /myapp/js/dojo/dojo/dojo.js
934 // and your modules are located at:
935 // | /myapp/js/foo/bar.js
936 // | /myapp/js/foo/baz.js
937 // | /myapp/js/foo/thud/xyzzy.js
938 // Your application can tell Dojo to locate the "foo" namespace by calling:
939 // | dojo.registerModulePath("foo", "../../foo");
940 // At which point you can then use dojo.require() to load the
941 // modules (assuming they provide() the same things which are
942 // required). The full code might be:
943 // | <script type="text/javascript"
944 // | src="/myapp/js/dojo/dojo/dojo.js"></script>
945 // | <script type="text/javascript">
946 // | dojo.registerModulePath("foo", "../../foo");
947 // | dojo.require("foo.bar");
948 // | dojo.require("foo.baz");
949 // | dojo.require("foo.thud.xyzzy");
951 d._modulePrefixes[module] = { name: module, value: prefix };
954 dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){
956 // Declares translated resources and loads them if necessary, in the
957 // same style as dojo.require. Contents of the resource bundle are
958 // typically strings, but may be any name/value pair, represented in
959 // JSON format. See also dojo.i18n.getLocalization.
961 // name of the package containing the "nls" directory in which the
964 // bundle name, i.e. the filename without the '.js' suffix
966 // the locale to load (optional) By default, the browser's user
967 // locale as defined by dojo.locale
968 // availableFlatLocales:
969 // A comma-separated list of the available, flattened locales for this
970 // bundle. This argument should only be set by the build process.
972 // Load translated resource bundles provided underneath the "nls"
973 // directory within a package. Translated resources may be located in
974 // different packages throughout the source tree. For example, a
975 // particular widget may define one or more resource bundles,
976 // structured in a program as follows, where moduleName is
977 // mycode.mywidget and bundleNames available include bundleone and
984 // | bundleone.js (the fallback translation, English in this example)
985 // | bundletwo.js (also a fallback translation)
992 // | (empty; use the fallback translation)
1003 // Each directory is named for a locale as specified by RFC 3066,
1004 // (http://www.ietf.org/rfc/rfc3066.txt), normalized in lowercase.
1005 // Note that the two bundles in the example do not define all the
1006 // same variants. For a given locale, bundles will be loaded for
1007 // that locale and all more general locales above it, including a
1008 // fallback at the root directory. For example, a declaration for
1009 // the "de-at" locale will first load `nls/de-at/bundleone.js`,
1010 // then `nls/de/bundleone.js` and finally `nls/bundleone.js`. The
1011 // data will be flattened into a single Object so that lookups
1012 // will follow this cascading pattern. An optional build step can
1013 // preload the bundles to avoid data redundancy and the multiple
1014 // network hits normally required to load these resources.
1016 d.require("dojo.i18n");
1017 d.i18n._requireLocalization.apply(d.hostenv, arguments);
1021 var ore = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$");
1022 var ire = new RegExp("^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$");
1024 dojo._Url = function(/*dojo._Url||String...*/){
1026 // Constructor to create an object representing a URL.
1027 // It is marked as private, since we might consider removing
1028 // or simplifying it.
1030 // Each argument is evaluated in order relative to the next until
1031 // a canonical uri is produced. To get an absolute Uri relative to
1032 // the current document use:
1033 // new dojo._Url(document.baseURI, url)
1037 // TODO: support for IPv6, see RFC 2732
1040 // resolve uri components relative to each other
1041 for(var i = 1; i<_a.length; i++){
1042 if(!_a[i]){ continue; }
1044 // Safari doesn't support this.constructor so we have to be explicit
1045 // FIXME: Tracked (and fixed) in Webkit bug 3537.
1046 // http://bugs.webkit.org/show_bug.cgi?id=3537
1047 var relobj = new d._Url(_a[i]+"");
1048 var uriobj = new d._Url(uri[0]+"");
1051 relobj.path == "" &&
1053 !relobj.authority &&
1056 if(relobj.fragment != n){
1057 uriobj.fragment = relobj.fragment;
1060 }else if(!relobj.scheme){
1061 relobj.scheme = uriobj.scheme;
1063 if(!relobj.authority){
1064 relobj.authority = uriobj.authority;
1066 if(relobj.path.charAt(0) != "/"){
1067 var path = uriobj.path.substring(0,
1068 uriobj.path.lastIndexOf("/") + 1) + relobj.path;
1070 var segs = path.split("/");
1071 for(var j = 0; j < segs.length; j++){
1073 // flatten "./" references
1074 if(j == segs.length - 1){
1080 }else if(j > 0 && !(j == 1 && segs[0] == "") &&
1081 segs[j] == ".." && segs[j-1] != ".."){
1082 // flatten "../" references
1083 if(j == (segs.length - 1)){
1087 segs.splice(j - 1, 2);
1092 relobj.path = segs.join("/");
1099 uri.push(relobj.scheme, ":");
1101 if(relobj.authority){
1102 uri.push("//", relobj.authority);
1104 uri.push(relobj.path);
1106 uri.push("?", relobj.query);
1108 if(relobj.fragment){
1109 uri.push("#", relobj.fragment);
1113 this.uri = uri.join("");
1115 // break the uri into its main components
1116 var r = this.uri.match(ore);
1118 this.scheme = r[2] || (r[1] ? "" : n);
1119 this.authority = r[4] || (r[3] ? "" : n);
1120 this.path = r[5]; // can never be undefined
1121 this.query = r[7] || (r[6] ? "" : n);
1122 this.fragment = r[9] || (r[8] ? "" : n);
1124 if(this.authority != n){
1125 // server based naming authority
1126 r = this.authority.match(ire);
1128 this.user = r[3] || n;
1129 this.password = r[4] || n;
1131 this.port = r[7] || n;
1135 dojo._Url.prototype.toString = function(){ return this.uri; };
1137 dojo.moduleUrl = function(/*String*/module, /*dojo._Url||String*/url){
1139 // Returns a `dojo._Url` object relative to a module.
1141 // | var pngPath = dojo.moduleUrl("acme","images/small.png");
1142 // | console.dir(pngPath); // list the object properties
1143 // | // create an image and set it's source to pngPath's value:
1144 // | var img = document.createElement("img");
1145 // | // NOTE: we assign the string representation of the url object
1146 // | img.src = pngPath.toString();
1147 // | // add our image to the document
1148 // | dojo.body().appendChild(img);
1150 // you may de-reference as far as you like down the package
1151 // hierarchy. This is sometimes handy to avoid lenghty relative
1152 // urls or for building portable sub-packages. In this example,
1153 // the `acme.widget` and `acme.util` directories may be located
1154 // under different roots (see `dojo.registerModulePath`) but the
1155 // the modules which reference them can be unaware of their
1156 // relative locations on the filesystem:
1157 // | // somewhere in a configuration block
1158 // | dojo.registerModulePath("acme.widget", "../../acme/widget");
1159 // | dojo.registerModulePath("acme.util", "../../util");
1163 // | // code in a module using acme resources
1164 // | var tmpltPath = dojo.moduleUrl("acme.widget","templates/template.html");
1165 // | var dataPath = dojo.moduleUrl("acme.util","resources/data.json");
1167 var loc = d._getModuleSymbols(module).join('/');
1168 if(!loc){ return null; }
1169 if(loc.lastIndexOf("/") != loc.length-1){
1173 //If the path is an absolute path (starts with a / or is on another
1174 //domain/xdomain) then don't add the baseUrl.
1175 var colonIndex = loc.indexOf(":");
1176 if(loc.charAt(0) != "/" && (colonIndex == -1 || colonIndex > loc.indexOf("/"))){
1177 loc = d.baseUrl + loc;
1180 return new d._Url(loc, url); // String
1187 // | if(dojo.isBrowser){ ... }
1192 // | if(dojo.isFF > 1){ ... }
1197 // | if(dojo.isIE > 6){
1204 // | if(dojo.isSafari){ ... }
1207 // | if(dojo.isSafari && (navigator.userAgent.indexOf("iPhone") < 0)){
1208 // | // we are iPhone. iPod touch reports "iPod" above
1213 // isBrowser: Boolean
1214 // True if the client is a web-browser
1217 // Greater than zero if client is FireFox. 0 otherwise. Corresponds to
1218 // major detected FireFox version (1.5, 2, 3, etc.)
1221 // Greater than zero if client is MSIE(PC). 0 otherwise. Corresponds to
1222 // major detected IE version (6, 7, 8, etc.)
1225 // Greater than zero if client is a KTHML-derived browser (Konqueror,
1226 // Safari, etc.). 0 otherwise. Corresponds to major detected version.
1228 // isMozilla: Number
1229 // Greater than zero if client is a Mozilla-based browser (Firefox,
1230 // SeaMonkey). 0 otherwise. Corresponds to major detected version.
1233 // Greater than zero if client is Opera. 0 otherwise. Corresponds to
1234 // major detected version.
1237 // Greater than zero if client is Safari or iPhone. 0 otherwise.
1242 if(typeof window != 'undefined'){
1243 dojo.isBrowser = true;
1244 dojo._name = "browser";
1247 // attempt to figure out the path to dojo if it isn't set in the config
1250 // this is a scope protection closure. We set browser versions and grab
1251 // the URL we were loaded from here.
1253 // grab the node we were loaded from
1254 if(document && document.getElementsByTagName){
1255 var scripts = document.getElementsByTagName("script");
1256 var rePkg = /dojo(\.xd)?\.js(\W|$)/i;
1257 for(var i = 0; i < scripts.length; i++){
1258 var src = scripts[i].getAttribute("src");
1259 if(!src){ continue; }
1260 var m = src.match(rePkg);
1262 // find out where we came from
1263 if(!d.config.baseUrl){
1264 d.config.baseUrl = src.substring(0, m.index);
1266 // and find out if we need to modify our behavior
1267 var cfg = scripts[i].getAttribute("djConfig");
1269 var cfgo = eval("({ "+cfg+" })");
1271 dojo.config[x] = cfgo[x];
1274 break; // "first Dojo wins"
1278 d.baseUrl = d.config.baseUrl;
1280 // fill in the rendering support information in dojo.render.*
1282 var dua = n.userAgent;
1283 var dav = n.appVersion;
1284 var tv = parseFloat(dav);
1286 d.isOpera = (dua.indexOf("Opera") >= 0) ? tv : 0;
1287 // safari detection derived from:
1288 // http://developer.apple.com/internet/safari/faq.html#anchor2
1289 // http://developer.apple.com/internet/safari/uamatrix.html
1290 var idx = Math.max(dav.indexOf("WebKit"), dav.indexOf("Safari"), 0);
1292 // try to grab the explicit Safari version first. If we don't get
1293 // one, look for 419.3+ as the indication that we're on something
1294 // "Safari 3-ish". Lastly, default to "Safari 2" handling.
1295 d.isSafari = parseFloat(dav.split("Version/")[1]) || ( ( parseFloat(dav.substr(idx+7)) >= 419.3 ) ? 3 : 2 ) || 2;
1297 d.isAIR = (dua.indexOf("AdobeAIR") >= 0) ? 1 : 0;
1298 d.isKhtml = (dav.indexOf("Konqueror") >= 0 || d.isSafari) ? tv : 0;
1299 d.isMozilla = d.isMoz = (dua.indexOf("Gecko") >= 0 && !d.isKhtml) ? tv : 0;
1300 d.isFF = d.isIE = 0;
1302 d.isFF = parseFloat(dua.split("Firefox/")[1]) || 0;
1304 if(document.all && !d.isOpera){
1305 d.isIE = parseFloat(dav.split("MSIE ")[1]) || 0;
1308 //Workaround to get local file loads of dojo to work on IE 7
1309 //by forcing to not use native xhr.
1310 if(dojo.isIE && window.location.protocol === "file:"){
1311 dojo.config.ieForceActiveXXhr=true;
1314 var cm = document.compatMode;
1315 d.isQuirks = cm == "BackCompat" || cm == "QuirksMode" || d.isIE < 6;
1317 // TODO: is the HTML LANG attribute relevant?
1318 d.locale = dojo.config.locale || (d.isIE ? n.userLanguage : n.language).toLowerCase();
1320 // These are in order of decreasing likelihood; this will change in time.
1321 d._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
1323 d._xhrObj = function(){
1325 // does the work of portably generating a new XMLHTTPRequest
1329 if(!dojo.isIE || !dojo.config.ieForceActiveXXhr){
1330 try{ http = new XMLHttpRequest(); }catch(e){}
1333 for(var i=0; i<3; ++i){
1334 var progid = d._XMLHTTP_PROGIDS[i];
1336 http = new ActiveXObject(progid);
1342 d._XMLHTTP_PROGIDS = [progid]; // so faster next time
1349 throw new Error("XMLHTTP not available: "+last_e);
1352 return http; // XMLHTTPRequest instance
1355 d._isDocumentOk = function(http){
1356 var stat = http.status || 0;
1357 return (stat >= 200 && stat < 300) || // Boolean
1358 stat == 304 || // allow any 2XX response code
1359 stat == 1223 || // get it out of the cache
1360 (!stat && (location.protocol=="file:" || location.protocol=="chrome:") ); // Internet Explorer mangled the status code
1363 //See if base tag is in use.
1364 //This is to fix http://trac.dojotoolkit.org/ticket/3973,
1365 //but really, we need to find out how to get rid of the dojo._Url reference
1366 //below and still have DOH work with the dojo.i18n test following some other
1367 //test that uses the test frame to load a document (trac #2757).
1368 //Opera still has problems, but perhaps a larger issue of base tag support
1369 //with XHR requests (hasBase is true, but the request is still made to document
1370 //path, not base path).
1371 var owloc = window.location+"";
1372 var base = document.getElementsByTagName("base");
1373 var hasBase = (base && base.length > 0);
1375 d._getText = function(/*URI*/ uri, /*Boolean*/ fail_ok){
1376 // summary: Read the contents of the specified uri and return those contents.
1378 // A relative or absolute uri. If absolute, it still must be in
1379 // the same "domain" as we are.
1381 // Default false. If fail_ok and loading fails, return null
1382 // instead of throwing.
1383 // returns: The response text. null is returned when there is a
1384 // failure and failure is okay (an exception otherwise)
1386 // alert("_getText: " + uri);
1388 // NOTE: must be declared before scope switches ie. this._xhrObj()
1389 var http = this._xhrObj();
1391 if(!hasBase && dojo._Url){
1392 uri = (new dojo._Url(owloc, uri)).toString();
1395 console.debug("_getText:", uri);
1396 console.debug(window.location+"");
1400 if(d.config.cacheBust){
1401 uri += (uri.indexOf("?") == -1 ? "?" : "&") + String(d.config.cacheBust).replace(/\W+/g,"");
1404 http.open('GET', uri, false);
1408 if(!d._isDocumentOk(http)){
1409 var err = Error("Unable to load "+uri+" status:"+ http.status);
1410 err.status = http.status;
1411 err.responseText = http.responseText;
1415 if(fail_ok){ return null; } // null
1416 // rethrow the exception
1419 return http.responseText; // String
1423 dojo._initFired = false;
1424 // BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)
1425 dojo._loadInit = function(e){
1426 dojo._initFired = true;
1427 // allow multiple calls, only first one will take effect
1428 // A bug in khtml calls events callbacks for document for event which isnt supported
1429 // for example a created contextmenu event calls DOMContentLoaded, workaround
1430 var type = (e && e.type) ? e.type.toLowerCase() : "load";
1431 if(arguments.callee.initialized || (type != "domcontentloaded" && type != "load")){ return; }
1432 arguments.callee.initialized = true;
1433 if("_khtmlTimer" in dojo){
1434 clearInterval(dojo._khtmlTimer);
1435 delete dojo._khtmlTimer;
1438 if(dojo._inFlightCount == 0){
1439 dojo._modulesLoaded();
1443 dojo._fakeLoadInit = function(){
1444 dojo._loadInit({type: "load"});
1447 if(!dojo.config.afterOnLoad){
1448 // START DOMContentLoaded
1449 // Mozilla and Opera 9 expose the event we could use
1450 if(document.addEventListener){
1452 // due to a threading issue in Firefox 2.0, we can't enable
1453 // DOMContentLoaded on that platform. For more information, see:
1454 // http://trac.dojotoolkit.org/ticket/1704
1455 if(dojo.isOpera || dojo.isFF >= 3 || (dojo.isMoz && dojo.config.enableMozDomContentLoaded === true)){
1456 document.addEventListener("DOMContentLoaded", dojo._loadInit, null);
1459 // mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already.
1460 // also used for Mozilla because of trac #1640
1461 window.addEventListener("load", dojo._loadInit, null);
1465 window.addEventListener("load", dojo._loadInit, null);
1466 }else if(/(WebKit|khtml)/i.test(navigator.userAgent)){ // sniff
1467 dojo._khtmlTimer = setInterval(function(){
1468 if(/loaded|complete/.test(document.readyState)){
1469 dojo._loadInit(); // call the onload handler
1473 // END DOMContentLoaded
1478 var _handleNodeEvent = function(/*String*/evtName, /*Function*/fp){
1480 // non-destructively adds the specified function to the node's
1482 // evtName: should be in the form "onclick" for "onclick" handlers.
1483 // Make sure you pass in the "on" part.
1484 var oldHandler = _w[evtName] || function(){};
1485 _w[evtName] = function(){
1486 fp.apply(_w, arguments);
1487 oldHandler.apply(_w, arguments);
1492 // for Internet Explorer. readyState will not be achieved on init
1493 // call, but dojo doesn't need it however, we'll include it
1494 // because we don't know if there are other functions added that
1495 // might. Note that this has changed because the build process
1496 // strips all comments -- including conditional ones.
1497 if(!dojo.config.afterOnLoad){
1498 document.write('<scr'+'ipt defer src="//:" '
1499 + 'onreadystatechange="if(this.readyState==\'complete\'){' + dojo._scopeName + '._loadInit();}">'
1504 // IE WebControl hosted in an application can fire "beforeunload" and "unload"
1505 // events when control visibility changes, causing Dojo to unload too soon. The
1506 // following code fixes the problem
1507 // Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;199155
1508 var _unloading = true;
1509 _handleNodeEvent("onbeforeunload", function(){
1510 _w.setTimeout(function(){ _unloading = false; }, 0);
1512 _handleNodeEvent("onunload", function(){
1513 if(_unloading){ dojo.unloaded(); }
1517 document.namespaces.add("v","urn:schemas-microsoft-com:vml");
1518 document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");
1521 // FIXME: dojo.unloaded requires dojo scope, so using anon function wrapper.
1522 _handleNodeEvent("onbeforeunload", function() { dojo.unloaded(); });
1528 OpenAjax.subscribe("OpenAjax", "onload", function(){
1529 if(dojo._inFlightCount == 0){
1530 dojo._modulesLoaded();
1534 OpenAjax.subscribe("OpenAjax", "onunload", function(){
1538 } //if (typeof window != 'undefined')
1540 //Register any module paths set up in djConfig. Need to do this
1541 //in the hostenvs since hostenv_browser can read djConfig from a
1542 //script tag's attribute.
1544 var mp = dojo.config["modulePaths"];
1546 for(var param in mp){
1547 dojo.registerModulePath(param, mp[param]);
1552 //Load debug code if necessary.
1553 if(dojo.config.isDebug){
1554 dojo.require("dojo._firebug.firebug");
1557 if(dojo.config.debugAtAllCosts){
1558 dojo.config.useXDomain = true;
1559 dojo.require("dojo._base._loader.loader_xd");
1560 dojo.require("dojo._base._loader.loader_debug");
1561 dojo.require("dojo.i18n");
1564 if(!dojo._hasResource["dojo._base.lang"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1565 dojo._hasResource["dojo._base.lang"] = true;
1566 dojo.provide("dojo._base.lang");
1568 // Crockford (ish) functions
1570 dojo.isString = function(/*anything*/ it){
1572 // Return true if it is a String
1573 return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean
1576 dojo.isArray = function(/*anything*/ it){
1578 // Return true if it is an Array
1579 return it && (it instanceof Array || typeof it == "array"); // Boolean
1583 dojo.isFunction = function(it){
1584 // summary: Return true if it is a Function
1590 dojo.isFunction = (function(){
1591 var _isFunction = function(/*anything*/ it){
1592 return it && (typeof it == "function" || it instanceof Function); // Boolean
1595 return dojo.isSafari ?
1596 // only slow this down w/ gratuitious casting in Safari since it's what's b0rken
1597 function(/*anything*/ it){
1598 if(typeof it == "function" && it == "[object NodeList]"){ return false; }
1599 return _isFunction(it); // Boolean
1603 dojo.isObject = function(/*anything*/ it){
1605 // Returns true if it is a JavaScript object (or an Array, a Function
1607 return it !== undefined &&
1608 (it === null || typeof it == "object" || dojo.isArray(it) || dojo.isFunction(it)); // Boolean
1611 dojo.isArrayLike = function(/*anything*/ it){
1613 // similar to dojo.isArray() but more permissive
1615 // Doesn't strongly test for "arrayness". Instead, settles for "isn't
1616 // a string or number and has a length property". Arguments objects
1617 // and DOM collections will return true when passed to
1618 // dojo.isArrayLike(), but will return false when passed to
1621 // If it walks like a duck and quicks like a duck, return `true`
1623 return it && it !== undefined &&
1624 // keep out built-in constructors (Number, String, ...) which have length
1626 !d.isString(it) && !d.isFunction(it) &&
1627 !(it.tagName && it.tagName.toLowerCase() == 'form') &&
1628 (d.isArray(it) || isFinite(it.length)); // Boolean
1631 dojo.isAlien = function(/*anything*/ it){
1633 // Returns true if it is a built-in function or some other kind of
1634 // oddball that *should* report as a function but doesn't
1635 return it && !dojo.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean
1638 dojo.extend = function(/*Object*/ constructor, /*Object...*/ props){
1640 // Adds all properties and methods of props to constructor's
1641 // prototype, making them available to all instances created with
1643 for(var i=1, l=arguments.length; i<l; i++){
1644 dojo._mixin(constructor.prototype, arguments[i]);
1646 return constructor; // Object
1649 dojo._hitchArgs = function(scope, method /*,...*/){
1650 var pre = dojo._toArray(arguments, 2);
1651 var named = dojo.isString(method);
1653 // arrayify arguments
1654 var args = dojo._toArray(arguments);
1655 // locate our method
1656 var f = named ? (scope||dojo.global)[method] : method;
1657 // invoke with collected args
1658 return f && f.apply(scope || this, pre.concat(args)); // mixed
1662 dojo.hitch = function(/*Object*/scope, /*Function|String*/method /*,...*/){
1664 // Returns a function that will only ever execute in the a given scope.
1665 // This allows for easy use of object member functions
1666 // in callbacks and other places in which the "this" keyword may
1667 // otherwise not reference the expected scope.
1668 // Any number of default positional arguments may be passed as parameters
1670 // Each of these values will be used to "placehold" (similar to curry)
1671 // for the hitched function.
1673 // The scope to use when method executes. If method is a string,
1674 // scope is also the object containing method.
1676 // A function to be hitched to scope, or the name of the method in
1677 // scope to be hitched.
1679 // | dojo.hitch(foo, "bar")();
1680 // runs foo.bar() in the scope of foo
1682 // | dojo.hitch(foo, myFunction);
1683 // returns a function that runs myFunction in the scope of foo
1684 if(arguments.length > 2){
1685 return dojo._hitchArgs.apply(dojo, arguments); // Function
1691 if(dojo.isString(method)){
1692 scope = scope || dojo.global;
1693 if(!scope[method]){ throw(['dojo.hitch: scope["', method, '"] is null (scope="', scope, '")'].join('')); }
1694 return function(){ return scope[method].apply(scope, arguments || []); }; // Function
1696 return !scope ? method : function(){ return method.apply(scope, arguments || []); }; // Function
1700 dojo.delegate = function(obj, props){
1702 // returns a new object which "looks" to obj for properties which it
1703 // does not have a value for. Optionally takes a bag of properties to
1704 // seed the returned object with initially.
1706 // This is a small implementaton of the Boodman/Crockford delegation
1707 // pattern in JavaScript. An intermediate object constructor mediates
1708 // the prototype chain for the returned object, using it to delegate
1709 // down to obj for property lookup when object-local lookup fails.
1710 // This can be thought of similarly to ES4's "wrap", save that it does
1711 // not act on types but rather on pure objects.
1713 // The object to delegate to for properties not found directly on the
1714 // return object or in props.
1716 // an object containing properties to assign to the returned object
1718 // an Object of anonymous type
1720 // | var foo = { bar: "baz" };
1721 // | var thinger = dojo.delegate(foo, { thud: "xyzzy"});
1722 // | thinger.bar == "baz"; // delegated to foo
1723 // | foo.thud == undefined; // by definition
1724 // | thinger.thud == "xyzzy"; // mixed in from props
1725 // | foo.bar = "thonk";
1726 // | thinger.bar == "thonk"; // still delegated to foo's bar
1731 dojo.delegate = dojo._delegate = function(obj, props){
1733 // boodman/crockford delegation
1735 TMP.prototype = obj;
1736 var tmp = new TMP();
1738 dojo.mixin(tmp, props);
1740 return tmp; // Object
1743 dojo.partial = function(/*Function|String*/method /*, ...*/){
1745 // similar to hitch() except that the scope object is left to be
1746 // whatever the execution context eventually becomes.
1748 // Calling dojo.partial is the functional equivalent of calling:
1749 // | dojo.hitch(null, funcName, ...);
1751 return dojo.hitch.apply(dojo, arr.concat(dojo._toArray(arguments))); // Function
1754 dojo._toArray = function(/*Object*/obj, /*Number?*/offset, /*Array?*/ startWith){
1756 // Converts an array-like object (i.e. arguments, DOMCollection) to an
1757 // array. Returns a new Array with the elements of obj.
1759 // the object to "arrayify". We expect the object to have, at a
1760 // minimum, a length property which corresponds to integer-indexed
1763 // the location in obj to start iterating from. Defaults to 0.
1766 // An array to pack with the properties of obj. If provided,
1767 // properties in obj are appended at the end of startWith and
1768 // startWith is the returned array.
1769 var arr = startWith||[];
1770 for(var x = offset || 0; x < obj.length; x++){
1773 return arr; // Array
1776 dojo.clone = function(/*anything*/ o){
1778 // Clones objects (including DOM nodes) and all children.
1779 // Warning: do not clone cyclic structures.
1781 if(dojo.isArray(o)){
1783 for(var i = 0; i < o.length; ++i){
1784 r.push(dojo.clone(o[i]));
1788 if(!dojo.isObject(o)){
1789 return o; /*anything*/
1791 if(o.nodeType && o.cloneNode){ // isNode
1792 return o.cloneNode(true); // Node
1794 if(o instanceof Date){
1795 return new Date(o.getTime()); // Date
1798 var r = new o.constructor(); // specific to dojo.declare()'d classes!
1800 if(!(i in r) || r[i] != o[i]){
1801 r[i] = dojo.clone(o[i]);
1807 dojo.trim = function(/*String*/ str){
1809 // trims whitespaces from both sides of the string
1811 // This version of trim() was selected for inclusion into the base due
1812 // to its compact size and relatively good performance (see Steven
1814 // http://blog.stevenlevithan.com/archives/faster-trim-javascript).
1815 // The fastest but longest version of this function is located at
1816 // dojo.string.trim()
1817 return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); // String
1822 if(!dojo._hasResource["dojo._base.declare"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
1823 dojo._hasResource["dojo._base.declare"] = true;
1824 dojo.provide("dojo._base.declare");
1827 // this file courtesy of the TurboAjax group, licensed under a Dojo CLA
1829 dojo.declare = function(/*String*/ className, /*Function|Function[]*/ superclass, /*Object*/ props){
1831 // Create a feature-rich constructor from compact notation
1833 // The name of the constructor (loosely, a "class")
1834 // stored in the "declaredClass" property in the created prototype
1836 // May be null, a Function, or an Array of Functions. If an array,
1837 // the first element is used as the prototypical ancestor and
1838 // any following Functions become mixin ancestors.
1840 // An object whose properties are copied to the
1841 // created prototype.
1842 // Add an instance-initialization function by making it a property
1843 // named "constructor".
1845 // Create a constructor using a compact notation for inheritance and
1846 // prototype extension.
1848 // All superclasses (including mixins) must be Functions (not simple Objects).
1850 // Mixin ancestors provide a type of multiple inheritance. Prototypes of mixin
1851 // ancestors are copied to the new class: changes to mixin prototypes will
1852 // not affect classes to which they have been mixed in.
1854 // "className" is cached in "declaredClass" property of the new class.
1857 // | dojo.declare("my.classes.bar", my.classes.foo, {
1858 // | // properties to be added to the class prototype
1860 // | // initialization function
1861 // | constructor: function(){
1862 // | this.myComplicatedObject = new ReallyComplicatedObject();
1864 // | // other functions
1865 // | someMethod: function(){
1870 // process superclass argument
1871 // var dd=dojo.declare, mixins=null;
1872 var dd = arguments.callee, mixins;
1873 if(dojo.isArray(superclass)){
1874 mixins = superclass;
1875 superclass = mixins.shift();
1877 // construct intermediate classes for mixins
1879 dojo.forEach(mixins, function(m){
1880 if(!m){ throw(className + ": mixin #" + i + " is null"); } // It's likely a required module is not loaded
1881 superclass = dd._delegate(superclass, m);
1885 var init = (props||0).constructor, ctor = dd._delegate(superclass), fn;
1886 // name methods (experimental)
1887 for(var i in props){ if(dojo.isFunction(fn = props[i]) && !0[i]){fn.nom = i;} } // 0[i] checks Object.prototype
1888 // decorate prototype
1889 dojo.extend(ctor, {declaredClass: className, _constructor: init, preamble: null}, props || 0);
1890 // special help for IE
1891 ctor.prototype.constructor = ctor;
1892 // create named reference
1893 return dojo.setObject(className, ctor); // Function
1896 dojo.mixin(dojo.declare, {
1897 _delegate: function(base, mixin){
1898 var bp = (base||0).prototype, mp = (mixin||0).prototype;
1899 // fresh constructor, fresh prototype
1900 var ctor = dojo.declare._makeCtor();
1902 dojo.mixin(ctor, {superclass: bp, mixin: mp, extend: dojo.declare._extend});
1904 if(base){ctor.prototype = dojo._delegate(bp);}
1905 // add mixin and core
1906 dojo.extend(ctor, dojo.declare._core, mp||0, {_constructor: null, preamble: null});
1907 // special help for IE
1908 ctor.prototype.constructor = ctor;
1909 // name this class for debugging
1910 ctor.prototype.declaredClass = (bp||0).declaredClass + '_' + (mp||0).declaredClass;
1913 _extend: function(props){
1914 for(var i in props){ if(dojo.isFunction(fn=props[i]) && !0[i]){fn.nom=i;} }
1915 dojo.extend(this, props);
1917 _makeCtor: function(){
1918 // we have to make a function, but don't want to close over anything
1919 return function(){ this._construct(arguments); };
1922 _construct: function(args){
1923 var c=args.callee, s=c.superclass, ct=s&&s.constructor, m=c.mixin, mct=m&&m.constructor, a=args, ii, fn;
1924 // side-effect of = used on purpose here, lint may complain, don't try this at home
1926 // FIXME: preambles for each mixin should be allowed
1928 // should we allow the preamble here NOT to modify the
1929 // default args, but instead to act on each mixin
1930 // independently of the class instance being constructed
1931 // (for impedence matching)?
1933 // allow any first argument w/ a "preamble" property to act as a
1934 // class preamble (not exclusive of the prototype preamble)
1935 if(/*dojo.isFunction*/((fn = a[0].preamble))){
1936 a = fn.apply(this, a) || a;
1939 // prototype preamble
1940 if((fn = c.prototype.preamble)){a = fn.apply(this, a) || a;}
1942 // need to provide an optional prototype-settable
1943 // "_explicitSuper" property which disables this
1944 // initialize superclass
1945 if(ct&&ct.apply){ct.apply(this, a);}
1947 if(mct&&mct.apply){mct.apply(this, a);}
1949 if((ii=c.prototype._constructor)){ii.apply(this, args);}
1950 // post construction
1951 if(this.constructor.prototype==c.prototype && (ct=this.postscript)){ ct.apply(this, args); }
1953 _findMixin: function(mixin){
1954 var c = this.constructor, p, m;
1958 if(m==mixin || (m instanceof mixin.constructor)){return p;}
1959 if(m && (m=m._findMixin(mixin))){return m;}
1960 c = p && p.constructor;
1963 _findMethod: function(name, method, ptype, has){
1964 // consciously trading readability for bytes and speed in this low-level method
1965 var p=ptype, c, m, f;
1969 // find method by name in our mixin ancestor
1970 if(m && (m=this._findMethod(name, method, m, has))){return m;}
1971 // if we found a named method that either exactly-is or exactly-is-not 'method'
1972 if((f=p[name])&&(has==(f==method))){return p;}
1976 // if we couldn't find an ancestor in our primary chain, try a mixin chain
1977 return !has && (p=this._findMixin(ptype)) && this._findMethod(name, method, p, has);
1979 inherited: function(name, args, newArgs){
1980 // optionalize name argument (experimental)
1982 if(!dojo.isString(a[0])){newArgs=args; args=name; name=args.callee.nom;}
1984 var c = args.callee, p = this.constructor.prototype, fn, mp;
1985 // if not an instance override
1986 if(this[name] != c || p[name] == c){
1987 mp = this._findMethod(name, c, p, true);
1988 if(!mp){throw(this.declaredClass + ': inherited method "' + name + '" mismatch');}
1989 p = this._findMethod(name, c, mp, false);
1992 if(!fn){throw(mp.declaredClass + ': inherited method "' + name + '" not found');}
1993 // if the function exists, invoke it in our scope
1994 return fn.apply(this, a);
2001 if(!dojo._hasResource["dojo._base.connect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2002 dojo._hasResource["dojo._base.connect"] = true;
2003 dojo.provide("dojo._base.connect");
2006 // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
2008 // low-level delegation machinery
2010 // create a dispatcher function
2011 getDispatcher: function(){
2012 // following comments pulled out-of-line to prevent cloning them
2013 // in the returned function.
2014 // - indices (i) that are really in the array of listeners (ls) will
2015 // not be in Array.prototype. This is the 'sparse array' trick
2016 // that keeps us safe from libs that take liberties with built-in
2018 // - listener is invoked with current scope (this)
2020 var ap=Array.prototype, c=arguments.callee, ls=c._listeners, t=c.target;
2021 // return value comes from original target function
2022 var r=t && t.apply(this, arguments);
2023 // invoke listeners after target function
2026 ls[i].apply(this, arguments);
2029 // return value comes from original target function
2033 // add a listener to an object
2034 add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){
2035 // Whenever 'method' is invoked, 'listener' will have the same scope.
2036 // Trying to supporting a context object for the listener led to
2038 // Non trivial to provide 'once' functionality here
2039 // because listener could be the result of a dojo.hitch call,
2040 // in which case two references to the same hitch target would not
2042 source = source || dojo.global;
2043 // The source method is either null, a dispatcher, or some other function
2044 var f = source[method];
2045 // Ensure a dispatcher
2046 if(!f||!f._listeners){
2047 var d = dojo._listener.getDispatcher();
2048 // original target function is special
2050 // dispatcher holds a list of listeners
2052 // redirect source to dispatcher
2053 f = source[method] = d;
2055 // The contract is that a handle is returned that can
2056 // identify this listener for disconnect.
2058 // The type of the handle is private. Here is it implemented as Integer.
2059 // DOM event code has this same contract but handle is Function
2060 // in non-IE browsers.
2062 // We could have separate lists of before and after listeners.
2063 return f._listeners.push(listener) ; /*Handle*/
2065 // remove a listener from an object
2066 remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){
2067 var f = (source||dojo.global)[method];
2068 // remember that handle is the index+1 (0 is not a valid handle)
2069 if(f && f._listeners && handle--){
2070 delete f._listeners[handle];
2075 // Multiple delegation for arbitrary methods.
2077 // This unit knows nothing about DOM,
2078 // but we include DOM aware
2079 // documentation and dontFix
2080 // argument here to help the autodocs.
2081 // Actual DOM aware code is in event.js.
2083 dojo.connect = function(/*Object|null*/ obj,
2085 /*Object|null*/ context,
2086 /*String|Function*/ method,
2087 /*Boolean*/ dontFix){
2089 // Create a link that calls one function when another executes.
2092 // Connects method to event, so that after event fires, method
2093 // does too. All connected functions are passed the same arguments as
2094 // the event function was initially called with. You may connect as
2095 // many methods to event as needed.
2097 // event must be a string. If obj is null, dojo.global is used.
2099 // null arguments may simply be omitted.
2101 // obj[event] can resolve to a function or undefined (null).
2102 // If obj[event] is null, it is assigned a function.
2104 // The return value is a handle that is needed to
2105 // remove this connection with dojo.disconnect.
2108 // The source object for the event function.
2109 // Defaults to dojo.global if null.
2110 // If obj is a DOM node, the connection is delegated
2111 // to the DOM event manager (unless dontFix is true).
2114 // String name of the event function in obj.
2115 // I.e. identifies a property obj[event].
2118 // The object that method will receive as "this".
2120 // If context is null and method is a function, then method
2121 // inherits the context of event.
2123 // If method is a string then context must be the source
2124 // object object for method (context[method]). If context is null,
2125 // dojo.global is used.
2128 // A function reference, or name of a function in context.
2129 // The function identified by method fires after event does.
2130 // method receives the same arguments as the event.
2131 // See context argument comments for information on method's scope.
2134 // If obj is a DOM node, set dontFix to true to prevent delegation
2135 // of this connection to the DOM event manager.
2138 // When obj.onchange(), do ui.update():
2139 // | dojo.connect(obj, "onchange", ui, "update");
2140 // | dojo.connect(obj, "onchange", ui, ui.update); // same
2143 // Using return value for disconnect:
2144 // | var link = dojo.connect(obj, "onchange", ui, "update");
2146 // | dojo.disconnect(link);
2149 // When onglobalevent executes, watcher.handler is invoked:
2150 // | dojo.connect(null, "onglobalevent", watcher, "handler");
2153 // When ob.onCustomEvent executes, customEventHandler is invoked:
2154 // | dojo.connect(ob, "onCustomEvent", null, "customEventHandler");
2155 // | dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same
2158 // When ob.onCustomEvent executes, customEventHandler is invoked
2159 // with the same scope (this):
2160 // | dojo.connect(ob, "onCustomEvent", null, customEventHandler);
2161 // | dojo.connect(ob, "onCustomEvent", customEventHandler); // same
2164 // When globalEvent executes, globalHandler is invoked
2165 // with the same scope (this):
2166 // | dojo.connect(null, "globalEvent", null, globalHandler);
2167 // | dojo.connect("globalEvent", globalHandler); // same
2169 // normalize arguments
2170 var a=arguments, args=[], i=0;
2171 // if a[0] is a String, obj was ommited
2172 args.push(dojo.isString(a[0]) ? null : a[i++], a[i++]);
2173 // if the arg-after-next is a String or Function, context was NOT omitted
2175 args.push(dojo.isString(a1)||dojo.isFunction(a1) ? a[i++] : null, a[i++]);
2176 // absorb any additional arguments
2177 for(var l=a.length; i<l; i++){ args.push(a[i]); }
2178 // do the actual work
2179 return dojo._connect.apply(this, args); /*Handle*/
2182 // used by non-browser hostenvs. always overriden by event.js
2183 dojo._connect = function(obj, event, context, method){
2184 var l=dojo._listener, h=l.add(obj, event, dojo.hitch(context, method));
2185 return [obj, event, h, l]; // Handle
2188 dojo.disconnect = function(/*Handle*/ handle){
2190 // Remove a link created by dojo.connect.
2192 // Removes the connection between event and the method referenced by handle.
2194 // the return value of the dojo.connect call that created the connection.
2195 if(handle && handle[0] !== undefined){
2196 dojo._disconnect.apply(this, handle);
2197 // let's not keep this reference
2202 dojo._disconnect = function(obj, event, handle, listener){
2203 listener.remove(obj, event, handle);
2206 // topic publish/subscribe
2210 dojo.subscribe = function(/*String*/ topic, /*Object|null*/ context, /*String|Function*/ method){
2212 // Attach a listener to a named topic. The listener function is invoked whenever the
2213 // named topic is published (see: dojo.publish).
2214 // Returns a handle which is needed to unsubscribe this listener.
2216 // Scope in which method will be invoked, or null for default scope.
2218 // The name of a function in context, or a function reference. This is the function that
2219 // is invoked when topic is published.
2221 // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };
2222 // | dojo.publish("alerts", [ "read this", "hello world" ]);
2224 // support for 2 argument invocation (omitting context) depends on hitch
2225 return [topic, dojo._listener.add(dojo._topics, topic, dojo.hitch(context, method))]; /*Handle*/
2228 dojo.unsubscribe = function(/*Handle*/ handle){
2230 // Remove a topic listener.
2232 // The handle returned from a call to subscribe.
2234 // | var alerter = dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };
2236 // | dojo.unsubscribe(alerter);
2238 dojo._listener.remove(dojo._topics, handle[0], handle[1]);
2242 dojo.publish = function(/*String*/ topic, /*Array*/ args){
2244 // Invoke all listener method subscribed to topic.
2246 // The name of the topic to publish.
2248 // An array of arguments. The arguments will be applied
2249 // to each topic subscriber (as first class parameters, via apply).
2251 // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); };
2252 // | dojo.publish("alerts", [ "read this", "hello world" ]);
2254 // Note that args is an array, which is more efficient vs variable length
2255 // argument list. Ideally, var args would be implemented via Array
2256 // throughout the APIs.
2257 var f = dojo._topics[topic];
2259 f.apply(this, args||[]);
2263 dojo.connectPublisher = function( /*String*/ topic,
2264 /*Object|null*/ obj,
2267 // Ensure that everytime obj.event() is called, a message is published
2268 // on the topic. Returns a handle which can be passed to
2269 // dojo.disconnect() to disable subsequent automatic publication on
2272 // The name of the topic to publish.
2274 // The source object for the event function. Defaults to dojo.global
2277 // The name of the event function in obj.
2278 // I.e. identifies a property obj[event].
2280 // | dojo.connectPublisher("/ajax/start", dojo, "xhrGet"};
2281 var pf = function(){ dojo.publish(topic, arguments); }
2282 return (event) ? dojo.connect(obj, event, pf) : dojo.connect(obj, pf); //Handle
2287 if(!dojo._hasResource["dojo._base.Deferred"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2288 dojo._hasResource["dojo._base.Deferred"] = true;
2289 dojo.provide("dojo._base.Deferred");
2292 dojo.Deferred = function(/*Function?*/ canceller){
2294 // Encapsulates a sequence of callbacks in response to a value that
2295 // may not yet be available. This is modeled after the Deferred class
2296 // from Twisted <http://twistedmatrix.com>.
2298 // JavaScript has no threads, and even if it did, threads are hard.
2299 // Deferreds are a way of abstracting non-blocking events, such as the
2300 // final response to an XMLHttpRequest. Deferreds create a promise to
2301 // return a response a some point in the future and an easy way to
2302 // register your interest in receiving that response.
2304 // The most important methods for Deffered users are:
2306 // * addCallback(handler)
2307 // * addErrback(handler)
2308 // * callback(result)
2309 // * errback(result)
2311 // In general, when a function returns a Deferred, users then "fill
2312 // in" the second half of the contract by registering callbacks and
2313 // error handlers. You may register as many callback and errback
2314 // handlers as you like and they will be executed in the order
2315 // registered when a result is provided. Usually this result is
2316 // provided as the result of an asynchronous operation. The code
2317 // "managing" the Deferred (the code that made the promise to provide
2318 // an answer later) will use the callback() and errback() methods to
2319 // communicate with registered listeners about the result of the
2320 // operation. At this time, all registered result handlers are called
2321 // *with the most recent result value*.
2323 // Deferred callback handlers are treated as a chain, and each item in
2324 // the chain is required to return a value that will be fed into
2325 // successive handlers. The most minimal callback may be registered
2328 // | var d = new dojo.Deferred();
2329 // | d.addCallback(function(result){ return result; });
2331 // Perhaps the most common mistake when first using Deferreds is to
2332 // forget to return a value (in most cases, the value you were
2335 // The sequence of callbacks is internally represented as a list of
2336 // 2-tuples containing the callback/errback pair. For example, the
2337 // following call sequence:
2339 // | var d = new dojo.Deferred();
2340 // | d.addCallback(myCallback);
2341 // | d.addErrback(myErrback);
2342 // | d.addBoth(myBoth);
2343 // | d.addCallbacks(myCallback, myErrback);
2345 // is translated into a Deferred with the following internal
2349 // | [myCallback, null],
2350 // | [null, myErrback],
2351 // | [myBoth, myBoth],
2352 // | [myCallback, myErrback]
2355 // The Deferred also keeps track of its current status (fired). Its
2356 // status may be one of three things:
2358 // * -1: no value yet (initial condition)
2362 // A Deferred will be in the error state if one of the following three
2363 // conditions are met:
2365 // 1. The result given to callback or errback is "instanceof" Error
2366 // 2. The previous callback or errback raised an exception while
2368 // 3. The previous callback or errback returned a value
2369 // "instanceof" Error
2371 // Otherwise, the Deferred will be in the success state. The state of
2372 // the Deferred determines the next element in the callback sequence
2375 // When a callback or errback occurs with the example deferred chain,
2376 // something equivalent to the following will happen (imagine
2377 // that exceptions are caught and returned):
2379 // | // d.callback(result) or d.errback(result)
2380 // | if(!(result instanceof Error)){
2381 // | result = myCallback(result);
2383 // | if(result instanceof Error){
2384 // | result = myErrback(result);
2386 // | result = myBoth(result);
2387 // | if(result instanceof Error){
2388 // | result = myErrback(result);
2390 // | result = myCallback(result);
2393 // The result is then stored away in case another step is added to the
2394 // callback sequence. Since the Deferred already has a value
2395 // available, any new callbacks added will be called immediately.
2397 // There are two other "advanced" details about this implementation
2400 // Callbacks are allowed to return Deferred instances themselves, so
2401 // you can build complicated sequences of events with ease.
2403 // The creator of the Deferred may specify a canceller. The canceller
2404 // is a function that will be called if Deferred.cancel is called
2405 // before the Deferred fires. You can use this to implement clean
2406 // aborting of an XMLHttpRequest, etc. Note that cancel will fire the
2407 // deferred with a CancelledError (unless your canceller returns
2408 // another kind of error), so the errbacks should be prepared to
2409 // handle that error for cancellable Deferreds.
2411 // | var deferred = new dojo.Deferred();
2412 // | setTimeout(function(){ deferred.callback({success: true}); }, 1000);
2413 // | return deferred;
2415 // Deferred objects are often used when making code asynchronous. It
2416 // may be easiest to write functions in a synchronous manner and then
2417 // split code using a deferred to trigger a response to a long-lived
2418 // operation. For example, instead of register a callback function to
2419 // denote when a rendering operation completes, the function can
2420 // simply return a deferred:
2422 // | // callback style:
2423 // | function renderLotsOfData(data, callback){
2424 // | var success = false
2426 // | for(var x in data){
2427 // | renderDataitem(data[x]);
2429 // | success = true;
2432 // | callback(success);
2436 // | // using callback style
2437 // | renderLotsOfData(someDataObj, function(success){
2438 // | // handles success or failure
2440 // | promptUserToRecover();
2443 // | // NOTE: no way to add another callback here!!
2445 // Using a Deferred doesn't simplify the sending code any, but it
2446 // provides a standard interface for callers and senders alike,
2447 // providing both with a simple way to service multiple callbacks for
2448 // an operation and freeing both sides from worrying about details
2449 // such as "did this get called already?". With Deferreds, new
2450 // callbacks can be added at any time.
2452 // | // Deferred style:
2453 // | function renderLotsOfData(data){
2454 // | var d = new dojo.Deferred();
2456 // | for(var x in data){
2457 // | renderDataitem(data[x]);
2459 // | d.callback(true);
2461 // | d.errback(new Error("rendering failed"));
2466 // | // using Deferred style
2467 // | renderLotsOfData(someDataObj).addErrback(function(){
2468 // | promptUserToRecover();
2470 // | // NOTE: addErrback and addCallback both return the Deferred
2471 // | // again, so we could chain adding callbacks or save the
2472 // | // deferred for later should we need to be notified again.
2474 // In this example, renderLotsOfData is syncrhonous and so both
2475 // versions are pretty artificial. Putting the data display on a
2476 // timeout helps show why Deferreds rock:
2478 // | // Deferred style and async func
2479 // | function renderLotsOfData(data){
2480 // | var d = new dojo.Deferred();
2481 // | setTimeout(function(){
2483 // | for(var x in data){
2484 // | renderDataitem(data[x]);
2486 // | d.callback(true);
2488 // | d.errback(new Error("rendering failed"));
2494 // | // using Deferred style
2495 // | renderLotsOfData(someDataObj).addErrback(function(){
2496 // | promptUserToRecover();
2499 // Note that the caller doesn't have to change his code at all to
2500 // handle the asynchronous case.
2503 this.id = this._nextId();
2506 this.results = [null, null];
2507 this.canceller = canceller;
2508 this.silentlyCancelled = false;
2511 dojo.extend(dojo.Deferred, {
2513 makeCalled: function(){
2515 // returns a new, empty deferred, which is already in the called
2516 // state. Calling callback() or errback() on this deferred will
2517 // yeild an error and adding new handlers to it will result in
2518 // them being called immediately.
2519 var deferred = new dojo.Deferred();
2520 deferred.callback();
2524 toString: function(){
2526 if(this.fired == -1){
2529 state = this.fired ? 'success' : 'error';
2531 return 'Deferred(' + this.id + ', ' + state + ')';
2535 _nextId: (function(){
2537 return function(){ return n++; };
2542 // Cancels a Deferred that has not yet received a value, or is
2543 // waiting on another Deferred as its value.
2545 // If a canceller is defined, the canceller is called. If the
2546 // canceller did not return an error, or there was no canceller,
2547 // then the errback chain is started.
2549 if(this.fired == -1){
2551 err = this.canceller(this);
2553 this.silentlyCancelled = true;
2555 if(this.fired == -1){
2556 if(!(err instanceof Error)){
2558 err = new Error("Deferred Cancelled");
2559 err.dojoType = "cancel";
2560 err.cancelResult = res;
2564 }else if( (this.fired == 0) &&
2565 (this.results[0] instanceof dojo.Deferred)
2567 this.results[0].cancel();
2572 _resback: function(res){
2574 // The private primitive that means either callback or errback
2575 this.fired = ((res instanceof Error) ? 1 : 0);
2576 this.results[this.fired] = res;
2581 if(this.fired != -1){
2582 if(!this.silentlyCancelled){
2583 throw new Error("already called!");
2585 this.silentlyCancelled = false;
2590 callback: function(res){
2592 // Begin the callback sequence with a non-error value.
2595 callback or errback should only be called once on a given
2602 errback: function(/*Error*/res){
2604 // Begin the callback sequence with an error result.
2606 if(!(res instanceof Error)){
2607 res = new Error(res);
2612 addBoth: function(/*Function|Object*/cb, /*String?*/cbfn){
2614 // Add the same function as both a callback and an errback as the
2615 // next element on the callback sequence.This is useful for code
2616 // that you want to guarantee to run, e.g. a finalizer.
2617 var enclosed = dojo.hitch.apply(dojo, arguments);
2618 return this.addCallbacks(enclosed, enclosed);
2621 addCallback: function(/*Function|Object*/cb, /*String?*/cbfn /*...*/){
2623 // Add a single callback to the end of the callback sequence.
2624 return this.addCallbacks(dojo.hitch.apply(dojo, arguments));
2627 addErrback: function(cb, cbfn){
2629 // Add a single callback to the end of the callback sequence.
2630 return this.addCallbacks(null, dojo.hitch.apply(dojo, arguments));
2633 addCallbacks: function(cb, eb){
2635 // Add separate callback and errback to the end of the callback
2637 this.chain.push([cb, eb])
2638 if(this.fired >= 0){
2646 // Used internally to exhaust the callback sequence when a result
2648 var chain = this.chain;
2649 var fired = this.fired;
2650 var res = this.results[fired];
2654 (chain.length > 0) &&
2658 var f = chain.shift()[fired];
2662 fired = ((res instanceof Error) ? 1 : 0);
2663 if(res instanceof dojo.Deferred){
2666 // inlined from _pause()
2669 (self.paused == 0) &&
2675 // inlined from _unpause
2685 this.results[fired] = res;
2686 if((cb)&&(this.paused)){
2687 // this is for "tail recursion" in case the dependent
2688 // deferred is already fired
2696 if(!dojo._hasResource["dojo._base.json"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2697 dojo._hasResource["dojo._base.json"] = true;
2698 dojo.provide("dojo._base.json");
2700 dojo.fromJson = function(/*String*/ json){
2702 // Parses a [JSON](http://json.org) string to return a JavaScript object.
2704 // a string literal of a JSON item, for instance:
2705 // `'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'`
2707 return eval("(" + json + ")"); // Object
2710 dojo._escapeString = function(/*String*/str){
2712 // Adds escape sequences for non-visual characters, double quote and
2713 // backslash and surrounds with double quotes to form a valid string
2715 return ('"' + str.replace(/(["\\])/g, '\\$1') + '"').
2716 replace(/[\f]/g, "\\f").replace(/[\b]/g, "\\b").replace(/[\n]/g, "\\n").
2717 replace(/[\t]/g, "\\t").replace(/[\r]/g, "\\r"); // string
2720 dojo.toJsonIndentStr = "\t";
2721 dojo.toJson = function(/*Object*/ it, /*Boolean?*/ prettyPrint, /*String?*/ _indentStr){
2723 // Returns a [JSON](http://json.org) serialization of an object.
2726 // Returns a [JSON](http://json.org) serialization of an object.
2727 // Note that this doesn't check for infinite recursion, so don't do that!
2730 // an object to be serialized. Objects may define their own
2731 // serialization via a special "__json__" or "json" function
2732 // property. If a specialized serializer has been defined, it will
2733 // be used as a fallback.
2736 // if true, we indent objects and arrays to make the output prettier.
2737 // The variable dojo.toJsonIndentStr is used as the indent string
2738 // -- to use something other than the default (tab),
2739 // change that variable before calling dojo.toJson().
2742 // private variable for recursive calls when pretty printing, do not use.
2744 if(it === undefined){
2747 var objtype = typeof it;
2748 if(objtype == "number" || objtype == "boolean"){
2754 if(dojo.isString(it)){
2755 return dojo._escapeString(it);
2757 if(it.nodeType && it.cloneNode){ // isNode
2758 return ""; // FIXME: would something like outerHTML be better here?
2761 var recurse = arguments.callee;
2762 // short-circuit for objects that support "json" serialization
2763 // if they return "self" then just pass-through...
2765 _indentStr = _indentStr || "";
2766 var nextIndent = prettyPrint ? _indentStr + dojo.toJsonIndentStr : "";
2767 if(typeof it.__json__ == "function"){
2768 newObj = it.__json__();
2770 return recurse(newObj, prettyPrint, nextIndent);
2773 if(typeof it.json == "function"){
2776 return recurse(newObj, prettyPrint, nextIndent);
2780 var sep = prettyPrint ? " " : "";
2781 var newLine = prettyPrint ? "\n" : "";
2784 if(dojo.isArray(it)){
2785 var res = dojo.map(it, function(obj){
2786 var val = recurse(obj, prettyPrint, nextIndent);
2787 if(typeof val != "string"){
2790 return newLine + nextIndent + val;
2792 return "[" + res.join("," + sep) + newLine + _indentStr + "]";
2795 // look in the registry
2798 newObj = dojo.json.jsonRegistry.match(it);
2799 return recurse(newObj, prettyPrint, nextIndent);
2801 // console.debug(e);
2803 // it's a function with no adapter, skip it
2805 if(objtype == "function"){
2806 return null; // null
2808 // generic object code path
2812 if(typeof key == "number"){
2813 keyStr = '"' + key + '"';
2814 }else if(typeof key == "string"){
2815 keyStr = dojo._escapeString(key);
2817 // skip non-string or number keys
2820 val = recurse(it[key], prettyPrint, nextIndent);
2821 if(typeof val != "string"){
2822 // skip non-serializable values
2825 // FIXME: use += on Moz!!
2826 // MOW NOTE: using += is a pain because you have to account for the dangling comma...
2827 output.push(newLine + nextIndent + keyStr + ":" + sep + val);
2829 return "{" + output.join("," + sep) + newLine + _indentStr + "}"; // String
2834 if(!dojo._hasResource["dojo._base.array"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2835 dojo._hasResource["dojo._base.array"] = true;
2837 dojo.provide("dojo._base.array");
2840 var _getParts = function(arr, obj, cb){
2842 dojo.isString(arr) ? arr.split("") : arr,
2844 // FIXME: cache the anonymous functions we create here?
2845 dojo.isString(cb) ? new Function("item", "index", "array", cb) : cb
2850 indexOf: function( /*Array*/ array,
2852 /*Integer?*/ fromIndex,
2853 /*Boolean?*/ findLast){
2855 // locates the first index of the provided value in the
2856 // passed array. If the value is not found, -1 is returned.
2858 // For details on this method, see:
2859 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf>
2861 var step = 1, end = array.length || 0, i = 0;
2866 if(fromIndex != undefined){ i = fromIndex; }
2867 if((findLast && i > end) || i < end){
2868 for(; i != end; i += step){
2869 if(array[i] == value){ return i; }
2872 return -1; // Number
2875 lastIndexOf: function(/*Array*/array, /*Object*/value, /*Integer?*/fromIndex){
2877 // locates the last index of the provided value in the passed array.
2878 // If the value is not found, -1 is returned.
2880 // For details on this method, see:
2881 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf>
2882 return dojo.indexOf(array, value, fromIndex, true); // Number
2885 forEach: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
2887 // for every item in arr, callback is invoked. Return values are ignored.
2888 // arr: the array to iterate on. If a string, operates on individual characters.
2889 // callback: a function is invoked with three arguments: item, index, and array
2890 // thisObject: may be used to scope the call to callback
2892 // This function corresponds to the JavaScript 1.6 Array.forEach() method.
2893 // In environments that support JavaScript 1.6, this function is a passthrough to the built-in method.
2894 // For more details, see:
2895 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach>
2897 // match the behavior of the built-in forEach WRT empty arrs
2898 if(!arr || !arr.length){ return; }
2900 // FIXME: there are several ways of handilng thisObject. Is
2901 // dojo.global always the default context?
2902 var _p = _getParts(arr, thisObject, callback); arr = _p[0];
2903 for(var i=0,l=_p[0].length; i<l; i++){
2904 _p[2].call(_p[1], arr[i], i, arr);
2908 _everyOrSome: function(/*Boolean*/every, /*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
2909 var _p = _getParts(arr, thisObject, callback); arr = _p[0];
2910 for(var i = 0, l = arr.length; i < l; i++){
2911 var result = !!_p[2].call(_p[1], arr[i], i, arr);
2913 return result; // Boolean
2916 return every; // Boolean
2919 every: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
2921 // Determines whether or not every item in arr satisfies the
2922 // condition implemented by callback.
2923 // arr: the array to iterate on. If a string, operates on individual characters.
2924 // callback: a function is invoked with three arguments: item, index, and array and returns true
2925 // if the condition is met.
2926 // thisObject: may be used to scope the call to callback
2928 // This function corresponds to the JavaScript 1.6 Array.every() method.
2929 // In environments that support JavaScript 1.6, this function is a passthrough to the built-in method.
2930 // For more details, see:
2931 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every>
2933 // | dojo.every([1, 2, 3, 4], function(item){ return item>1; });
2936 // | dojo.every([1, 2, 3, 4], function(item){ return item>0; });
2938 return this._everyOrSome(true, arr, callback, thisObject); // Boolean
2941 some: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){
2943 // Determines whether or not any item in arr satisfies the
2944 // condition implemented by callback.
2945 // arr: the array to iterate on. If a string, operates on individual characters.
2946 // callback: a function is invoked with three arguments: item, index, and array and returns true
2947 // if the condition is met.
2948 // thisObject: may be used to scope the call to callback
2950 // This function corresponds to the JavaScript 1.6 Array.some() method.
2951 // In environments that support JavaScript 1.6, this function is a passthrough to the built-in method.
2952 // For more details, see:
2953 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some>
2955 // | dojo.some([1, 2, 3, 4], function(item){ return item>1; });
2958 // | dojo.some([1, 2, 3, 4], function(item){ return item<1; });
2960 return this._everyOrSome(false, arr, callback, thisObject); // Boolean
2963 map: function(/*Array|String*/arr, /*Function|String*/callback, /*Function?*/thisObject){
2965 // applies callback to each element of arr and returns
2966 // an Array with the results
2967 // arr: the array to iterate on. If a string, operates on individual characters.
2968 // callback: a function is invoked with three arguments: item, index, and array and returns a value
2969 // thisObject: may be used to scope the call to callback
2971 // This function corresponds to the JavaScript 1.6 Array.map() method.
2972 // In environments that support JavaScript 1.6, this function is a passthrough to the built-in method.
2973 // For more details, see:
2974 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:map>
2976 // | dojo.map([1, 2, 3, 4], function(item){ return item+1 });
2977 // returns [2, 3, 4, 5]
2978 var _p = _getParts(arr, thisObject, callback); arr = _p[0];
2979 var outArr = (arguments[3] ? (new arguments[3]()) : []);
2980 for(var i=0;i<arr.length;++i){
2981 outArr.push(_p[2].call(_p[1], arr[i], i, arr));
2983 return outArr; // Array
2986 filter: function(/*Array*/arr, /*Function|String*/callback, /*Object?*/thisObject){
2988 // Returns a new Array with those items from arr that match the
2989 // condition implemented by callback.
2990 // arr: the array to iterate on. If a string, operates on individual characters.
2991 // callback: a function is invoked with three arguments: item, index, and array and returns true
2992 // if the condition is met.
2993 // thisObject: may be used to scope the call to callback
2995 // This function corresponds to the JavaScript 1.6 Array.filter() method.
2996 // In environments that support JavaScript 1.6, this function is a passthrough to the built-in method.
2997 // For more details, see:
2998 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:filter>
3000 // | dojo.filter([1, 2, 3, 4], function(item){ return item>1; });
3001 // returns [2, 3, 4]
3003 var _p = _getParts(arr, thisObject, callback); arr = _p[0];
3005 for(var i = 0; i < arr.length; i++){
3006 if(_p[2].call(_p[1], arr[i], i, arr)){
3007 outArr.push(arr[i]);
3010 return outArr; // Array
3017 if(!dojo._hasResource["dojo._base.Color"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3018 dojo._hasResource["dojo._base.Color"] = true;
3019 dojo.provide("dojo._base.Color");
3023 dojo.Color = function(/*Array|String|Object*/ color){
3025 // takes a named string, hex string, array of rgb or rgba values,
3026 // an object with r, g, b, and a properties, or another dojo.Color object
3027 if(color){ this.setColor(color); }
3030 // FIXME: there's got to be a more space-efficient way to encode or discover these!! Use hex?
3031 dojo.Color.named = {
3033 silver: [192,192,192],
3034 gray: [128,128,128],
3035 white: [255,255,255],
3038 purple: [128,0,128],
3039 fuchsia: [255,0,255],
3043 yellow: [255,255,0],
3051 dojo.extend(dojo.Color, {
3052 r: 255, g: 255, b: 255, a: 1,
3053 _set: function(r, g, b, a){
3054 var t = this; t.r = r; t.g = g; t.b = b; t.a = a;
3056 setColor: function(/*Array|String|Object*/ color){
3058 // takes a named string, hex string, array of rgb or rgba values,
3059 // an object with r, g, b, and a properties, or another dojo.Color object
3061 if(d.isString(color)){
3062 d.colorFromString(color, this);
3063 }else if(d.isArray(color)){
3064 d.colorFromArray(color, this);
3066 this._set(color.r, color.g, color.b, color.a);
3067 if(!(color instanceof d.Color)){ this.sanitize(); }
3069 return this; // dojo.Color
3071 sanitize: function(){
3073 // makes sure that the object has correct attributes
3075 // the default implementation does nothing, include dojo.colors to
3076 // augment it to real checks
3077 return this; // dojo.Color
3080 // summary: returns 3 component array of rgb values
3082 return [t.r, t.g, t.b]; // Array
3085 // summary: returns a 4 component array of rgba values
3087 return [t.r, t.g, t.b, t.a]; // Array
3090 // summary: returns a css color string in hexadecimal representation
3091 var arr = dojo.map(["r", "g", "b"], function(x){
3092 var s = this[x].toString(16);
3093 return s.length < 2 ? "0" + s : s;
3095 return "#" + arr.join(""); // String
3097 toCss: function(/*Boolean?*/ includeAlpha){
3098 // summary: returns a css color string in rgb(a) representation
3099 var t = this, rgb = t.r + ", " + t.g + ", " + t.b;
3100 return (includeAlpha ? "rgba(" + rgb + ", " + t.a : "rgb(" + rgb) + ")"; // String
3102 toString: function(){
3103 // summary: returns a visual representation of the color
3104 return this.toCss(true); // String
3108 dojo.blendColors = function(
3109 /*dojo.Color*/ start,
3115 // blend colors end and start with weight from 0 to 1, 0.5 being a 50/50 blend,
3116 // can reuse a previously allocated dojo.Color object for the result
3117 var d = dojo, t = obj || new dojo.Color();
3118 d.forEach(["r", "g", "b", "a"], function(x){
3119 t[x] = start[x] + (end[x] - start[x]) * weight;
3120 if(x != "a"){ t[x] = Math.round(t[x]); }
3122 return t.sanitize(); // dojo.Color
3125 dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){
3126 // summary: get rgb(a) array from css-style color declarations
3127 var m = color.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/);
3128 return m && dojo.colorFromArray(m[1].split(/\s*,\s*/), obj); // dojo.Color
3131 dojo.colorFromHex = function(/*String*/ color, /*dojo.Color?*/ obj){
3132 // summary: converts a hex string with a '#' prefix to a color object.
3133 // Supports 12-bit #rgb shorthand.
3134 var d = dojo, t = obj || new d.Color(),
3135 bits = (color.length == 4) ? 4 : 8,
3136 mask = (1 << bits) - 1;
3137 color = Number("0x" + color.substr(1));
3139 return null; // dojo.Color
3141 d.forEach(["b", "g", "r"], function(x){
3142 var c = color & mask;
3144 t[x] = bits == 4 ? 17 * c : c;
3147 return t; // dojo.Color
3150 dojo.colorFromArray = function(/*Array*/ a, /*dojo.Color?*/ obj){
3151 // summary: builds a color from 1, 2, 3, or 4 element array
3152 var t = obj || new dojo.Color();
3153 t._set(Number(a[0]), Number(a[1]), Number(a[2]), Number(a[3]));
3154 if(isNaN(t.a)){ t.a = 1; }
3155 return t.sanitize(); // dojo.Color
3158 dojo.colorFromString = function(/*String*/ str, /*dojo.Color?*/ obj){
3160 // parses str for a color value.
3162 // Acceptable input values for str may include arrays of any form
3163 // accepted by dojo.colorFromArray, hex strings such as "#aaaaaa", or
3164 // rgb or rgba strings such as "rgb(133, 200, 16)" or "rgba(10, 10,
3167 // a dojo.Color object. If obj is passed, it will be the return value.
3168 var a = dojo.Color.named[str];
3169 return a && dojo.colorFromArray(a, obj) || dojo.colorFromRgb(str, obj) || dojo.colorFromHex(str, obj);
3174 if(!dojo._hasResource["dojo._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3175 dojo._hasResource["dojo._base"] = true;
3176 dojo.provide("dojo._base");
3188 if(!dojo._hasResource["dojo._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3189 dojo._hasResource["dojo._base.window"] = true;
3190 dojo.provide("dojo._base.window");
3192 dojo._gearsObject = function(){
3194 // factory method to get a Google Gears plugin instance to
3195 // expose in the browser runtime environment, if present
3199 var gearsObj = dojo.getObject("google.gears");
3200 if(gearsObj){ return gearsObj; } // already defined elsewhere
3202 if(typeof GearsFactory != "undefined"){ // Firefox
3203 factory = new GearsFactory();
3208 factory = new ActiveXObject("Gears.Factory");
3210 // ok to squelch; there's no gears factory. move on.
3212 }else if(navigator.mimeTypes["application/x-googlegears"]){
3214 factory = document.createElement("object");
3215 factory.setAttribute("type", "application/x-googlegears");
3216 factory.setAttribute("width", 0);
3217 factory.setAttribute("height", 0);
3218 factory.style.display = "none";
3219 document.documentElement.appendChild(factory);
3224 if(!factory){ return null; }
3226 // define the global objects now; don't overwrite them though if they
3227 // were somehow set internally by the Gears plugin, which is on their
3228 // dev roadmap for the future
3229 dojo.setObject("google.gears.factory", factory);
3230 return dojo.getObject("google.gears");
3235 // summary: True if client is using Google Gears
3238 // see if we have Google Gears installed, and if
3239 // so, make it available in the runtime environment
3240 // and in the Google standard 'google.gears' global object
3241 dojo.isGears = (!!dojo._gearsObject())||0;
3246 // Alias for the current document. 'dojo.doc' can be modified
3247 // for temporary context shifting. Also see dojo.withDoc().
3249 // Refer to dojo.doc rather
3250 // than referring to 'window.document' to ensure your code runs
3251 // correctly in managed contexts.
3253 // | n.appendChild(dojo.doc.createElement('div'));
3256 dojo.doc = window["document"] || null;
3258 dojo.body = function(){
3260 // Return the body element of the document
3261 // return the body object associated with dojo.doc
3263 // | dojo.body().appendChild(dojo.doc.createElement('div'));
3265 // Note: document.body is not defined for a strict xhtml document
3266 // Would like to memoize this, but dojo.doc can change vi dojo.withDoc().
3267 return dojo.doc.body || dojo.doc.getElementsByTagName("body")[0]; // Node
3270 dojo.setContext = function(/*Object*/globalObject, /*DocumentElement*/globalDocument){
3272 // changes the behavior of many core Dojo functions that deal with
3273 // namespace and DOM lookup, changing them to work in a new global
3274 // context (e.g., an iframe). The varibles dojo.global and dojo.doc
3275 // are modified as a result of calling this function and the result of
3276 // `dojo.body()` likewise differs.
3277 dojo.global = globalObject;
3278 dojo.doc = globalDocument;
3281 dojo._fireCallback = function(callback, context, cbArguments){
3282 if(context && dojo.isString(callback)){
3283 callback = context[callback];
3285 return callback.apply(context, cbArguments || [ ]);
3288 dojo.withGlobal = function( /*Object*/globalObject,
3289 /*Function*/callback,
3290 /*Object?*/thisObject,
3291 /*Array?*/cbArguments){
3293 // Call callback with globalObject as dojo.global and
3294 // globalObject.document as dojo.doc. If provided, globalObject
3295 // will be executed in the context of object thisObject
3297 // When callback() returns or throws an error, the dojo.global
3298 // and dojo.doc will be restored to its previous state.
3300 var oldGlob = dojo.global;
3301 var oldDoc = dojo.doc;
3303 dojo.setContext(globalObject, globalObject.document);
3304 rval = dojo._fireCallback(callback, thisObject, cbArguments);
3306 dojo.setContext(oldGlob, oldDoc);
3311 dojo.withDoc = function( /*Object*/documentObject,
3312 /*Function*/callback,
3313 /*Object?*/thisObject,
3314 /*Array?*/cbArguments){
3316 // Call callback with documentObject as dojo.doc. If provided,
3317 // callback will be executed in the context of object thisObject
3319 // When callback() returns or throws an error, the dojo.doc will
3320 // be restored to its previous state.
3322 var oldDoc = dojo.doc;
3324 dojo.doc = documentObject;
3325 rval = dojo._fireCallback(callback, thisObject, cbArguments);
3334 if(!dojo._hasResource["dojo._base.event"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3335 dojo._hasResource["dojo._base.event"] = true;
3336 dojo.provide("dojo._base.event");
3339 // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
3342 // DOM event listener machinery
3343 var del = (dojo._event_listener = {
3344 add: function(/*DOMNode*/node, /*String*/name, /*Function*/fp){
3346 name = del._normalizeEventName(name);
3347 fp = del._fixCallback(name, fp);
3349 if(!dojo.isIE && (name == "mouseenter" || name == "mouseleave")){
3352 name = (name == "mouseenter") ? "mouseover" : "mouseout";
3355 if(!dojo.isDescendant(e.relatedTarget, node)){
3356 // e.type = oname; // FIXME: doesn't take? SJM: event.type is generally immutable.
3357 return ofp.call(this, e);
3361 node.addEventListener(name, fp, false);
3362 return fp; /*Handle*/
3364 remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){
3366 // clobbers the listener from the node
3368 // DOM node to attach the event to
3370 // the name of the handler to remove the function from
3372 // the handle returned from add
3374 node.removeEventListener(del._normalizeEventName(event), handle, false);
3377 _normalizeEventName: function(/*String*/name){
3378 // Generally, name should be lower case, unless it is special
3379 // somehow (e.g. a Mozilla DOM event).
3381 return name.slice(0,2) =="on" ? name.slice(2) : name;
3383 _fixCallback: function(/*String*/name, fp){
3384 // By default, we only invoke _fixEvent for 'keypress'
3385 // If code is added to _fixEvent for other events, we have
3386 // to revisit this optimization.
3387 // This also applies to _fixEvent overrides for Safari and Opera
3389 return name != "keypress" ? fp : function(e){ return fp.call(this, del._fixEvent(e, this)); };
3391 _fixEvent: function(evt, sender){
3392 // _fixCallback only attaches us to keypress.
3393 // Switch on evt.type anyway because we might
3394 // be called directly from dojo.fixEvent.
3397 del._setKeyChar(evt);
3402 _setKeyChar: function(evt){
3403 evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : '';
3409 dojo.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){
3411 // normalizes properties on the event object including event
3412 // bubbling methods, keystroke normalization, and x/y positions
3414 // native event object
3416 // node to treat as "currentTarget"
3417 return del._fixEvent(evt, sender);
3420 dojo.stopEvent = function(/*Event*/evt){
3422 // prevents propagation and clobbers the default action of the
3425 // The event object. If omitted, window.event is used on IE.
3426 evt.preventDefault();
3427 evt.stopPropagation();
3428 // NOTE: below, this method is overridden for IE
3431 // the default listener to use on dontFix nodes, overriden for IE
3432 var node_listener = dojo._listener;
3434 // Unify connect and event listeners
3435 dojo._connect = function(obj, event, context, method, dontFix){
3436 // FIXME: need a more strict test
3437 var isNode = obj && (obj.nodeType||obj.attachEvent||obj.addEventListener);
3438 // choose one of three listener options: raw (connect.js), DOM event on a Node, custom event on a Node
3439 // we need the third option to provide leak prevention on broken browsers (IE)
3440 var lid = !isNode ? 0 : (!dontFix ? 1 : 2), l = [dojo._listener, del, node_listener][lid];
3441 // create a listener
3442 var h = l.add(obj, event, dojo.hitch(context, method));
3443 // formerly, the disconnect package contained "l" directly, but if client code
3444 // leaks the disconnect package (by connecting it to a node), referencing "l"
3445 // compounds the problem.
3446 // instead we return a listener id, which requires custom _disconnect below.
3447 // return disconnect package
3448 return [ obj, event, h, lid ];
3451 dojo._disconnect = function(obj, event, handle, listener){
3452 ([dojo._listener, del, node_listener][listener]).remove(obj, event, handle);
3457 // Public: client code should test
3458 // keyCode against these named constants, as the
3459 // actual codes can vary by browser.
3461 // summary: definitions for common key values
3497 NUMPAD_MULTIPLY: 106,
3522 // IE event normalization
3524 var _trySetKeyCode = function(e, code){
3526 // squelch errors when keyCode is read-only
3527 // (e.g. if keyCode is ctrl or shift)
3528 return (e.keyCode = code);
3534 // by default, use the standard listener
3535 var iel = dojo._listener;
3536 // dispatcher tracking property
3537 if(!dojo.config._allow_leaks){
3538 // custom listener that handles leak protection for DOM events
3539 node_listener = iel = dojo._ie_listener = {
3540 // support handler indirection: event handler functions are
3541 // referenced here. Event dispatchers hold only indices.
3543 // add a listener to an object
3544 add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){
3545 source = source || dojo.global;
3546 var f = source[method];
3547 if(!f||!f._listeners){
3548 var d = dojo._getIeDispatcher();
3549 // original target function is special
3550 d.target = f && (ieh.push(f) - 1);
3551 // dispatcher holds a list of indices into handlers table
3553 // redirect source to dispatcher
3554 f = source[method] = d;
3556 return f._listeners.push(ieh.push(listener) - 1) ; /*Handle*/
3558 // remove a listener from an object
3559 remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){
3560 var f = (source||dojo.global)[method], l = f && f._listeners;
3561 if(f && l && handle--){
3562 delete ieh[l[handle]];
3568 var ieh = iel.handlers;
3572 add: function(/*DOMNode*/node, /*String*/event, /*Function*/fp){
3573 if(!node){return;} // undefined
3574 event = del._normalizeEventName(event);
3575 if(event=="onkeypress"){
3576 // we need to listen to onkeydown to synthesize
3577 // keypress events that otherwise won't fire
3579 var kd = node.onkeydown;
3580 if(!kd || !kd._listeners || !kd._stealthKeydownHandle){
3581 var h = del.add(node, "onkeydown", del._stealthKeyDown);
3582 kd = node.onkeydown;
3583 kd._stealthKeydownHandle = h;
3584 kd._stealthKeydownRefs = 1;
3586 kd._stealthKeydownRefs++;
3589 return iel.add(node, event, del._fixCallback(fp));
3591 remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){
3592 event = del._normalizeEventName(event);
3593 iel.remove(node, event, handle);
3594 if(event=="onkeypress"){
3595 var kd = node.onkeydown;
3596 if(--kd._stealthKeydownRefs <= 0){
3597 iel.remove(node, "onkeydown", kd._stealthKeydownHandle);
3598 delete kd._stealthKeydownHandle;
3602 _normalizeEventName: function(/*String*/eventName){
3603 // Generally, eventName should be lower case, unless it is
3604 // special somehow (e.g. a Mozilla event)
3606 return eventName.slice(0,2) != "on" ? "on" + eventName : eventName;
3609 _fixEvent: function(/*Event*/evt, /*DOMNode*/sender){
3611 // normalizes properties on the event object including event
3612 // bubbling methods, keystroke normalization, and x/y positions
3613 // evt: native event object
3614 // sender: node to treat as "currentTarget"
3616 var w = sender && (sender.ownerDocument || sender.document || sender).parentWindow || window;
3619 if(!evt){return(evt);}
3620 evt.target = evt.srcElement;
3621 evt.currentTarget = (sender || evt.srcElement);
3622 evt.layerX = evt.offsetX;
3623 evt.layerY = evt.offsetY;
3624 // FIXME: scroll position query is duped from dojo.html to
3625 // avoid dependency on that entire module. Now that HTML is in
3626 // Base, we should convert back to something similar there.
3627 var se = evt.srcElement, doc = (se && se.ownerDocument) || document;
3628 // DO NOT replace the following to use dojo.body(), in IE, document.documentElement should be used
3629 // here rather than document.body
3630 var docBody = ((dojo.isIE < 6) || (doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement;
3631 var offset = dojo._getIeDocumentElementOffset();
3632 evt.pageX = evt.clientX + dojo._fixIeBiDiScrollLeft(docBody.scrollLeft || 0) - offset.x;
3633 evt.pageY = evt.clientY + (docBody.scrollTop || 0) - offset.y;
3634 if(evt.type == "mouseover"){
3635 evt.relatedTarget = evt.fromElement;
3637 if(evt.type == "mouseout"){
3638 evt.relatedTarget = evt.toElement;
3640 evt.stopPropagation = del._stopPropagation;
3641 evt.preventDefault = del._preventDefault;
3642 return del._fixKeys(evt);
3644 _fixKeys: function(evt){
3647 var c = ("charCode" in evt ? evt.charCode : evt.keyCode);
3649 // CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla
3652 }else if(c==13||c==27){
3653 c=0; // Mozilla considers ENTER and ESC non-printable
3655 c=99; // Mozilla maps CTRL-BREAK to CTRL-c
3657 // Mozilla sets keyCode to 0 when there is a charCode
3658 // but that stops the event on IE.
3660 del._setKeyChar(evt);
3665 // some ctrl-key combinations (mostly w/punctuation) do not emit a char code in IE
3666 // we map those virtual key codes to ascii here
3667 // not valid for all (non-US) keyboards, so maybe we shouldn't bother
3683 _stealthKeyDown: function(evt){
3684 // IE doesn't fire keypress for most non-printable characters.
3685 // other browsers do, we simulate it here.
3686 var kp = evt.currentTarget.onkeypress;
3687 // only works if kp exists and is a dispatcher
3688 if(!kp || !kp._listeners){ return; }
3689 // munge key/charCode
3691 // These are Windows Virtual Key Codes
3692 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp
3693 var unprintable = (k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222);
3694 // synthesize keypress for most unprintables and CTRL-keys
3695 if(unprintable||evt.ctrlKey){
3696 var c = unprintable ? 0 : k;
3699 return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively
3700 }else if(c>95 && c<106){
3701 c -= 48; // map CTRL-[numpad 0-9] to ASCII
3702 }else if((!evt.shiftKey)&&(c>=65&&c<=90)){
3703 c += 32; // map CTRL-[A-Z] to lowercase
3705 c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII
3708 // simulate a keypress event
3709 var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c});
3710 kp.call(evt.currentTarget, faux);
3711 evt.cancelBubble = faux.cancelBubble;
3712 evt.returnValue = faux.returnValue;
3713 _trySetKeyCode(evt, faux.keyCode);
3716 // Called in Event scope
3717 _stopPropagation: function(){
3718 this.cancelBubble = true;
3720 _preventDefault: function(){
3721 // Setting keyCode to 0 is the only way to prevent certain keypresses (namely
3722 // ctrl-combinations that correspond to menu accelerator keys).
3723 // Otoh, it prevents upstream listeners from getting this information
3724 // Try to split the difference here by clobbering keyCode only for ctrl
3725 // combinations. If you still need to access the key upstream, bubbledKeyCode is
3726 // provided as a workaround.
3727 this.bubbledKeyCode = this.keyCode;
3728 if(this.ctrlKey){_trySetKeyCode(this, 0);}
3729 this.returnValue = false;
3733 // override stopEvent for IE
3734 dojo.stopEvent = function(evt){
3735 evt = evt || window.event;
3736 del._stopPropagation.call(evt);
3737 del._preventDefault.call(evt);
3741 del._synthesizeEvent = function(evt, props){
3742 var faux = dojo.mixin({}, evt, props);
3743 del._setKeyChar(faux);
3744 // FIXME: would prefer to use dojo.hitch: dojo.hitch(evt, evt.preventDefault);
3745 // but it throws an error when preventDefault is invoked on Safari
3746 // does Event.preventDefault not support "apply" on Safari?
3747 faux.preventDefault = function(){ evt.preventDefault(); };
3748 faux.stopPropagation = function(){ evt.stopPropagation(); };
3752 // Opera event normalization
3755 _fixEvent: function(evt, sender){
3760 c=99; // Mozilla maps CTRL-BREAK to CTRL-c
3762 // can't trap some keys at all, like INSERT and DELETE
3763 // there is no differentiating info between DELETE and ".", or INSERT and "-"
3764 c = ((c<41)&&(!evt.shiftKey) ? 0 : c);
3765 if((evt.ctrlKey)&&(!evt.shiftKey)&&(c>=65)&&(c<=90)){
3766 // lowercase CTRL-[A-Z] keys
3769 return del._synthesizeEvent(evt, { charCode: c });
3776 // Safari event normalization
3779 _fixEvent: function(evt, sender){
3782 var c = evt.charCode, s = evt.shiftKey, k = evt.keyCode;
3783 // FIXME: This is a hack, suggest we rethink keyboard strategy.
3784 // Arrow and page keys have 0 "keyCode" in keypress events.on Safari for Windows
3785 k = k || identifierMap[evt.keyIdentifier] || 0;
3786 if(evt.keyIdentifier=="Enter"){
3787 c = 0; // differentiate Enter from CTRL-m (both code 13)
3788 }else if((evt.ctrlKey)&&(c>0)&&(c<27)){
3789 c += 96; // map CTRL-[A-Z] codes to ASCII
3790 } else if (c==dojo.keys.SHIFT_TAB) {
3791 c = dojo.keys.TAB; // morph SHIFT_TAB into TAB + shiftKey: true
3794 c = (c>=32 && c<63232 ? c : 0); // avoid generating keyChar for non-printables
3796 return del._synthesizeEvent(evt, {charCode: c, shiftKey: s, keyCode: k});
3802 dojo.mixin(dojo.keys, {
3827 PRINT_SCREEN: 63248,
3831 var dk = dojo.keys, identifierMap = { "Up": dk.UP_ARROW, "Down": dk.DOWN_ARROW, "Left": dk.LEFT_ARROW, "Right": dk.RIGHT_ARROW, "PageUp": dk.PAGE_UP, "PageDown": dk.PAGE_DOWN };
3836 // keep this out of the closure
3837 // closing over 'iel' or 'ieh' b0rks leak prevention
3838 // ls[i] is an index into the master handler array
3839 dojo._ieDispatcher = function(args, sender){
3840 var ap=Array.prototype, h=dojo._ie_listener.handlers, c=args.callee, ls=c._listeners, t=h[c.target];
3841 // return value comes from original target function
3842 var r = t && t.apply(sender, args);
3843 // invoke listeners after target function
3846 h[ls[i]].apply(sender, args);
3851 dojo._getIeDispatcher = function(){
3852 // ensure the returned function closes over nothing
3853 return new Function(dojo._scopeName + "._ieDispatcher(arguments, this)"); // function
3855 // keep this out of the closure to reduce RAM allocation
3856 dojo._event_listener._fixCallback = function(fp){
3857 var f = dojo._event_listener._fixEvent;
3858 return function(e){ return fp.call(this, f(e, this)); };
3864 if(!dojo._hasResource["dojo._base.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
3865 dojo._hasResource["dojo._base.html"] = true;
3867 dojo.provide("dojo._base.html");
3869 // FIXME: need to add unit tests for all the semi-public methods
3872 document.execCommand("BackgroundImageCache", false, true);
3874 // sane browsers don't have cache "issues"
3877 // =============================
3879 // =============================
3882 dojo.byId = function(id, doc){
3884 // Returns DOM node with matching `id` attribute or `null`
3885 // if not found, similar to "$" function in another library.
3886 // If `id` is a DomNode, this function is a no-op.
3888 // id: String|DOMNode
3889 // A string to match an HTML id attribute or a reference to a DOM Node
3892 // Document to work in. Defaults to the current value of
3893 // dojo.doc. Can be used to retrieve
3894 // node references from other documents.
3896 if(dojo.isIE || dojo.isOpera){
3897 dojo.byId = function(id, doc){
3898 if(dojo.isString(id)){
3899 var _d = doc || dojo.doc;
3900 var te = _d.getElementById(id);
3901 // attributes.id.value is better than just id in case the
3902 // user has a name=id inside a form
3903 if(te && te.attributes.id.value == id){
3906 var eles = _d.all[id];
3907 if(!eles || !eles.length){ return eles; }
3908 // if more than 1, choose first with the correct id
3910 while((te=eles[i++])){
3911 if(te.attributes.id.value == id){ return te; }
3915 return id; // DomNode
3919 dojo.byId = function(id, doc){
3920 return dojo.isString(id) ? (doc || dojo.doc).getElementById(id) : id; // DomNode
3929 dojo.createElement = function(obj, parent, position){
3930 // TODO: need to finish this!
3936 var _destroyContainer = null;
3937 dojo.addOnUnload(function(){
3938 _destroyContainer=null; //prevent IE leak
3940 dojo._destroyElement = function(/*String||DomNode*/node){
3942 // removes node from its parent, clobbers it and all of its
3945 // the element to be destroyed, either as an ID or a reference
3947 node = d.byId(node);
3949 if(!_destroyContainer){
3950 _destroyContainer = document.createElement("div");
3952 _destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) : node);
3953 // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature
3954 _destroyContainer.innerHTML = "";
3960 dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor){
3962 // Returns true if node is a descendant of ancestor
3963 // node: id or node reference to test
3964 // ancestor: id or node reference of potential parent to test against
3966 node = d.byId(node);
3967 ancestor = d.byId(ancestor);
3969 if(node === ancestor){
3970 return true; // Boolean
3972 node = node.parentNode;
3974 }catch(e){ /* squelch, return false */ }
3975 return false; // Boolean
3978 dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable){
3979 // summary: enable or disable selection on a node
3981 // id or reference to node
3983 node = d.byId(node);
3985 node.style.MozUserSelect = selectable ? "" : "none";
3986 }else if(d.isKhtml){
3987 node.style.KhtmlUserSelect = selectable ? "auto" : "none";
3989 node.unselectable = selectable ? "" : "on";
3990 d.query("*", node).forEach(function(descendant){
3991 descendant.unselectable = selectable ? "" : "on";
3994 //FIXME: else? Opera?
3997 var _insertBefore = function(/*Node*/node, /*Node*/ref){
3998 ref.parentNode.insertBefore(node, ref);
3999 return true; // boolean
4002 var _insertAfter = function(/*Node*/node, /*Node*/ref){
4004 // Try to insert node after ref
4005 var pn = ref.parentNode;
4006 if(ref == pn.lastChild){
4007 pn.appendChild(node);
4009 return _insertBefore(node, ref.nextSibling); // boolean
4011 return true; // boolean
4014 dojo.place = function(/*String|DomNode*/node, /*String|DomNode*/refNode, /*String|Number*/position){
4016 // Attempt to insert node into the DOM, choosing from various positioning options.
4017 // Returns true if successful, false otherwise.
4019 // id or node reference to place relative to refNode
4021 // id or node reference to use as basis for placement
4023 // string noting the position of node relative to refNode or a
4024 // number indicating the location in the childNodes collection of
4025 // refNode. Accepted string values are:
4032 // "first" and "last" indicate positions as children of refNode.
4034 // FIXME: need to write tests for this!!!!
4035 if(!node || !refNode || position === undefined){
4036 return false; // boolean
4038 node = d.byId(node);
4039 refNode = d.byId(refNode);
4040 if(typeof position == "number"){
4041 var cn = refNode.childNodes;
4042 if((position == 0 && cn.length == 0) ||
4043 cn.length == position){
4044 refNode.appendChild(node); return true;
4047 return _insertBefore(node, refNode.firstChild);
4049 return _insertAfter(node, cn[position-1]);
4051 switch(position.toLowerCase()){
4053 return _insertBefore(node, refNode); // boolean
4055 return _insertAfter(node, refNode); // boolean
4057 if(refNode.firstChild){
4058 return _insertBefore(node, refNode.firstChild); // boolean
4060 // else fallthrough...
4061 default: // aka: last
4062 refNode.appendChild(node);
4063 return true; // boolean
4067 // Box functions will assume this model.
4068 // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode.
4069 // Can be set to change behavior of box setters.
4073 // "content-box" (default)
4074 dojo.boxModel = "content-box";
4076 // We punt per-node box mode testing completely.
4077 // If anybody cares, we can provide an additional (optional) unit
4078 // that overrides existing code to include per-node box sensitivity.
4080 // Opera documentation claims that Opera 9 uses border-box in BackCompat mode.
4081 // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box.
4082 // IIRC, earlier versions of Opera did in fact use border-box.
4083 // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault.
4085 if(d.isIE /*|| dojo.isOpera*/){
4086 var _dcm = document.compatMode;
4087 // client code may have to adjust if compatMode varies across iframes
4088 d.boxModel = _dcm == "BackCompat" || _dcm == "QuirksMode" || d.isIE<6 ? "border-box" : "content-box"; // FIXME: remove IE < 6 support?
4091 // =============================
4093 // =============================
4095 // getComputedStyle drives most of the style code.
4096 // Wherever possible, reuse the returned object.
4098 // API functions below that need to access computed styles accept an
4099 // optional computedStyle parameter.
4100 // If this parameter is omitted, the functions will call getComputedStyle themselves.
4101 // This way, calling code can access computedStyle once, and then pass the reference to
4102 // multiple API functions.
4105 dojo.getComputedStyle = function(node){
4107 // Returns a "computed style" object.
4110 // Gets a "computed style" object which can be used to gather
4111 // information about the current state of the rendered node.
4113 // Note that this may behave differently on different browsers.
4114 // Values may have different formats and value encodings across
4117 // Note also that this method is expensive. Wherever possible,
4118 // reuse the returned object.
4120 // Use the dojo.style() method for more consistent (pixelized)
4124 // A reference to a DOM node. Does NOT support taking an
4125 // ID string for speed reasons.
4127 // | dojo.getComputedStyle(dojo.byId('foo')).borderWidth;
4128 return; // CSS2Properties
4132 var gcs, dv = document.defaultView;
4134 gcs = function(/*DomNode*/node){
4135 var s = dv.getComputedStyle(node, null);
4136 if(!s && node.style){
4137 node.style.display = "";
4138 s = dv.getComputedStyle(node, null);
4143 gcs = function(node){
4144 return node.currentStyle;
4147 gcs = function(node){
4148 return dv.getComputedStyle(node, null);
4151 dojo.getComputedStyle = gcs;
4154 dojo._toPixelValue = function(element, value){
4155 // style values can be floats, client code may want
4156 // to round for integer pixels.
4157 return parseFloat(value) || 0;
4160 dojo._toPixelValue = function(element, avalue){
4161 if(!avalue){ return 0; }
4162 // on IE7, medium is usually 4 pixels
4163 if(avalue=="medium"){ return 4; }
4164 // style values can be floats, client code may
4165 // want to round this value for integer pixels.
4166 if(avalue.slice && (avalue.slice(-2)=='px')){ return parseFloat(avalue); }
4168 var sLeft = style.left;
4169 var rsLeft = runtimeStyle.left;
4170 runtimeStyle.left = currentStyle.left;
4172 // 'avalue' may be incompatible with style.left, which can cause IE to throw
4173 // this has been observed for border widths using "thin", "medium", "thick" constants
4174 // those particular constants could be trapped by a lookup
4175 // but perhaps there are more
4176 style.left = avalue;
4177 avalue = style.pixelLeft;
4182 runtimeStyle.left = rsLeft;
4187 var px = d._toPixelValue;
4189 // FIXME: there opacity quirks on FF that we haven't ported over. Hrm.
4191 dojo._getOpacity = function(node){
4193 // Returns the current opacity of the passed node as a
4194 // floating-point value between 0 and 1.
4196 // a reference to a DOM node. Does NOT support taking an
4197 // ID string for speed reasons.
4198 // return: Number between 0 and 1
4202 dojo._getOpacity = d.isIE ? function(node){
4204 return node.filters.alpha.opacity / 100; // Number
4209 return gcs(node).opacity;
4213 dojo._setOpacity = function(node, opacity){
4215 // set the opacity of the passed node portably. Returns the
4216 // new opacity of the node.
4218 // a reference to a DOM node. Does NOT support taking an
4219 // ID string for performance reasons.
4221 // A Number between 0 and 1. 0 specifies transparent.
4222 // return: Number between 0 and 1
4226 dojo._setOpacity = d.isIE ? function(/*DomNode*/node, /*Number*/opacity){
4228 // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so remove it altogether (bug #2661)
4229 var filterRE = /FILTER:[^;]*;?/i;
4230 node.style.cssText = node.style.cssText.replace(filterRE, "");
4231 if(node.nodeName.toLowerCase() == "tr"){
4232 d.query("> td", node).forEach(function(i){
4233 i.style.cssText = i.style.cssText.replace(filterRE, "");
4237 var o = "Alpha(Opacity="+ opacity * 100 +")";
4238 node.style.filter = o;
4240 if(node.nodeName.toLowerCase() == "tr"){
4241 d.query("> td", node).forEach(function(i){
4246 } : function(node, opacity){
4247 return node.style.opacity = opacity;
4250 var _pixelNamesCache = {
4251 left: true, top: true
4253 var _pixelRegExp = /margin|padding|width|height|max|min|offset/; // |border
4254 var _toStyleValue = function(node, type, value){
4255 type = type.toLowerCase();
4256 if(d.isIE && value == "auto"){
4257 if(type == "height"){ return node.offsetHeight; }
4258 if(type == "width"){ return node.offsetWidth; }
4260 if(!(type in _pixelNamesCache)){
4261 // if(dojo.isOpera && type == "cssText"){
4262 // FIXME: add workaround for #2855 here
4264 _pixelNamesCache[type] = _pixelRegExp.test(type);
4266 return _pixelNamesCache[type] ? px(node, value) : value;
4269 var _floatStyle = d.isIE ? "styleFloat" : "cssFloat";
4270 var _floatAliases = { "cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle };
4274 dojo.style = function( /*DomNode|String*/ node,
4275 /*String?|Object?*/ style,
4278 // Accesses styles on a node. If 2 arguments are
4279 // passed, acts as a getter. If 3 arguments are passed, acts
4282 // id or reference to node to get/set style for
4284 // the style property to set in DOM-accessor format
4285 // ("borderWidth", not "border-width") or an object with key/value
4286 // pairs suitable for setting each property.
4288 // If passed, sets value on the node for style, handling
4289 // cross-browser concerns.
4291 // Passing only an ID or node returns the computed style object of
4293 // | dojo.style("thinger");
4295 // Passing a node and a style property returns the current
4296 // normalized, computed value for that property:
4297 // | dojo.style("thinger", "opacity"); // 1 by default
4300 // Passing a node, a style property, and a value changes the
4301 // current display of the node and returns the new computed value
4302 // | dojo.style("thinger", "opacity", 0.5); // == 0.5
4305 // Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node:
4306 // | dojo.style("thinger", {
4307 // | "opacity": 0.5,
4308 // | "border": "3px solid black",
4313 // When the CSS style property is hyphenated, the JavaScript property is camelCased.
4314 // font-size becomes fontSize, and so on.
4315 // | dojo.style("thinger",{
4316 // | fontSize:"14pt",
4317 // | letterSpacing:"1.2em"
4321 // dojo.NodeList implements .style() using the same syntax, omitting the "node" parameter, calling
4322 // dojo.style() on every element of the list. See: dojo.query and dojo.NodeList
4323 // | dojo.query(".someClassName").style("visibility","hidden");
4325 // | dojo.query("#baz > div").style({
4327 // | fontSize:"13pt"
4330 var n = d.byId(node), args = arguments.length, op = (style=="opacity");
4331 style = _floatAliases[style] || style;
4333 return op ? d._setOpacity(n, value) : n.style[style] = value; /*Number*/
4335 if(args == 2 && op){
4336 return d._getOpacity(n);
4339 if(args == 2 && !d.isString(style)){
4340 for(var x in style){
4341 d.style(node, x, style[x]);
4345 return (args == 1) ? s : _toStyleValue(n, style, s[style]); /* CSS2Properties||String||Number */
4348 // =============================
4350 // =============================
4352 dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle){
4354 // Returns object with special values specifically useful for node
4357 // * l/t = left/top padding (respectively)
4358 // * w = the total of the left and right padding
4359 // * h = the total of the top and bottom padding
4361 // If 'node' has position, l/t forms the origin for child nodes.
4362 // The w/h are used for calculating boxes.
4363 // Normally application code will not need to invoke this
4364 // directly, and will use the ...box... functions instead.
4366 s = computedStyle||gcs(n),
4367 l = px(n, s.paddingLeft),
4368 t = px(n, s.paddingTop);
4372 w: l+px(n, s.paddingRight),
4373 h: t+px(n, s.paddingBottom)
4377 dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
4379 // returns an object with properties useful for noting the border
4382 // * l/t = the sum of left/top border (respectively)
4383 // * w = the sum of the left and right border
4384 // * h = the sum of the top and bottom border
4386 // The w/h are used for calculating boxes.
4387 // Normally application code will not need to invoke this
4388 // directly, and will use the ...box... functions instead.
4391 s = computedStyle||gcs(n),
4392 bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0),
4393 bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0);
4397 w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0),
4398 h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0)
4402 dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){
4404 // returns object with properties useful for box fitting with
4405 // regards to padding.
4407 // * l/t = the sum of left/top padding and left/top border (respectively)
4408 // * w = the sum of the left and right padding and border
4409 // * h = the sum of the top and bottom padding and border
4411 // The w/h are used for calculating boxes.
4412 // Normally application code will not need to invoke this
4413 // directly, and will use the ...box... functions instead.
4415 s = computedStyle||gcs(n),
4416 p = d._getPadExtents(n, s),
4417 b = d._getBorderExtents(n, s);
4426 dojo._getMarginExtents = function(n, computedStyle){
4428 // returns object with properties useful for box fitting with
4429 // regards to box margins (i.e., the outer-box).
4431 // * l/t = marginLeft, marginTop, respectively
4432 // * w = total width, margin inclusive
4433 // * h = total height, margin inclusive
4435 // The w/h are used for calculating boxes.
4436 // Normally application code will not need to invoke this
4437 // directly, and will use the ...box... functions instead.
4439 s = computedStyle||gcs(n),
4440 l = px(n, s.marginLeft),
4441 t = px(n, s.marginTop),
4442 r = px(n, s.marginRight),
4443 b = px(n, s.marginBottom);
4444 if(d.isSafari && (s.position != "absolute")){
4445 // FIXME: Safari's version of the computed right margin
4446 // is the space between our right edge and the right edge
4447 // of our offsetParent.
4448 // What we are looking for is the actual margin value as
4449 // determined by CSS.
4450 // Hack solution is to assume left/right margins are the same.
4461 // Box getters work in any box context because offsetWidth/clientWidth
4462 // are invariant wrt box context
4464 // They do *not* work for display: inline objects that have padding styles
4465 // because the user agent ignores padding (it's bogus styling in any case)
4467 // Be careful with IMGs because they are inline or block depending on
4468 // browser and browser mode.
4470 // Although it would be easier to read, there are not separate versions of
4471 // _getMarginBox for each browser because:
4472 // 1. the branching is not expensive
4473 // 2. factoring the shared code wastes cycles (function call overhead)
4474 // 3. duplicating the shared code wastes bytes
4476 dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle){
4478 // returns an object that encodes the width, height, left and top
4479 // positions of the node's margin box.
4480 var s = computedStyle||gcs(node), me = d._getMarginExtents(node, s);
4481 var l = node.offsetLeft - me.l, t = node.offsetTop - me.t;
4484 // If offsetParent has a computed overflow != visible, the offsetLeft is decreased
4485 // by the parent's border.
4486 // We don't want to compute the parent's style, so instead we examine node's
4487 // computed left/top which is more stable.
4488 var sl = parseFloat(s.left), st = parseFloat(s.top);
4489 if(!isNaN(sl) && !isNaN(st)){
4492 // If child's computed left/top are not parseable as a number (e.g. "auto"), we
4493 // have no choice but to examine the parent's computed style.
4494 var p = node.parentNode;
4497 if(pcs.overflow != "visible"){
4498 var be = d._getBorderExtents(p, pcs);
4499 l += be.l, t += be.t;
4503 }else if(d.isOpera){
4504 // On Opera, offsetLeft includes the parent's border
4505 var p = node.parentNode;
4507 var be = d._getBorderExtents(p);
4508 l -= be.l, t -= be.t;
4514 w: node.offsetWidth + me.w,
4515 h: node.offsetHeight + me.h
4519 dojo._getContentBox = function(node, computedStyle){
4521 // Returns an object that encodes the width, height, left and top
4522 // positions of the node's content box, irrespective of the
4523 // current box model.
4525 // clientWidth/Height are important since the automatically account for scrollbars
4526 // fallback to offsetWidth/Height for special cases (see #3378)
4527 var s=computedStyle||gcs(node), pe=d._getPadExtents(node, s), be=d._getBorderExtents(node, s), w=node.clientWidth, h;
4529 w=node.offsetWidth, h=node.offsetHeight;
4531 h=node.clientHeight, be.w = be.h = 0;
4533 // On Opera, offsetLeft includes the parent's border
4534 if(d.isOpera){ pe.l += be.l; pe.t += be.t; };
4543 dojo._getBorderBox = function(node, computedStyle){
4544 var s=computedStyle||gcs(node), pe=d._getPadExtents(node, s), cb=d._getContentBox(node, s);
4553 // Box setters depend on box context because interpretation of width/height styles
4554 // vary wrt box context.
4556 // The value of dojo.boxModel is used to determine box context.
4557 // dojo.boxModel can be set directly to change behavior.
4559 // Beware of display: inline objects that have padding styles
4560 // because the user agent ignores padding (it's a bogus setup anyway)
4562 // Be careful with IMGs because they are inline or block depending on
4563 // browser and browser mode.
4565 // Elements other than DIV may have special quirks, like built-in
4566 // margins or padding, or values not detectable via computedStyle.
4567 // In particular, margins on TABLE do not seems to appear
4568 // at all in computedStyle on Mozilla.
4570 dojo._setBox = function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){
4572 // sets width/height/left/top in the current (native) box-model
4573 // dimentions. Uses the unit passed in u.
4574 // node: DOM Node reference. Id string not supported for performance reasons.
4575 // l: optional. left offset from parent.
4576 // t: optional. top offset from parent.
4577 // w: optional. width in current box model.
4578 // h: optional. width in current box model.
4579 // u: optional. unit measure to use for other measures. Defaults to "px".
4582 if(!isNaN(l)){ s.left = l+u; }
4583 if(!isNaN(t)){ s.top = t+u; }
4584 if(w>=0){ s.width = w+u; }
4585 if(h>=0){ s.height = h+u; }
4588 dojo._usesBorderBox = function(/*DomNode*/node){
4590 // True if the node uses border-box layout.
4592 // We could test the computed style of node to see if a particular box
4593 // has been specified, but there are details and we choose not to bother.
4594 var n = node.tagName;
4595 // For whatever reason, TABLE and BUTTON are always border-box by default.
4596 // If you have assigned a different box to either one via CSS then
4597 // box functions will break.
4598 return d.boxModel=="border-box" || n=="TABLE" || n=="BUTTON"; // boolean
4601 dojo._setContentSize = function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle){
4603 // Sets the size of the node's contents, irrespective of margins,
4604 // padding, or borders.
4605 if(d._usesBorderBox(node)){
4606 var pb = d._getPadBorderExtents(node, computedStyle);
4607 if(widthPx >= 0){ widthPx += pb.w; }
4608 if(heightPx >= 0){ heightPx += pb.h; }
4610 d._setBox(node, NaN, NaN, widthPx, heightPx);
4613 dojo._setMarginBox = function(/*DomNode*/node, /*Number?*/leftPx, /*Number?*/topPx,
4614 /*Number?*/widthPx, /*Number?*/heightPx,
4615 /*Object*/computedStyle){
4617 // sets the size of the node's margin box and placement
4618 // (left/top), irrespective of box model. Think of it as a
4619 // passthrough to dojo._setBox that handles box-model vagaries for
4622 var s = computedStyle||gcs(node);
4623 // Some elements have special padding, margin, and box-model settings.
4624 // To use box functions you may need to set padding, margin explicitly.
4625 // Controlling box-model is harder, in a pinch you might set dojo.boxModel.
4626 var bb=d._usesBorderBox(node),
4627 pb=bb ? _nilExtents : d._getPadBorderExtents(node, s),
4628 mb=d._getMarginExtents(node, s);
4629 if(widthPx>=0){ widthPx = Math.max(widthPx - pb.w - mb.w, 0); }
4630 if(heightPx>=0){ heightPx = Math.max(heightPx - pb.h - mb.h, 0); }
4631 d._setBox(node, leftPx, topPx, widthPx, heightPx);
4634 var _nilExtents = { l:0, t:0, w:0, h:0 };
4638 dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box){
4640 // Getter/setter for the margin-box of node.
4642 // Returns an object in the expected format of box (regardless
4643 // if box is passed). The object might look like:
4644 // `{ l: 50, t: 200, w: 300: h: 150 }`
4645 // for a node offset from its parent 50px to the left, 200px from
4646 // the top with a margin width of 300px and a margin-height of
4649 // id or reference to DOM Node to get/set box for
4651 // If passed, denotes that dojo.marginBox() should
4652 // update/set the margin box for node. Box is an object in the
4653 // above format. All properties are optional if passed.
4654 var n=d.byId(node), s=gcs(n), b=box;
4655 return !b ? d._getMarginBox(n, s) : d._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object
4658 dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box){
4660 // Getter/setter for the content-box of node.
4662 // Returns an object in the expected format of box (regardless if box is passed).
4663 // The object might look like:
4664 // `{ l: 50, t: 200, w: 300: h: 150 }`
4665 // for a node offset from its parent 50px to the left, 200px from
4666 // the top with a content width of 300px and a content-height of
4667 // 150px. Note that the content box may have a much larger border
4668 // or margin box, depending on the box model currently in use and
4669 // CSS values set/inherited for node.
4671 // id or reference to DOM Node to get/set box for
4673 // If passed, denotes that dojo.contentBox() should
4674 // update/set the content box for node. Box is an object in the
4675 // above format. All properties are optional if passed.
4676 var n=dojo.byId(node), s=gcs(n), b=box;
4677 return !b ? d._getContentBox(n, s) : d._setContentSize(n, b.w, b.h, s); // Object
4680 // =============================
4682 // =============================
4684 var _sumAncestorProperties = function(node, prop){
4685 if(!(node = (node||0).parentNode)){return 0};
4686 var val, retVal = 0, _b = d.body();
4687 while(node && node.style){
4688 if(gcs(node).position == "fixed"){
4694 // opera and khtml #body & #html has the same values, we only
4696 if(node == _b){ break; }
4698 node = node.parentNode;
4700 return retVal; // integer
4703 dojo._docScroll = function(){
4707 de = d.doc.documentElement;
4709 y: (_w.pageYOffset || de.scrollTop || _b.scrollTop || 0),
4710 x: (_w.pageXOffset || d._fixIeBiDiScrollLeft(de.scrollLeft) || _b.scrollLeft || 0)
4714 dojo._isBodyLtr = function(){
4715 //FIXME: could check html and body tags directly instead of computed style? need to ignore case, accept empty values
4716 return !("_bodyLtr" in d) ?
4717 d._bodyLtr = gcs(d.body()).direction == "ltr" :
4718 d._bodyLtr; // Boolean
4721 dojo._getIeDocumentElementOffset = function(){
4723 // The following values in IE contain an offset:
4726 // node.getBoundingClientRect().left
4727 // node.getBoundingClientRect().top
4728 // But other position related values do not contain this offset, such as
4729 // node.offsetLeft, node.offsetTop, node.style.left and node.style.top.
4730 // The offset is always (2, 2) in LTR direction. When the body is in RTL
4731 // direction, the offset counts the width of left scroll bar's width.
4732 // This function computes the actual offset.
4734 //NOTE: assumes we're being called in an IE browser
4736 var de = d.doc.documentElement;
4737 //FIXME: use this instead? var de = d.compatMode == "BackCompat" ? d.body : d.documentElement;
4739 return (d.isIE >= 7) ?
4740 {x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top}
4743 {x: d._isBodyLtr() || window.parent == window ?
4744 de.clientLeft : de.offsetWidth - de.clientWidth - de.clientLeft,
4745 y: de.clientTop}; // Object
4748 dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft){
4749 // In RTL direction, scrollLeft should be a negative value, but IE
4750 // returns a positive one. All codes using documentElement.scrollLeft
4751 // must call this function to fix this error, otherwise the position
4752 // will offset to right when there is a horizontal scrollbar.
4754 if(d.isIE && !dojo._isBodyLtr()){
4755 var de = dd.compatMode == "BackCompat" ? dd.body : dd.documentElement;
4756 return scrollLeft + de.clientWidth - de.scrollWidth; // Integer
4758 return scrollLeft; // Integer
4761 dojo._abs = function(/*DomNode*/node, /*Boolean?*/includeScroll){
4763 // Gets the position of the passed element relative to
4764 // the viewport (if includeScroll==false), or relative to the
4765 // document root (if includeScroll==true).
4767 // Returns an object of the form:
4768 // { x: 100, y: 300 }
4769 // if includeScroll is passed, the x and y values will include any
4770 // document offsets that may affect the position relative to the
4773 // FIXME: need to decide in the brave-new-world if we're going to be
4774 // margin-box or border-box.
4775 var ownerDocument = node.ownerDocument;
4781 // targetBoxType == "border-box"
4783 if(d.isIE || (d.isFF >= 3)){
4784 var client = node.getBoundingClientRect();
4785 var offset = (d.isIE) ? d._getIeDocumentElementOffset() : { x: 0, y: 0};
4786 ret.x = client.left - offset.x;
4787 ret.y = client.top - offset.y;
4788 }else if(ownerDocument["getBoxObjectFor"]){
4790 var bo = ownerDocument.getBoxObjectFor(node),
4791 b = d._getBorderExtents(node);
4792 ret.x = bo.x - b.l - _sumAncestorProperties(node, "scrollLeft");
4793 ret.y = bo.y - b.t - _sumAncestorProperties(node, "scrollTop");
4795 if(node["offsetParent"]){
4797 // in Safari, if the node is an absolutely positioned child of
4798 // the body and the body has a margin the offset of the child
4799 // and the body contain the body's margins, so we need to end
4801 // FIXME: getting contrary results to the above in latest WebKit.
4803 //(node.style.getPropertyValue("position") == "absolute") &&
4804 (gcs(node).position == "absolute") &&
4805 (node.parentNode == db)){
4808 endNode = db.parentNode;
4810 if(node.parentNode != db){
4812 if(d.isOpera){ nd = db; }
4813 ret.x -= _sumAncestorProperties(nd, "scrollLeft");
4814 ret.y -= _sumAncestorProperties(nd, "scrollTop");
4818 var n = curnode.offsetLeft;
4819 //FIXME: ugly hack to workaround the submenu in
4820 //popupmenu2 does not shown up correctly in opera.
4821 //Someone have a better workaround?
4822 if(!d.isOpera || n > 0){
4823 ret.x += isNaN(n) ? 0 : n;
4825 var t = curnode.offsetTop;
4826 ret.y += isNaN(t) ? 0 : t;
4827 if(d.isSafari && curnode != node){
4828 var cs = gcs(curnode);
4829 ret.x += px(curnode, cs.borderLeftWidth);
4830 ret.y += px(curnode, cs.borderTopWidth);
4832 curnode = curnode.offsetParent;
4833 }while((curnode != endNode) && curnode);
4834 }else if(node.x && node.y){
4835 ret.x += isNaN(node.x) ? 0 : node.x;
4836 ret.y += isNaN(node.y) ? 0 : node.y;
4839 // account for document scrolling
4840 // if offsetParent is used, ret value already includes scroll position
4841 // so we may have to actually remove that value if !includeScroll
4843 var scroll = d._docScroll();
4848 return ret; // object
4851 // FIXME: need a setter for coords or a moveTo!!
4852 dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){
4854 // Returns an object that measures margin box width/height and
4855 // absolute positioning data from dojo._abs().
4858 // Returns an object that measures margin box width/height and
4859 // absolute positioning data from dojo._abs().
4860 // Return value will be in the form:
4861 // `{ l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }`
4862 // Does not act as a setter. If includeScroll is passed, the x and
4863 // y params are affected as one would expect in dojo._abs().
4864 var n=d.byId(node), s=gcs(n), mb=d._getMarginBox(n, s);
4865 var abs = d._abs(n, includeScroll);
4871 // =============================
4872 // Element attribute Functions
4873 // =============================
4875 var _fixAttrName = function(/*String*/name){
4876 switch(name.toLowerCase()){
4878 // Internet Explorer will only set or remove tabindex
4879 // if it is spelled "tabIndex"
4880 // console.debug((dojo.isIE && dojo.isIE < 8)? "tabIndex" : "tabindex");
4881 return (d.isIE && d.isIE < 8) ? "tabIndex" : "tabindex";
4887 // non-deprecated HTML4 attributes with default values
4888 // http://www.w3.org/TR/html401/index/attributes.html
4889 // FF and Safari will return the default values if you
4890 // access the attributes via a property but not
4891 // via getAttribute()
4895 frameborder: "frameborder",
4898 scrolling: "scrolling",
4902 valuetype: "valueType"
4905 dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name){
4907 // Returns true if the requested attribute is specified on the
4908 // given element, and false otherwise.
4910 // id or reference to the element to check
4912 // the name of the attribute
4914 // true if the requested attribute is specified on the
4915 // given element, and false otherwise
4916 var attr = d.byId(node).getAttributeNode(_fixAttrName(name));
4917 return attr ? attr.specified : false; // Boolean
4925 var _attrId = dojo._scopeName + "attrid";
4927 dojo.attr = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value){
4929 // Gets or sets an attribute on an HTML element.
4931 // Handles normalized getting and setting of attributes on DOM
4932 // Nodes. If 2 arguments are passed, and a the second argumnt is a
4933 // string, acts as a getter.
4935 // If a third argument is passed, or if the second argumnt is a
4936 // map of attributes, acts as a setter.
4938 // When passing functions as values, note that they will not be
4939 // directly assigned to slots on the node, but rather the default
4940 // behavior will be removed and the new behavior will be added
4941 // using `dojo.connect()`, meaning that event handler properties
4942 // will be normalized and that some caveats with regards to
4943 // non-standard behaviors for onsubmit apply. Namely that you
4944 // should cancel form submission using `dojo.stopEvent()` on the
4945 // passed event object instead of returning a boolean value from
4946 // the handler itself.
4948 // id or reference to the element to get or set the attribute on
4950 // the name of the attribute to get or set.
4952 // The value to set for the attribute
4954 // when used as a getter, the value of the requested attribute
4955 // or null if that attribute does not have a specified or
4958 // when user as a setter, undefined
4960 // | // get the current value of the "foo" attribute on a node
4961 // | dojo.attr(dojo.byId("nodeId"), "foo");
4963 // | // we can just pass the id:
4964 // | dojo.attr("nodeId", "foo");
4966 // | // use attr() to set the tab index
4967 // | dojo.attr("nodeId", "tabindex", 3);
4969 // | // set multiple values at once, including event handlers:
4970 // | dojo.attr("formId", {
4972 // | "tabindex": -1,
4973 // | "method": "POST",
4974 // | "onsubmit": function(e){
4975 // | // stop submitting the form. Note that the IE behavior
4976 // | // of returning true or false will have no effect here
4977 // | // since our handler is connect()ed to the built-in
4978 // | // onsubmit behavior and so we need to use
4979 // | // dojo.stopEvent() to ensure that the submission
4980 // | // doesn't proceed.
4981 // | dojo.stopEvent(e);
4983 // | // submit the form with Ajax
4984 // | dojo.xhrPost({ form: "formId" });
4988 var args = arguments.length;
4989 if(args == 2 && !d.isString(name)){
4990 for(var x in name){ d.attr(node, x, name[x]); }
4993 node = d.byId(node);
4994 name = _fixAttrName(name);
4996 if(d.isFunction(value)){
4997 // clobber if we can
4998 var attrId = d.attr(node, _attrId);
5001 d.attr(node, _attrId, attrId);
5003 if(!_evtHdlrMap[attrId]){
5004 _evtHdlrMap[attrId] = {};
5006 var h = _evtHdlrMap[attrId][name];
5015 // ensure that event objects are normalized, etc.
5016 _evtHdlrMap[attrId][name] = d.connect(node, name, value);
5018 }else if(typeof value == "boolean"){ // e.g. onsubmit, disabled
5019 // if a function, we should normalize the event object here!!!
5022 node.setAttribute(name, value);
5026 // should we access this attribute via a property or
5027 // via getAttribute()?
5028 var prop = _attrProps[name.toLowerCase()];
5032 var value = node[name];
5033 return (typeof value == 'boolean' || typeof value == 'function') ? value : (d.hasAttr(node, name) ? node.getAttribute(name) : null);
5038 dojo.removeAttr = function(/*DomNode|String*/node, /*String*/name){
5040 // Removes an attribute from an HTML element.
5042 // id or reference to the element to remove the attribute from
5044 // the name of the attribute to remove
5045 d.byId(node).removeAttribute(_fixAttrName(name));
5049 // =============================
5050 // (CSS) Class Functions
5051 // =============================
5053 dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr){
5055 // Returns whether or not the specified classes are a portion of the
5056 // class list currently applied to the node.
5057 return ((" "+dojo.byId(node).className+" ").indexOf(" "+classStr+" ") >= 0); // Boolean
5060 dojo.addClass = function(/*DomNode|String*/node, /*String*/classStr){
5062 // Adds the specified classes to the end of the class list on the
5064 node = dojo.byId(node);
5065 var cls = node.className;
5066 if((" "+cls+" ").indexOf(" "+classStr+" ") < 0){
5067 node.className = cls + (cls ? ' ' : '') + classStr;
5071 dojo.removeClass = function(/*DomNode|String*/node, /*String*/classStr){
5072 // summary: Removes the specified classes from node.
5073 node = dojo.byId(node);
5074 var t = dojo.trim((" " + node.className + " ").replace(" " + classStr + " ", " "));
5075 if(node.className != t){ node.className = t; }
5078 dojo.toggleClass = function(/*DomNode|String*/node, /*String*/classStr, /*Boolean?*/condition){
5080 // Adds a class to node if not present, or removes if present.
5081 // Pass a boolean condition if you want to explicitly add or remove.
5083 // If passed, true means to add the class, false means to remove.
5084 if(condition === undefined){
5085 condition = !dojo.hasClass(node, classStr);
5087 dojo[condition ? "addClass" : "removeClass"](node, classStr);
5092 if(!dojo._hasResource["dojo._base.NodeList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5093 dojo._hasResource["dojo._base.NodeList"] = true;
5094 dojo.provide("dojo._base.NodeList");
5102 var tnl = function(arr){
5103 // decorate an array to make it look like a NodeList
5104 arr.constructor = dojo.NodeList;
5105 dojo._mixin(arr, dojo.NodeList.prototype);
5109 var _mapIntoDojo = function(func, alwaysThis){
5110 // returns a function which, when executed in the scope of its caller,
5111 // applies the passed arguments to a particular dojo.* function (named
5112 // in func) and aggregates the returns. if alwaysThis is true, it
5113 // always returns the scope object and not the collected returns from
5117 var aa = d._toArray(_a, 0, [null]);
5118 var s = this.map(function(i){
5120 return d[func].apply(d, aa);
5122 return (alwaysThis || ( (_a.length > 1) || !d.isString(_a[0]) )) ? this : s; // String||dojo.NodeList
5126 dojo.NodeList = function(){
5128 // dojo.NodeList is as subclass of Array which adds syntactic
5129 // sugar for chaining, common iteration operations, animation,
5130 // and node manipulation. NodeLists are most often returned as
5131 // the result of dojo.query() calls.
5133 // create a node list from a node
5134 // | new dojo.NodeList(dojo.byId("foo"));
5136 return tnl(Array.apply(null, arguments));
5139 dojo.NodeList._wrap = tnl;
5141 dojo.extend(dojo.NodeList, {
5142 // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array#Methods
5144 // FIXME: handle return values for #3244
5145 // http://trac.dojotoolkit.org/ticket/3244
5148 // need to wrap or implement:
5149 // join (perhaps w/ innerHTML/outerHTML overload for toString() of items?)
5153 slice: function(/*===== begin, end =====*/){
5155 // Returns a new NodeList, maintaining this one in place
5157 // This method behaves exactly like the Array.slice method
5158 // with the caveat that it returns a dojo.NodeList and not a
5159 // raw Array. For more details, see:
5160 // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:slice
5162 // Can be a positive or negative integer, with positive
5163 // integers noting the offset to begin at, and negative
5164 // integers denoting an offset from the end (i.e., to the left
5167 // Optional parameter to describe what position relative to
5168 // the NodeList's zero index to end the slice at. Like begin,
5169 // can be positive or negative.
5170 var a = dojo._toArray(arguments);
5171 return tnl(a.slice.apply(this, a));
5174 splice: function(/*===== index, howmany, item =====*/){
5176 // Returns a new NodeList, manipulating this NodeList based on
5177 // the arguments passed, potentially splicing in new elements
5178 // at an offset, optionally deleting elements
5180 // This method behaves exactly like the Array.splice method
5181 // with the caveat that it returns a dojo.NodeList and not a
5182 // raw Array. For more details, see:
5183 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:splice>
5185 // begin can be a positive or negative integer, with positive
5186 // integers noting the offset to begin at, and negative
5187 // integers denoting an offset from the end (i.e., to the left
5189 // howmany: Integer?
5190 // Optional parameter to describe what position relative to
5191 // the NodeList's zero index to end the slice at. Like begin,
5192 // can be positive or negative.
5194 // Any number of optional parameters may be passed in to be
5195 // spliced into the NodeList
5198 var a = dojo._toArray(arguments);
5199 return tnl(a.splice.apply(this, a));
5202 concat: function(/*===== item =====*/){
5204 // Returns a new NodeList comprised of items in this NodeList
5205 // as well as items passed in as parameters
5207 // This method behaves exactly like the Array.concat method
5208 // with the caveat that it returns a dojo.NodeList and not a
5209 // raw Array. For more details, see:
5210 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:concat>
5212 // Any number of optional parameters may be passed in to be
5213 // spliced into the NodeList
5216 var a = dojo._toArray(arguments, 0, [this]);
5217 return tnl(a.concat.apply([], a));
5220 indexOf: function(/*Object*/ value, /*Integer?*/ fromIndex){
5222 // see dojo.indexOf(). The primary difference is that the acted-on
5223 // array is implicitly this NodeList
5225 // The value to search for.
5227 // The loction to start searching from. Optional. Defaults to 0.
5229 // For more details on the behavior of indexOf, see:
5230 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf>
5232 // Positive Integer or 0 for a match, -1 of not found.
5233 return d.indexOf(this, value, fromIndex); // Integer
5236 lastIndexOf: function(/*===== value, fromIndex =====*/){
5238 // see dojo.lastIndexOf(). The primary difference is that the
5239 // acted-on array is implicitly this NodeList
5241 // For more details on the behavior of lastIndexOf, see:
5242 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf>
5244 // The value to search for.
5245 // fromIndex: Integer?
5246 // The loction to start searching from. Optional. Defaults to 0.
5248 // Positive Integer or 0 for a match, -1 of not found.
5249 return d.lastIndexOf.apply(d, d._toArray(arguments, 0, [this])); // Integer
5252 every: function(/*Function*/callback, /*Object?*/thisObject){
5254 // see `dojo.every()` and:
5255 // <http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every>
5256 // Takes the same structure of arguments and returns as
5257 // dojo.every() with the caveat that the passed array is
5258 // implicitly this NodeList
5259 return d.every(this, callback, thisObject); // Boolean
5262 some: function(/*Function*/callback, /*Object?*/thisObject){
5264 // see dojo.some() and:
5265 // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some
5266 // Takes the same structure of arguments and returns as
5267 // dojo.some() with the caveat that the passed array is
5268 // implicitly this NodeList
5269 return d.some(this, callback, thisObject); // Boolean
5272 map: function(/*Function*/ func, /*Function?*/ obj){
5274 // see dojo.map(). The primary difference is that the acted-on
5275 // array is implicitly this NodeList and the return is a
5276 // dojo.NodeList (a subclass of Array)
5278 return d.map(this, func, obj, d.NodeList); // dojo.NodeList
5281 forEach: function(callback, thisObj){
5283 // see dojo.forEach(). The primary difference is that the acted-on
5284 // array is implicitly this NodeList
5286 d.forEach(this, callback, thisObj);
5287 // non-standard return to allow easier chaining
5288 return this; // dojo.NodeList
5295 // Returns the box objects all elements in a node list as
5296 // an Array (*not* a NodeList)
5298 return d.map(this, d.coords); // Array
5302 attr: function(property, value){
5304 // gets or sets the DOM attribute for every element in the
5307 // the attribute to get/set
5309 // optional. The value to set the property to
5311 // if no value is passed, the result is an array of attribute values
5312 // If a value is passed, the return is this NodeList
5315 style: function(property, value){
5317 // gets or sets the CSS property for every element in the NodeList
5319 // the CSS property to get/set, in JavaScript notation
5320 // ("lineHieght" instead of "line-height")
5322 // optional. The value to set the property to
5324 // if no value is passed, the result is an array of strings.
5325 // If a value is passed, the return is this NodeList
5328 addClass: function(className){
5330 // adds the specified class to every node in the list
5331 // className: String
5332 // the CSS class to add
5334 // dojo.NodeList, this list
5337 removeClass: function(className){
5339 // removes the specified class from every node in the list
5340 // className: String
5341 // the CSS class to add
5343 // dojo.NodeList, this list
5346 toggleClass: function(className, condition){
5348 // Adds a class to node if not present, or removes if present.
5349 // Pass a boolean condition if you want to explicitly add or remove.
5350 // condition: Boolean?
5351 // If passed, true means to add the class, false means to remove.
5352 // className: String
5353 // the CSS class to add
5354 // return: dojo.NodeList
5358 connect: function(methodName, objOrFunc, funcName){
5360 // attach event handlers to every item of the NodeList. Uses dojo.connect()
5361 // so event properties are normalized
5362 // methodName: String
5363 // the name of the method to attach to. For DOM events, this should be
5364 // the lower-case name of the event
5365 // objOrFunc: Object|Function|String
5366 // if 2 arguments are passed (methodName, objOrFunc), objOrFunc should
5367 // reference a function or be the name of the function in the global
5368 // namespace to attach. If 3 arguments are provided
5369 // (methodName, objOrFunc, funcName), objOrFunc must be the scope to
5370 // locate the bound function in
5371 // funcName: String?
5372 // optional. A string naming the function in objOrFunc to bind to the
5373 // event. May also be a function reference.
5375 // add an onclick handler to every button on the page
5376 // | dojo.query("div:nth-child(odd)").connect("onclick", function(e){
5377 // | console.debug("clicked!");
5380 // attach foo.bar() to every odd div's onmouseover
5381 // | dojo.query("div:nth-child(odd)").connect("onmouseover", foo, "bar");
5384 attr: _mapIntoDojo("attr"),
5385 style: _mapIntoDojo("style"),
5386 addClass: _mapIntoDojo("addClass", true),
5387 removeClass: _mapIntoDojo("removeClass", true),
5388 toggleClass: _mapIntoDojo("toggleClass", true),
5389 connect: _mapIntoDojo("connect", true),
5391 // FIXME: connectPublisher()? connectRunOnce()?
5393 place: function(/*String||Node*/ queryOrNode, /*String*/ position){
5395 // places elements of this node list relative to the first element matched
5396 // by queryOrNode. Returns the original NodeList.
5398 // may be a string representing any valid CSS3 selector or a DOM node.
5399 // In the selector case, only the first matching element will be used
5400 // for relative positioning.
5403 // * "last"||"end" (default)
5404 // * "first||"start"
5407 // or an offset in the childNodes property
5408 var item = d.query(queryOrNode)[0];
5409 return this.forEach(function(i){ d.place(i, item, (position||"last")); }); // dojo.NodeList
5412 orphan: function(/*String?*/ simpleFilter){
5414 // removes elements in this list that match the simple
5415 // filter from their parents and returns them as a new
5418 // single-expression CSS filter
5420 // `dojo.NodeList` the orpahned elements
5421 var orphans = simpleFilter ? d._filterQueryResult(this, simpleFilter) : this;
5422 orphans.forEach(function(item){
5423 if(item.parentNode){
5424 item.parentNode.removeChild(item);
5427 return orphans; // dojo.NodeList
5430 adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){
5432 // places any/all elements in queryOrListOrNode at a
5433 // position relative to the first element in this list.
5434 // Returns a dojo.NodeList of the adopted elements.
5435 // queryOrListOrNode:
5436 // a DOM node or a query string or a query result.
5437 // Represents the nodes to be adopted relative to the
5438 // first element of this NodeList.
5441 // * "last"||"end" (default)
5442 // * "first||"start"
5445 // or an offset in the childNodes property
5447 return d.query(queryOrListOrNode).forEach(function(ai){ d.place(ai, item, position || "last"); }); // dojo.NodeList
5450 // FIXME: do we need this?
5451 query: function(/*String*/ queryStr){
5453 // Returns a new, flattened NodeList. Elements of the new list
5454 // satisfy the passed query but use elements of the
5455 // current NodeList as query roots.
5457 if(!queryStr){ return this; }
5459 // FIXME: probably slow
5461 var ret = d.NodeList();
5462 this.forEach(function(item){
5463 d.query(queryStr, item).forEach(function(subItem){
5464 if(subItem !== undefined){
5469 return ret; // dojo.NodeList
5472 filter: function(/*String*/ simpleQuery){
5474 // "masks" the built-in javascript filter() method to support
5475 // passing a simple string filter in addition to supporting
5476 // filtering function objects.
5478 // "regular" JS filter syntax as exposed in dojo.filter:
5479 // | dojo.query("*").filter(function(item){
5480 // | // highlight every paragraph
5481 // | return (item.nodeName == "p");
5482 // | }).styles("backgroundColor", "yellow");
5484 // the same filtering using a CSS selector
5485 // | dojo.query("*").filter("p").styles("backgroundColor", "yellow");
5489 var r = d.NodeList();
5490 var rp = function(t){
5491 if(t !== undefined){
5495 if(d.isString(simpleQuery)){
5496 items = d._filterQueryResult(this, _a[0]);
5498 // if we only got a string query, pass back the filtered results
5499 return items; // dojo.NodeList
5501 // if we got a callback, run it over the filtered items
5504 // handle the (callback, [thisObject]) case
5505 d.forEach(d.filter(items, _a[0], _a[1]), rp);
5506 return r; // dojo.NodeList
5510 // FIXME: should this be "copyTo" and include parenting info?
5513 // creates node clones of each element of this list
5514 // and returns a new list containing the clones
5518 addContent: function(/*String*/ content, /*String||Integer?*/ position){
5520 // add a node or some HTML as a string to every item in the list.
5521 // Returns the original list.
5523 // a copy of the HTML content is added to each item in the
5524 // list, with an optional position argument. If no position
5525 // argument is provided, the content is appended to the end of
5528 // the HTML in string format to add at position to every item
5531 // * "last"||"end" (default)
5532 // * "first||"start"
5535 // or an offset in the childNodes property
5537 // appends content to the end if the position is ommitted
5538 // | dojo.query("h3 > p").addContent("hey there!");
5540 // add something to the front of each element that has a "thinger" property:
5541 // | dojo.query("[thinger]").addContent("...", "first");
5543 // adds a header before each element of the list
5544 // | dojo.query(".note").addContent("<h4>NOTE:</h4>", "before");
5545 var ta = d.doc.createElement("span");
5546 if(d.isString(content)){
5547 ta.innerHTML = content;
5549 ta.appendChild(content);
5551 if(position === undefined){
5554 var ct = (position == "first" || position == "after") ? "lastChild" : "firstChild";
5555 this.forEach(function(item){
5556 var tn = ta.cloneNode(true);
5558 d.place(tn[ct], item, position);
5561 return this; // dojo.NodeList
5566 // clears all content from each node in the list
5567 return this.forEach("item.innerHTML='';"); // dojo.NodeList
5569 // FIXME: should we be checking for and/or disposing of widgets below these nodes?
5572 instantiate: function(/*String|Object*/ declaredClass, /*Object?*/ properties){
5574 // Create a new instance of a specified class, using the
5575 // specified properties and each node in the nodeList as a
5578 var c = d.isFunction(declaredClass) ? declaredClass : d.getObject(declaredClass);
5579 return this.forEach(function(i){
5580 new c(properties||{},i);
5586 // syntactic sugar for DOM events
5588 "blur", "focus", "click", "keydown", "keypress", "keyup", "mousedown",
5589 "mouseenter", "mouseleave", "mousemove", "mouseout", "mouseover",
5593 dojo.NodeList.prototype[_oe] = function(a, b){
5594 return this.connect(_oe, a, b);
5596 // FIXME: should these events trigger publishes?
5598 return (a ? this.connect(_oe, a, b) :
5599 this.forEach(function(n){
5601 // listeners get buried by
5602 // addEventListener and can't be dug back
5603 // out to be triggered externally.
5605 // http://developer.mozilla.org/en/docs/DOM:element
5607 console.debug(n, evt, _oe);
5609 // FIXME: need synthetic event support!
5610 var _e = { target: n, faux: true, type: evt };
5611 // dojo._event_listener._synthesizeEvent({}, { target: n, faux: true, type: evt });
5612 try{ n[evt](_e); }catch(e){ console.debug(e); }
5613 try{ n[_oe](_e); }catch(e){ console.debug(e); }
5625 if(!dojo._hasResource["dojo._base.query"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
5626 dojo._hasResource["dojo._base.query"] = true;
5627 dojo.provide("dojo._base.query");
5631 dojo.query() architectural overview:
5633 dojo.query is a relatively full-featured CSS3 query library. It is
5634 designed to take any valid CSS3 selector and return the nodes matching
5635 the selector. To do this quickly, it processes queries in several
5636 steps, applying caching where profitable.
5638 The steps (roughly in reverse order of the way they appear in the code):
5639 1.) check to see if we already have a "query dispatcher"
5640 - if so, use that with the given parameterization. Skip to step 4.
5641 2.) attempt to determine which branch to dispatch the query to:
5642 - JS (optimized DOM iteration)
5643 - xpath (for browsers that support it and where it's fast)
5644 - native (not available in any browser yet)
5645 3.) tokenize and convert to executable "query dispatcher"
5646 - this is where the lion's share of the complexity in the
5647 system lies. In the DOM version, the query dispatcher is
5648 assembled as a chain of "yes/no" test functions pertaining to
5649 a section of a simple query statement (".blah:nth-child(odd)"
5650 but not "div div", which is 2 simple statements). Individual
5651 statement dispatchers are cached (to prevent re-definition)
5652 as are entire dispatch chains (to make re-execution of the
5654 - in the xpath path, tokenization yeilds a concatenation of
5655 parameterized xpath selectors. As with the DOM version, both
5656 simple selector blocks and overall evaluators are cached to
5657 prevent re-defintion
5658 4.) the resulting query dispatcher is called in the passed scope (by default the top-level document)
5659 - for DOM queries, this results in a recursive, top-down
5660 evaluation of nodes based on each simple query section
5661 - xpath queries can, thankfully, be executed in one shot
5662 5.) matched nodes are pruned to ensure they are unique
5666 // define everything in a closure for compressability reasons. "d" is an
5667 // alias to "dojo" since it's so frequently used. This seems a
5668 // transformation that the build system could perform on a per-file basis.
5670 ////////////////////////////////////////////////////////////////////////
5672 ////////////////////////////////////////////////////////////////////////
5675 var childNodesName = dojo.isIE ? "children" : "childNodes";
5676 var caseSensitive = false;
5678 var getQueryParts = function(query){
5679 // summary: state machine for query tokenization
5680 if(">~+".indexOf(query.charAt(query.length-1)) >= 0){
5683 query += " "; // ensure that we terminate the state machine
5685 var ts = function(s, e){
5686 return d.trim(query.slice(s, e));
5689 // the overall data graph of the full query, as represented by queryPart objects
5691 // state keeping vars
5692 var inBrackets = -1;
5694 var inMatchFor = -1;
5699 var lc = ""; // the last character
5700 var cc = ""; // the current character
5703 var x = 0; // index in the query
5704 var ql = query.length;
5705 var currentPart = null; // data structure representing the entire clause
5706 var _cp = null; // the current pseudo or attr matcher
5708 var endTag = function(){
5710 var tv = (inTag == x) ? null : ts(inTag, x).toLowerCase();
5711 currentPart[ (">~+".indexOf(tv) < 0) ? "tag" : "oper" ] = tv;
5716 var endId = function(){
5718 currentPart.id = ts(inId, x).replace(/\\/g, "");
5723 var endClass = function(){
5725 currentPart.classes.push(ts(inClass+1, x).replace(/\\/g, ""));
5730 var endAll = function(){
5731 endId(); endTag(); endClass();
5734 for(; lc=cc, cc=query.charAt(x),x<ql; x++){
5735 if(lc == "\\"){ continue; }
5737 // NOTE: I hate all this alloc, but it's shorter than writing tons of if's
5751 if(inBrackets >= 0){
5752 // look for a the close first
5755 _cp.attr = ts(inBrackets+1, x);
5757 _cp.matchFor = ts((inMatchFor||inBrackets+1), x);
5759 var cmf = _cp.matchFor;
5761 if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){
5762 _cp.matchFor = cmf.substring(1, cmf.length-1);
5765 currentPart.attrs.push(_cp);
5766 _cp = null; // necessaray?
5767 inBrackets = inMatchFor = -1;
5768 }else if(cc == "="){
5769 var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";
5770 _cp.type = addToCc+cc;
5771 _cp.attr = ts(inBrackets+1, x-addToCc.length);
5774 // now look for other clause parts
5775 }else if(inParens >= 0){
5778 _cp.value = ts(inParens+1, x);
5780 inPseudo = inParens = -1;
5782 }else if(cc == "#"){
5785 }else if(cc == "."){
5788 }else if(cc == ":"){
5791 }else if(cc == "["){
5796 attr: null, type: null, matchFor: null
5799 }else if(cc == "("){
5802 name: ts(inPseudo+1, x),
5805 currentPart.pseudos.push(_cp);
5808 }else if(cc == " " && lc != cc){
5809 // note that we expect the string to be " " terminated
5812 currentPart.pseudos.push({ name: ts(inPseudo+1, x) });
5814 currentPart.hasLoops = (
5815 currentPart.pseudos.length ||
5816 currentPart.attrs.length ||
5817 currentPart.classes.length );
5818 currentPart.query = ts(pStart, x);
5819 currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*");
5820 qparts.push(currentPart);
5828 ////////////////////////////////////////////////////////////////////////
5830 ////////////////////////////////////////////////////////////////////////
5832 // this array is a lookup used to generate an attribute matching function.
5833 // There is a similar lookup/generator list for the DOM branch with similar
5834 // calling semantics.
5836 "*=": function(attr, value){
5837 return "[contains(@"+attr+", '"+ value +"')]";
5839 "^=": function(attr, value){
5840 return "[starts-with(@"+attr+", '"+ value +"')]";
5842 "$=": function(attr, value){
5843 return "[substring(@"+attr+", string-length(@"+attr+")-"+(value.length-1)+")='"+value+"']";
5845 "~=": function(attr, value){
5846 return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
5848 "|=": function(attr, value){
5849 return "[contains(concat(' ',@"+attr+",' '), ' "+ value +"-')]";
5851 "=": function(attr, value){
5852 return "[@"+attr+"='"+ value +"']";
5856 // takes a list of attribute searches, the overall query, a function to
5857 // generate a default matcher, and a closure-bound method for providing a
5858 // matching function that generates whatever type of yes/no distinguisher
5859 // the query method needs. The method is a bit tortured and hard to read
5860 // because it needs to be used in both the XPath and DOM branches.
5861 var handleAttrs = function( attrList,
5865 d.forEach(query.attrs, function(attr){
5867 // type, attr, matchFor
5868 if(attr.type && attrList[attr.type]){
5869 matcher = attrList[attr.type](attr.attr, attr.matchFor);
5870 }else if(attr.attr.length){
5871 matcher = getDefault(attr.attr);
5873 if(matcher){ handleMatch(matcher); }
5877 var buildPath = function(query){
5879 var qparts = getQueryParts(d.trim(query));
5880 while(qparts.length){
5881 var tqp = qparts.shift();
5884 if(tqp.oper == ">"){
5886 // prefix = "/child::*";
5887 tqp = qparts.shift();
5888 }else if(tqp.oper == "~"){
5889 prefix = "/following-sibling::"; // get element following siblings
5890 tqp = qparts.shift();
5891 }else if(tqp.oper == "+"){
5893 // fails when selecting subsequent siblings by node type
5894 // because the position() checks the position in the list
5895 // of matching elements and not the localized siblings
5896 prefix = "/following-sibling::";
5897 postfix = "[position()=1]";
5898 tqp = qparts.shift();
5901 // prefix = "/descendant::*"
5904 // get the tag name (if any)
5906 xpath += prefix + tqp.tag + postfix;
5908 // check to see if it's got an id. Needs to come first in xpath.
5910 xpath += "[@id='"+tqp.id+"'][1]";
5913 d.forEach(tqp.classes, function(cn){
5914 var cnl = cn.length;
5916 if(cn.charAt(cnl-1) == "*"){
5917 padding = ""; cn = cn.substr(0, cnl-1);
5920 "[contains(concat(' ',@class,' '), ' "+
5921 cn + padding + "')]";
5924 handleAttrs(xPathAttrs, tqp,
5925 function(condition){
5926 return "[@"+condition+"]";
5933 // FIXME: need to implement pseudo-class checks!!
5938 var _xpathFuncCache = {};
5939 var getXPathFunc = function(path){
5940 if(_xpathFuncCache[path]){
5941 return _xpathFuncCache[path];
5945 // don't need to memoize. The closure scope handles it for us.
5946 var xpath = buildPath(path);
5948 var tf = function(parent){
5949 // XPath query strings are memoized.
5953 xpathResult = doc.evaluate(xpath, parent, null,
5954 // XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
5955 XPathResult.ANY_TYPE, null);
5957 console.debug("failure in exprssion:", xpath, "under:", parent);
5960 var result = xpathResult.iterateNext();
5963 result = xpathResult.iterateNext();
5967 return _xpathFuncCache[path] = tf;
5971 d.xPathMatch = function(query){
5972 // XPath based DOM query system. Handles a small subset of CSS
5973 // selectors, subset is identical to the non-XPath version of this
5976 return getXPathFunc(query)();
5980 ////////////////////////////////////////////////////////////////////////
5982 ////////////////////////////////////////////////////////////////////////
5984 var _filtersCache = {};
5985 var _simpleFiltersCache = {};
5987 // the basic building block of the yes/no chaining system. agree(f1, f2)
5988 // generates a new function which returns the boolean results of both of
5989 // the passed functions to a single logical-anded result.
5990 var agree = function(first, second){
5991 if(!first){ return second; }
5992 if(!second){ return first; }
5995 return first.apply(window, arguments) && second.apply(window, arguments);
5999 var _childElements = function(root){
6001 var te, x=0, tret = root[childNodesName];
6002 while(te=tret[x++]){
6003 if(te.nodeType == 1){ ret.push(te); }
6008 var _nextSiblings = function(root, single){
6011 while(te = te.nextSibling){
6012 if(te.nodeType == 1){
6014 if(single){ break; }
6020 var _filterDown = function(element, queryParts, matchArr, idx){
6022 // in the fast path! this function is called recursively and for
6023 // every run of a query.
6025 var isFinal = (queryParts.length == nidx);
6026 var tqp = queryParts[idx];
6028 // see if we can constrain our next level to direct children
6030 var ecn = (tqp.oper == ">") ?
6031 _childElements(element) :
6032 _nextSiblings(element, (tqp.oper == "+"));
6034 if(!ecn || !ecn.length){
6038 isFinal = (queryParts.length == nidx);
6039 // kinda janky, too much array alloc
6040 var tf = getFilterFunc(queryParts[idx+1]);
6041 // for(var x=ecn.length-1, te; x>=0, te=ecn[x]; x--){
6042 for(var x=0, ecnl=ecn.length, te; x<ecnl, te=ecn[x]; x++){
6047 _filterDown(te, queryParts, matchArr, nidx);
6058 // otherwise, keep going down, unless we'er at the end
6059 var candidates = getElementsFunc(tqp)(element);
6061 while(candidates.length){
6062 matchArr.push(candidates.shift());
6065 candidates.unshift(0, matchArr.length-1);
6066 matchArr.splice.apply(matchArr, candidates);
6069 // if we're not yet at the bottom, keep going!
6070 while(candidates.length){
6071 _filterDown(candidates.shift(), queryParts, matchArr, nidx);
6076 var filterDown = function(elements, queryParts){
6079 // for every root, get the elements that match the descendant selector
6080 // for(var x=elements.length-1, te; x>=0, te=elements[x]; x--){
6081 var x = elements.length - 1, te;
6082 while(te = elements[x--]){
6083 _filterDown(te, queryParts, ret, 0);
6088 var getFilterFunc = function(q){
6089 // note: query can't have spaces!
6090 if(_filtersCache[q.query]){
6091 return _filtersCache[q.query];
6095 // does it have a tagName component?
6100 return (elem.nodeType == 1);
6108 (elem.nodeType == 1) &&
6109 (q.tag == elem.tagName.toLowerCase())
6117 // does the node have an ID?
6122 (elem.nodeType == 1) &&
6130 // if we have other query param parts, make sure we add them to the
6132 ff = agree(ff, getSimpleFilterFunc(q));
6135 return _filtersCache[q.query] = ff;
6138 var getNodeIndex = function(node){
6140 // we could have a more accurate caching mechanism by invalidating
6141 // caches after the query has finished, but I think that'd lead to
6142 // significantly more cache churn than the cache would provide
6143 // value for in the common case. Generally, we're more
6144 // conservative (and therefore, more accurate) than jQuery and
6145 // DomQuery WRT node node indexes, but there may be corner cases
6146 // in which we fall down. How much we care about them is TBD.
6148 var pn = node.parentNode;
6149 var pnc = pn.childNodes;
6151 // check to see if we can trust the cache. If not, re-key the whole
6152 // thing and return our node match from that.
6155 var child = pn.firstChild;
6160 var ci = node["__cachedIndex"];
6161 var cl = pn["__cachedLength"];
6163 // only handle cache building if we've gone out of sync
6164 if(((typeof cl == "number")&&(cl != pnc.length))||(typeof ci != "number")){
6165 // rip though the whole set, building cache indexes as we go
6166 pn["__cachedLength"] = pnc.length;
6169 // we only assign indexes for nodes with nodeType == 1, as per:
6170 // http://www.w3.org/TR/css3-selectors/#nth-child-pseudo
6171 // only elements are counted in the search order, and they
6172 // begin at 1 for the first child's index
6177 if(child.nodeType == 1){
6178 child["__cachedIndex"] = idx;
6181 child = child.nextSibling;
6185 // could be incorrect in some cases (node swaps involving the
6186 // passed node, etc.), but we ignore those due to the relative
6187 // unlikelihood of that occuring
6196 var _getAttr = function(elem, attr){
6197 if(attr == "class"){
6198 return elem.className || blank;
6201 return elem.htmlFor || blank;
6203 return elem.getAttribute(attr, 2) || blank;
6207 "*=": function(attr, value){
6208 return function(elem){
6210 // an E element whose "foo" attribute value contains
6211 // the substring "bar"
6212 return (_getAttr(elem, attr).indexOf(value)>=0);
6215 "^=": function(attr, value){
6217 // an E element whose "foo" attribute value begins exactly
6218 // with the string "bar"
6219 return function(elem){
6220 return (_getAttr(elem, attr).indexOf(value)==0);
6223 "$=": function(attr, value){
6225 // an E element whose "foo" attribute value ends exactly
6226 // with the string "bar"
6227 var tval = " "+value;
6228 return function(elem){
6229 var ea = " "+_getAttr(elem, attr);
6230 return (ea.lastIndexOf(value)==(ea.length-value.length));
6233 "~=": function(attr, value){
6235 // an E element whose "foo" attribute value is a list of
6236 // space-separated values, one of which is exactly equal
6239 // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
6240 var tval = " "+value+" ";
6241 return function(elem){
6242 var ea = " "+_getAttr(elem, attr)+" ";
6243 return (ea.indexOf(tval)>=0);
6246 "|=": function(attr, value){
6247 // E[hreflang|="en"]
6248 // an E element whose "hreflang" attribute has a
6249 // hyphen-separated list of values beginning (from the
6251 var valueDash = " "+value+"-";
6252 return function(elem){
6253 var ea = " "+(elem.getAttribute(attr, 2) || "");
6256 (ea.indexOf(valueDash)==0)
6260 "=": function(attr, value){
6261 return function(elem){
6262 return (_getAttr(elem, attr) == value);
6268 "first-child": function(name, condition){
6269 return function(elem){
6270 if(elem.nodeType != 1){ return false; }
6271 // check to see if any of the previous siblings are elements
6272 var fc = elem.previousSibling;
6273 while(fc && (fc.nodeType != 1)){
6274 fc = fc.previousSibling;
6279 "last-child": function(name, condition){
6280 return function(elem){
6281 if(elem.nodeType != 1){ return false; }
6282 // check to see if any of the next siblings are elements
6283 var nc = elem.nextSibling;
6284 while(nc && (nc.nodeType != 1)){
6285 nc = nc.nextSibling;
6290 "empty": function(name, condition){
6291 return function(elem){
6292 // DomQuery and jQuery get this wrong, oddly enough.
6293 // The CSS 3 selectors spec is pretty explicit about
6295 var cn = elem.childNodes;
6296 var cnl = elem.childNodes.length;
6297 // if(!cnl){ return true; }
6298 for(var x=cnl-1; x >= 0; x--){
6299 var nt = cn[x].nodeType;
6300 if((nt == 1)||(nt == 3)){ return false; }
6305 "contains": function(name, condition){
6306 return function(elem){
6307 // FIXME: I dislike this version of "contains", as
6308 // whimsical attribute could set it off. An inner-text
6309 // based version might be more accurate, but since
6310 // jQuery and DomQuery also potentially get this wrong,
6311 // I'm leaving it for now.
6312 return (elem.innerHTML.indexOf(condition) >= 0);
6315 "not": function(name, condition){
6316 var ntf = getFilterFunc(getQueryParts(condition)[0]);
6317 return function(elem){
6318 return (!ntf(elem));
6321 "nth-child": function(name, condition){
6323 if(condition == "odd"){
6324 return function(elem){
6326 ((getNodeIndex(elem)) % 2) == 1
6329 }else if((condition == "2n")||
6330 (condition == "even")){
6331 return function(elem){
6332 return ((getNodeIndex(elem) % 2) == 0);
6334 }else if(condition.indexOf("0n+") == 0){
6335 var ncount = pi(condition.substr(3));
6336 return function(elem){
6337 return (elem.parentNode[childNodesName][ncount-1] === elem);
6339 }else if( (condition.indexOf("n+") > 0) &&
6340 (condition.length > 3) ){
6341 var tparts = condition.split("n+", 2);
6342 var pred = pi(tparts[0]);
6343 var idx = pi(tparts[1]);
6344 return function(elem){
6345 return ((getNodeIndex(elem) % pred) == idx);
6347 }else if(condition.indexOf("n") == -1){
6348 var ncount = pi(condition);
6349 return function(elem){
6350 return (getNodeIndex(elem) == ncount);
6356 var defaultGetter = (d.isIE) ? function(cond){
6357 var clc = cond.toLowerCase();
6358 return function(elem){
6359 return elem[cond]||elem[clc];
6362 return function(elem){
6363 return (elem && elem.getAttribute && elem.hasAttribute(cond));
6367 var getSimpleFilterFunc = function(query){
6369 var fcHit = (_simpleFiltersCache[query.query]||_filtersCache[query.query]);
6370 if(fcHit){ return fcHit; }
6374 // the only case where we'll need the tag name is if we came from an ID query
6375 if(query.id){ // do we have an ID component?
6376 if(query.tag != "*"){
6377 ff = agree(ff, function(elem){
6378 return (elem.tagName.toLowerCase() == query.tag);
6383 // if there's a class in our query, generate a match function for it
6384 d.forEach(query.classes, function(cname, idx, arr){
6385 // get the class name
6386 var isWildcard = cname.charAt(cname.length-1) == "*";
6388 cname = cname.substr(0, cname.length-1);
6390 // I dislike the regex thing, even if memozied in a cache, but it's VERY short
6391 var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)");
6392 ff = agree(ff, function(elem){
6393 return re.test(elem.className);
6398 d.forEach(query.pseudos, function(pseudo){
6399 if(pseudos[pseudo.name]){
6400 ff = agree(ff, pseudos[pseudo.name](pseudo.name, pseudo.value));
6404 handleAttrs(attrs, query, defaultGetter,
6405 function(tmatcher){ ff = agree(ff, tmatcher); }
6408 ff = function(){ return true; };
6410 return _simpleFiltersCache[query.query] = ff;
6413 var _getElementsFuncCache = { };
6415 var getElementsFunc = function(query, root){
6416 var fHit = _getElementsFuncCache[query.query];
6417 if(fHit){ return fHit; }
6419 // NOTE: this function is in the fast path! not memoized!!!
6421 // the query doesn't contain any spaces, so there's only so many
6422 // things it could be
6424 if(query.id && !query.hasLoops && !query.tag){
6425 // ID-only query. Easy.
6426 return _getElementsFuncCache[query.query] = function(root){
6427 // FIXME: if root != document, check for parenting!
6428 return [ d.byId(query.id) ];
6432 var filterFunc = getSimpleFilterFunc(query);
6435 if(query.tag && query.id && !query.hasLoops){
6436 // we got a filtered ID search (e.g., "h4#thinger")
6437 retFunc = function(root){
6438 var te = d.byId(query.id);
6446 if(!query.hasLoops){
6447 // it's just a plain-ol elements-by-tag-name query from the root
6448 retFunc = function(root){
6450 var te, x=0, tret = root.getElementsByTagName(query.tag);
6451 while(te=tret[x++]){
6457 retFunc = function(root){
6459 var te, x=0, tret = root.getElementsByTagName(query.tag);
6460 while(te=tret[x++]){
6469 return _getElementsFuncCache[query.query] = retFunc;
6472 var _partsCache = {};
6474 ////////////////////////////////////////////////////////////////////////
6476 ////////////////////////////////////////////////////////////////////////
6478 // this is the second level of spliting, from full-length queries (e.g.,
6479 // "div.foo .bar") into simple query expressions (e.g., ["div.foo",
6481 var _queryFuncCache = {
6487 return root.getElementsByTagName("*");
6490 "+": function(root){ return _nextSiblings(root, true); },
6494 var getStepQueryFunc = function(query){
6495 // if it's trivial, get a fast-path dispatcher
6496 var qparts = getQueryParts(d.trim(query));
6497 // if(query[query.length-1] == ">"){ query += " *"; }
6498 if(qparts.length == 1){
6499 var tt = getElementsFunc(qparts[0]);
6504 // otherwise, break it up and return a runner that iterates over the parts recursively
6505 var sqf = function(root){
6506 var localQueryParts = qparts.slice(0); // clone the src arr
6508 if(localQueryParts[0].oper == ">"){ // FIXME: what if it's + or ~?
6509 candidates = [ root ];
6512 candidates = getElementsFunc(localQueryParts.shift())(root);
6514 return filterDown(candidates, localQueryParts);
6519 // a specialized method that implements our primoridal "query optimizer".
6520 // This allows us to dispatch queries to the fastest subsystem we can get.
6521 var _getQueryFunc = (
6523 // XPath on the Webkit nighlies is slower than it's DOM iteration
6524 // for most test cases
6526 // we should try to capture some runtime speed data for each query
6527 // function to determine on the fly if we should stick w/ the
6528 // potentially optimized variant or if we should try something
6530 (document["evaluate"] && !d.isSafari) ?
6532 // has xpath support that's faster than DOM
6533 var qparts = query.split(" ");
6534 // can we handle it?
6535 if( (document["evaluate"])&&
6536 (query.indexOf(":") == -1)&&
6537 (query.indexOf("+") == -1) // skip direct sibling matches. See line ~344
6539 // dojo.debug(query);
6540 // should we handle it?
6542 // kind of a lame heuristic, but it works
6544 // a "div div div" style query
6545 ((qparts.length > 2)&&(query.indexOf(">") == -1))||
6546 // or something else with moderate complexity. kinda janky
6547 (qparts.length > 3)||
6548 (query.indexOf("[")>=0)||
6549 // or if it's a ".thinger" query
6550 ((1 == qparts.length)&&(0 <= query.indexOf(".")))
6553 // use get and cache a xpath runner for this selector
6554 return getXPathFunc(query);
6559 return getStepQueryFunc(query);
6560 } : getStepQueryFunc
6562 // uncomment to disable XPath for testing and tuning the DOM path
6563 // _getQueryFunc = getStepQueryFunc;
6565 // FIXME: we've got problems w/ the NodeList query()/filter() functions if we go XPath for everything
6567 // uncomment to disable DOM queries for testing and tuning XPath
6568 // _getQueryFunc = getXPathFunc;
6570 // this is the primary caching for full-query results. The query dispatcher
6571 // functions are generated here and then pickled for hash lookup in the
6573 var getQueryFunc = function(query){
6574 // return a cached version if one is available
6575 var qcz = query.charAt(0);
6576 if(d.doc["querySelectorAll"] &&
6577 ( (!d.isSafari) || (d.isSafari > 3.1) ) && // see #5832
6578 // as per CSS 3, we can't currently start w/ combinator:
6579 // http://www.w3.org/TR/css3-selectors/#w3cselgrammar
6580 (">+~".indexOf(qcz) == -1)
6582 return function(root){
6583 var r = root.querySelectorAll(query);
6584 r.nozip = true; // skip expensive duplication checks and just wrap in a NodeList
6588 if(_queryFuncCache[query]){ return _queryFuncCache[query]; }
6589 if(0 > query.indexOf(",")){
6590 // if it's not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher
6591 return _queryFuncCache[query] = _getQueryFunc(query);
6593 // if it's a complex query, break it up into it's constituent parts
6594 // and return a dispatcher that will merge the parts when run
6596 // var parts = query.split(", ");
6597 var parts = query.split(/\s*,\s*/);
6598 var tf = function(root){
6599 var pindex = 0; // avoid array alloc for every invocation
6602 while(tp = parts[pindex++]){
6603 ret = ret.concat(_getQueryFunc(tp, tp.indexOf(" "))(root));
6607 // ...cache and return
6608 return _queryFuncCache[query] = tf;
6613 // Dean's Base2 uses a system whereby queries themselves note if
6614 // they'll need duplicate filtering. We need to get on that plan!!
6616 // attempt to efficiently determine if an item in a list is a dupe,
6617 // returning a list of "uniques", hopefully in doucment order
6619 var _zip = function(arr){
6620 if(arr && arr.nozip){ return d.NodeList._wrap(arr); }
6621 var ret = new d.NodeList();
6622 if(!arr){ return ret; }
6626 if(arr.length < 2){ return ret; }
6628 arr[0]["_zipIdx"] = _zipIdx;
6629 for(var x=1, te; te = arr[x]; x++){
6630 if(arr[x]["_zipIdx"] != _zipIdx){
6633 te["_zipIdx"] = _zipIdx;
6635 // FIXME: should we consider stripping these properties?
6639 // the main executor
6640 d.query = function(/*String*/ query, /*String|DOMNode?*/ root){
6642 // Returns nodes which match the given CSS3 selector, searching the
6643 // entire document by default but optionally taking a node to scope
6644 // the search by. Returns an instance of dojo.NodeList.
6646 // dojo.query() is the swiss army knife of DOM node manipulation in
6647 // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
6648 // "$" function, dojo.query provides robust, high-performance
6649 // CSS-based node selector support with the option of scoping searches
6650 // to a particular sub-tree of a document.
6652 // Supported Selectors:
6653 // --------------------
6655 // dojo.query() supports a rich set of CSS3 selectors, including:
6657 // * class selectors (e.g., `.foo`)
6658 // * node type selectors like `span`
6659 // * ` ` descendant selectors
6660 // * `>` child element selectors
6661 // * `#foo` style ID selectors
6662 // * `*` universal selector
6663 // * `~`, the immediately preceeded-by sibling selector
6664 // * `+`, the preceeded-by sibling selector
6665 // * attribute queries:
6666 // | * `[foo]` attribute presence selector
6667 // | * `[foo='bar']` attribute value exact match
6668 // | * `[foo~='bar']` attribute value list item match
6669 // | * `[foo^='bar']` attribute start match
6670 // | * `[foo$='bar']` attribute end match
6671 // | * `[foo*='bar']` attribute substring match
6672 // * `:first-child`, `:last-child` positional selectors
6673 // * `:empty` content emtpy selector
6674 // * `:empty` content emtpy selector
6675 // * `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
6676 // * `:nth-child(even)`, `:nth-child(odd)` positional selectors
6677 // * `:not(...)` negation pseudo selectors
6679 // Any legal combination of these selectors will work with
6680 // `dojo.query()`, including compound selectors ("," delimited).
6681 // Very complex and useful searches can be constructed with this
6682 // palette of selectors and when combined with functions for
6683 // maniplation presented by dojo.NodeList, many types of DOM
6684 // manipulation operations become very straightforward.
6686 // Unsupported Selectors:
6687 // ----------------------
6689 // While dojo.query handles many CSS3 selectors, some fall outside of
6690 // what's resaonable for a programmatic node querying engine to
6691 // handle. Currently unsupported selectors include:
6693 // * namespace-differentiated selectors of any form
6694 // * all `::` pseduo-element selectors
6695 // * certain pseduo-selectors which don't get a lot of day-to-day use:
6696 // | * `:root`, `:lang()`, `:target`, `:focus`
6697 // * all visual and state selectors:
6698 // | * `:root`, `:active`, `:hover`, `:visisted`, `:link`,
6699 // `:enabled`, `:disabled`, `:checked`
6700 // * `:*-of-type` pseudo selectors
6702 // dojo.query and XML Documents:
6703 // -----------------------------
6705 // `dojo.query` currently only supports searching XML documents
6706 // whose tags and attributes are 100% lower-case. This is a known
6707 // limitation and will [be addressed soon](http://trac.dojotoolkit.org/ticket/3866)
6708 // Non-selector Queries:
6709 // ---------------------
6711 // If something other than a String is passed for the query,
6712 // `dojo.query` will return a new `dojo.NodeList` constructed from
6713 // that parameter alone and all further processing will stop. This
6714 // means that if you have a reference to a node or NodeList, you
6715 // can quickly construct a new NodeList from the original by
6716 // calling `dojo.query(node)` or `dojo.query(list)`.
6719 // The CSS3 expression to match against. For details on the syntax of
6720 // CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors>
6722 // A DOMNode (or node id) to scope the search from. Optional.
6723 // returns: dojo.NodeList
6724 // An instance of `dojo.NodeList`. Many methods are available on
6725 // NodeLists for searching, iterating, manipulating, and handling
6726 // events on the matched nodes in the returned list.
6728 // search the entire document for elements with the class "foo":
6729 // | dojo.query(".foo");
6730 // these elements will match:
6731 // | <span class="foo"></span>
6732 // | <span class="foo bar"></span>
6733 // | <p class="thud foo"></p>
6735 // search the entire document for elements with the classes "foo" *and* "bar":
6736 // | dojo.query(".foo.bar");
6737 // these elements will match:
6738 // | <span class="foo bar"></span>
6739 // while these will not:
6740 // | <span class="foo"></span>
6741 // | <p class="thud foo"></p>
6743 // find `<span>` elements which are descendants of paragraphs and
6744 // which have a "highlighted" class:
6745 // | dojo.query("p span.highlighted");
6746 // the innermost span in this fragment matches:
6747 // | <p class="foo">
6749 // | <span class="highlighted foo bar">...</span>
6753 // set an "odd" class on all odd table rows inside of the table
6754 // `#tabular_data`, using the `>` (direct child) selector to avoid
6755 // affecting any nested tables:
6756 // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
6758 // remove all elements with the class "error" from the document
6759 // and store them in a list:
6760 // | var errors = dojo.query(".error").orphan();
6762 // add an onclick handler to every submit button in the document
6763 // which causes the form to be sent via Ajax instead:
6764 // | dojo.query("input[type='submit']").onclick(function(e){
6765 // | dojo.stopEvent(e); // prevent sending the form
6766 // | var btn = e.target;
6768 // | form: btn.form,
6769 // | load: function(data){
6770 // | // replace the form with the response
6771 // | var div = dojo.doc.createElement("div");
6772 // | dojo.place(div, btn.form, "after");
6773 // | div.innerHTML = data;
6774 // | dojo.style(btn.form, "display", "none");
6780 // NOTE: elementsById is not currently supported
6781 // NOTE: ignores xpath-ish queries for now
6783 if(query.constructor == d.NodeList){
6786 if(!d.isString(query)){
6787 return new d.NodeList(query); // dojo.NodeList
6789 if(d.isString(root)){
6790 root = d.byId(root);
6793 return _zip(getQueryFunc(query)(root||d.doc)); // dojo.NodeList
6797 // exposing this was a mistake
6798 d.query.attrs = attrs;
6800 // exposing this because new pseudo matches are only executed through the
6801 // DOM query path (never through the xpath optimizing branch)
6802 d.query.pseudos = pseudos;
6804 // one-off function for filtering a NodeList based on a simple selector
6805 d._filterQueryResult = function(nodeList, simpleFilter){
6806 var tnl = new d.NodeList();
6807 var ff = (simpleFilter) ? getFilterFunc(getQueryParts(simpleFilter)[0]) : function(){ return true; };
6808 for(var x=0, te; te = nodeList[x]; x++){
6809 if(ff(te)){ tnl.push(te); }
6817 if(!dojo._hasResource["dojo._base.xhr"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
6818 dojo._hasResource["dojo._base.xhr"] = true;
6819 dojo.provide("dojo._base.xhr");
6827 function setValue(/*Object*/obj, /*String*/name, /*String*/value){
6829 // For the nameed property in object, set the value. If a value
6830 // already exists and it is a string, convert the value to be an
6832 var val = obj[name];
6833 if(_d.isString(val)){
6834 obj[name] = [val, value];
6835 }else if(_d.isArray(val)){
6842 dojo.formToObject = function(/*DOMNode||String*/ formNode){
6844 // dojo.formToObject returns the values encoded in an HTML form as
6845 // string properties in an object which it then returns. Disabled form
6846 // elements, buttons, and other non-value form elements are skipped.
6847 // Multi-select elements are returned as an array of string values.
6851 // | <form id="test_form">
6852 // | <input type="text" name="blah" value="blah">
6853 // | <input type="text" name="no_value" value="blah" disabled>
6854 // | <input type="button" name="no_value2" value="blah">
6855 // | <select type="select" multiple name="multi" size="5">
6856 // | <option value="blah">blah</option>
6857 // | <option value="thud" selected>thud</option>
6858 // | <option value="thonk" selected>thonk</option>
6862 // yields this object structure as the result of a call to
6874 var iq = "input:not([type=file]):not([type=submit]):not([type=image]):not([type=reset]):not([type=button]), select, textarea";
6875 _d.query(iq, formNode).filter(function(node){
6876 return !node.disabled && node.name;
6877 }).forEach(function(item){
6878 var _in = item.name;
6879 var type = (item.type||"").toLowerCase();
6880 if(type == "radio" || type == "checkbox"){
6881 if(item.checked){ setValue(ret, _in, item.value); }
6882 }else if(item.multiple){
6884 _d.query("option", item).forEach(function(opt){
6886 setValue(ret, _in, opt.value);
6890 setValue(ret, _in, item.value);
6891 if(type == "image"){
6892 ret[_in+".x"] = ret[_in+".y"] = ret[_in].x = ret[_in].y = 0;
6896 return ret; // Object
6899 dojo.objectToQuery = function(/*Object*/ map){
6901 // takes a name/value mapping object and returns a string representing
6902 // a URL-encoded version of that object.
6914 // yields the following query string:
6916 // | "blah=blah&multi=thud&multi=thonk"
6918 // FIXME: need to implement encodeAscii!!
6919 var enc = encodeURIComponent;
6922 for(var name in map){
6923 var value = map[name];
6924 if(value != backstop[name]){
6925 var assign = enc(name) + "=";
6926 if(_d.isArray(value)){
6927 for(var i=0; i < value.length; i++){
6928 pairs.push(assign + enc(value[i]));
6931 pairs.push(assign + enc(value));
6935 return pairs.join("&"); // String
6938 dojo.formToQuery = function(/*DOMNode||String*/ formNode){
6940 // Returns a URL-encoded string representing the form passed as either a
6941 // node or string ID identifying the form to serialize
6942 return _d.objectToQuery(_d.formToObject(formNode)); // String
6945 dojo.formToJson = function(/*DOMNode||String*/ formNode, /*Boolean?*/prettyPrint){
6947 // return a serialized JSON string from a form node or string
6948 // ID identifying the form to serialize
6949 return _d.toJson(_d.formToObject(formNode), prettyPrint); // String
6952 dojo.queryToObject = function(/*String*/ str){
6954 // returns an object representing a de-serialized query section of a
6955 // URL. Query keys with multiple values are returned in an array.
6959 // | "foo=bar&foo=baz&thinger=%20spaces%20=blah&zonk=blarg&"
6961 // results in this object structure:
6964 // | foo: [ "bar", "baz" ],
6965 // | thinger: " spaces =blah",
6969 // Note that spaces and other urlencoded entities are correctly
6972 // FIXME: should we grab the URL string if we're not passed one?
6974 var qp = str.split("&");
6975 var dec = decodeURIComponent;
6976 _d.forEach(qp, function(item){
6978 var parts = item.split("=");
6979 var name = dec(parts.shift());
6980 var val = dec(parts.join("="));
6981 if(_d.isString(ret[name])){
6982 ret[name] = [ret[name]];
6984 if(_d.isArray(ret[name])){
6985 ret[name].push(val);
6991 return ret; // Object
6997 all bind() replacement APIs take the following argument structure:
7002 // all below are optional, but must be supported in some form by
7004 timeout: 1000, // milliseconds
7005 handleAs: "text", // replaces the always-wrong "mimetype"
7010 // browser-specific, MAY be unsupported
7011 sync: true, // defaults to false
7012 form: dojo.byId("someForm")
7016 // need to block async callbacks from snatching this thread as the result
7017 // of an async callback might call another sync XHR, this hangs khtml forever
7018 // must checked by watchInFlight()
7020 dojo._blockAsync = false;
7022 dojo._contentHandlers = {
7023 "text": function(xhr){ return xhr.responseText; },
7024 "json": function(xhr){
7025 if(!dojo.config.usePlainJson){
7026 console.warn("Consider using mimetype:text/json-comment-filtered"
7027 + " to avoid potential security issues with JSON endpoints"
7028 + " (use djConfig.usePlainJson=true to turn off this message)");
7030 return (xhr.status == 204) ? undefined : _d.fromJson(xhr.responseText);
7032 "json-comment-filtered": function(xhr){
7033 // NOTE: we provide the json-comment-filtered option as one solution to
7034 // the "JavaScript Hijacking" issue noted by Fortify and others. It is
7035 // not appropriate for all circumstances.
7037 var value = xhr.responseText;
7038 var cStartIdx = value.indexOf("\/*");
7039 var cEndIdx = value.lastIndexOf("*\/");
7040 if(cStartIdx == -1 || cEndIdx == -1){
7041 throw new Error("JSON was not comment filtered");
7043 return (xhr.status == 204) ? undefined :
7044 _d.fromJson(value.substring(cStartIdx+2, cEndIdx));
7046 "javascript": function(xhr){
7047 // FIXME: try Moz and IE specific eval variants?
7048 return _d.eval(xhr.responseText);
7050 "xml": function(xhr){
7051 var result = xhr.responseXML;
7052 if(_d.isIE && (!result || window.location.protocol == "file:")){
7053 _d.forEach(["MSXML2", "Microsoft", "MSXML", "MSXML3"], function(prefix){
7055 var dom = new ActiveXObject(prefix + ".XMLDOM");
7057 dom.loadXML(xhr.responseText);
7059 }catch(e){ /* Not available. Squelch and try next one. */ }
7062 return result; // DOMDocument
7066 dojo._contentHandlers["json-comment-optional"] = function(xhr){
7067 var handlers = _d._contentHandlers;
7069 return handlers["json-comment-filtered"](xhr);
7071 return handlers["json"](xhr);
7076 dojo.__IoArgs = function(){
7078 // URL to server endpoint.
7080 // Contains properties with string values. These
7081 // properties will be serialized as name1=value2 and
7082 // passed in the request.
7083 // timeout: Integer?
7084 // Milliseconds to wait for the response. If this time
7085 // passes, the then error callbacks are called.
7087 // DOM node for a form. Used to extract the form values
7088 // and send to the server.
7089 // preventCache: Boolean?
7090 // Default is false. If true, then a
7091 // "dojo.preventCache" parameter is sent in the request
7092 // with a value that changes with each request
7093 // (timestamp). Useful only with GET-type requests.
7094 // handleAs: String?
7095 // Acceptable values depend on the type of IO
7096 // transport (see specific IO calls for more information).
7098 // function(response, ioArgs){}. response is an Object, ioArgs
7099 // is of type dojo.__IoCallbackArgs. The load function will be
7100 // called on a successful response.
7102 // function(response, ioArgs){}. response is an Object, ioArgs
7103 // is of type dojo.__IoCallbackArgs. The error function will
7104 // be called in an error case.
7105 // handle: Function?
7106 // function(response, ioArgs){}. response is an Object, ioArgs
7107 // is of type dojo.__IoCallbackArgs. The handle function will
7108 // be called in either the successful or error case. For
7109 // the load, error and handle functions, the ioArgs object
7110 // will contain the following properties:
7112 this.content = content;
7113 this.timeout = timeout;
7115 this.preventCache = preventCache;
7116 this.handleAs = handleAs;
7119 this.handle = handle;
7124 dojo.__IoCallbackArgs = function(args, xhr, url, query, handleAs, id, canDelete, json){
7126 // the original object argument to the IO call.
7127 // xhr: XMLHttpRequest
7128 // For XMLHttpRequest calls only, the
7129 // XMLHttpRequest object that was used for the
7132 // The final URL used for the call. Many times it
7133 // will be different than the original args.url
7136 // For non-GET requests, the
7137 // name1=value1&name2=value2 parameters sent up in
7140 // The final indicator on how the response will be
7143 // For dojo.io.script calls only, the internal
7144 // script ID used for the request.
7145 // canDelete: Boolean
7146 // For dojo.io.script calls only, indicates
7147 // whether the script tag that represents the
7148 // request can be deleted after callbacks have
7149 // been called. Used internally to know when
7150 // cleanup can happen on JSONP-type requests.
7152 // For dojo.io.script calls only: holds the JSON
7153 // response for JSONP-type requests. Used
7154 // internally to hold on to the JSON responses.
7155 // You should not need to access it directly --
7156 // the same object should be passed to the success
7157 // callbacks directly.
7162 this.handleAs = handleAs;
7164 this.canDelete = canDelete;
7171 dojo._ioSetArgs = function(/*dojo.__IoArgs*/args,
7172 /*Function*/canceller,
7173 /*Function*/okHandler,
7174 /*Function*/errHandler){
7176 // sets up the Deferred and ioArgs property on the Deferred so it
7177 // can be used in an io call.
7179 // The args object passed into the public io call. Recognized properties on
7180 // the args object are:
7182 // The canceller function used for the Deferred object. The function
7183 // will receive one argument, the Deferred object that is related to the
7186 // The first OK callback to be registered with Deferred. It has the opportunity
7187 // to transform the OK response. It will receive one argument -- the Deferred
7188 // object returned from this function.
7190 // The first error callback to be registered with Deferred. It has the opportunity
7191 // to do cleanup on an error. It will receive two arguments: error (the
7192 // Error object) and dfd, the Deferred object returned from this function.
7194 var ioArgs = {args: args, url: args.url};
7196 //Get values from form if requestd.
7197 var formObject = null;
7199 var form = _d.byId(args.form);
7200 //IE requires going through getAttributeNode instead of just getAttribute in some form cases,
7201 //so use it for all. See #2844
7202 var actnNode = form.getAttributeNode("action");
7203 ioArgs.url = ioArgs.url || (actnNode ? actnNode.value : null);
7204 formObject = _d.formToObject(form);
7207 // set up the query params
7211 // potentially over-ride url-provided params w/ form values
7212 miArgs.push(formObject);
7215 // stuff in content over-rides what's set by form
7216 miArgs.push(args.content);
7218 if(args.preventCache){
7219 miArgs.push({"dojo.preventCache": new Date().valueOf()});
7221 ioArgs.query = _d.objectToQuery(_d.mixin.apply(null, miArgs));
7223 // .. and the real work of getting the deferred in order, etc.
7224 ioArgs.handleAs = args.handleAs || "text";
7225 var d = new _d.Deferred(canceller);
7226 d.addCallbacks(okHandler, function(error){
7227 return errHandler(error, d);
7230 //Support specifying load, error and handle callback functions from the args.
7231 //For those callbacks, the "this" object will be the args object.
7232 //The callbacks will get the deferred result value as the
7233 //first argument and the ioArgs object as the second argument.
7235 if(ld && _d.isFunction(ld)){
7236 d.addCallback(function(value){
7237 return ld.call(args, value, ioArgs);
7240 var err = args.error;
7241 if(err && _d.isFunction(err)){
7242 d.addErrback(function(value){
7243 return err.call(args, value, ioArgs);
7246 var handle = args.handle;
7247 if(handle && _d.isFunction(handle)){
7248 d.addBoth(function(value){
7249 return handle.call(args, value, ioArgs);
7255 // FIXME: need to wire up the xhr object's abort method to something
7256 // analagous in the Deferred
7260 var _deferredCancel = function(/*Deferred*/dfd){
7261 //summary: canceller function for dojo._ioSetArgs call.
7263 dfd.canceled = true;
7264 var xhr = dfd.ioArgs.xhr;
7265 var _at = typeof xhr.abort;
7266 if(_at == "function" || _at == "unknown"){
7269 var err = new Error("xhr cancelled");
7270 err.dojoType = "cancel";
7273 var _deferredOk = function(/*Deferred*/dfd){
7274 //summary: okHandler function for dojo._ioSetArgs call.
7276 return _d._contentHandlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);
7278 var _deferError = function(/*Error*/error, /*Deferred*/dfd){
7279 //summary: errHandler function for dojo._ioSetArgs call.
7281 // console.debug("xhr error in:", dfd.ioArgs.xhr);
7282 console.debug(error);
7286 var _makeXhrDeferred = function(/*dojo.__XhrArgs*/args){
7287 //summary: makes the Deferred object for this xhr request.
7288 var dfd = _d._ioSetArgs(args, _deferredCancel, _deferredOk, _deferError);
7289 //Pass the args to _xhrObj, to allow xhr iframe proxy interceptions.
7290 dfd.ioArgs.xhr = _d._xhrObj(dfd.ioArgs.args);
7294 // avoid setting a timer per request. It degrades performance on IE
7295 // something fierece if we don't use unified loops.
7296 var _inFlightIntvl = null;
7298 var _watchInFlight = function(){
7300 // internal method that checks each inflight XMLHttpRequest to see
7301 // if it has completed or if the timeout situation applies.
7303 var now = (new Date()).getTime();
7304 // make sure sync calls stay thread safe, if this callback is called
7305 // during a sync call and this results in another sync call before the
7306 // first sync call ends the browser hangs
7307 if(!_d._blockAsync){
7308 // we need manual loop because we often modify _inFlight (and therefore 'i') while iterating
7309 // note: the second clause is an assigment on purpose, lint may complain
7310 for(var i = 0, tif; i < _inFlight.length && (tif = _inFlight[i]); i++){
7313 if(!dfd || dfd.canceled || !tif.validCheck(dfd)){
7314 _inFlight.splice(i--, 1);
7315 }else if(tif.ioCheck(dfd)){
7316 _inFlight.splice(i--, 1);
7318 }else if(dfd.startTime){
7320 if(dfd.startTime + (dfd.ioArgs.args.timeout || 0) < now){
7321 _inFlight.splice(i--, 1);
7322 var err = new Error("timeout exceeded");
7323 err.dojoType = "timeout";
7325 //Cancel the request so the io module can do appropriate cleanup.
7330 // FIXME: make sure we errback! (fixed? remove console.debug?)
7332 dfd.errback(new Error("_watchInFlightError!"));
7337 if(!_inFlight.length){
7338 clearInterval(_inFlightIntvl);
7339 _inFlightIntvl = null;
7345 dojo._ioCancelAll = function(){
7346 //summary: Cancels all pending IO requests, regardless of IO type
7347 //(xhr, script, iframe).
7349 _d.forEach(_inFlight, function(i){
7352 }catch(e){/*squelch*/}
7355 //Automatically call cancel all io calls on unload
7356 //in IE for trac issue #2357.
7358 _d.addOnUnload(_d._ioCancelAll);
7361 _d._ioWatch = function(/*Deferred*/dfd,
7362 /*Function*/validCheck,
7363 /*Function*/ioCheck,
7364 /*Function*/resHandle){
7365 //summary: watches the io request represented by dfd to see if it completes.
7367 // The Deferred object to watch.
7369 // Function used to check if the IO request is still valid. Gets the dfd
7370 // object as its only argument.
7372 // Function used to check if basic IO call worked. Gets the dfd
7373 // object as its only argument.
7375 // Function used to process response. Gets the dfd
7376 // object as its only argument.
7377 if(dfd.ioArgs.args.timeout){
7378 dfd.startTime = (new Date()).getTime();
7380 _inFlight.push({dfd: dfd, validCheck: validCheck, ioCheck: ioCheck, resHandle: resHandle});
7381 if(!_inFlightIntvl){
7382 _inFlightIntvl = setInterval(_watchInFlight, 50);
7384 _watchInFlight(); // handle sync requests
7387 var _defaultContentType = "application/x-www-form-urlencoded";
7389 var _validCheck = function(/*Deferred*/dfd){
7390 return dfd.ioArgs.xhr.readyState; //boolean
7392 var _ioCheck = function(/*Deferred*/dfd){
7393 return 4 == dfd.ioArgs.xhr.readyState; //boolean
7395 var _resHandle = function(/*Deferred*/dfd){
7396 var xhr = dfd.ioArgs.xhr;
7397 if(_d._isDocumentOk(xhr)){
7400 var err = new Error("Unable to load " + dfd.ioArgs.url + " status:" + xhr.status);
7401 err.status = xhr.status;
7402 err.responseText = xhr.responseText;
7407 var _doIt = function(/*String*/type, /*Deferred*/dfd){
7408 // IE 6 is a steaming pile. It won't let you call apply() on the native function (xhr.open).
7409 // workaround for IE6's apply() "issues"
7410 var ioArgs = dfd.ioArgs;
7411 var args = ioArgs.args;
7412 var xhr = ioArgs.xhr;
7413 xhr.open(type, ioArgs.url, args.sync !== true, args.user || undefined, args.password || undefined);
7415 for(var hdr in args.headers){
7416 if(hdr.toLowerCase() === "content-type" && !args.contentType){
7417 args.contentType = args.headers[hdr];
7419 xhr.setRequestHeader(hdr, args.headers[hdr]);
7423 // FIXME: is this appropriate for all content types?
7424 xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType);
7425 if(!args.headers || !args.headers["X-Requested-With"]){
7426 xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
7428 // FIXME: set other headers here!
7430 xhr.send(ioArgs.query);
7434 _d._ioWatch(dfd, _validCheck, _ioCheck, _resHandle);
7436 return dfd; //Deferred
7439 dojo._ioAddQueryToUrl = function(/*dojo.__IoCallbackArgs*/ioArgs){
7440 //summary: Adds query params discovered by the io deferred construction to the URL.
7441 //Only use this for operations which are fundamentally GET-type operations.
7442 if(ioArgs.query.length){
7443 ioArgs.url += (ioArgs.url.indexOf("?") == -1 ? "?" : "&") + ioArgs.query;
7444 ioArgs.query = null;
7449 dojo.declare("dojo.__XhrArgs", dojo.__IoArgs, {
7450 constructor: function(){
7452 // In addition to the properties listed for the dojo._IoArgs type,
7453 // the following properties are allowed for dojo.xhr* methods.
7454 // handleAs: String?
7455 // Acceptable values are: text (default), json, json-comment-optional,
7456 // json-comment-filtered, javascript, xml
7458 // false is default. Indicates whether the request should
7459 // be a synchronous (blocking) request.
7461 // Additional HTTP headers to send in the request.
7462 this.handleAs = handleAs;
7464 this.headers = headers;
7469 dojo.xhr = function(/*String*/ method, /*dojo.__XhrArgs*/ args, /*Boolean?*/ hasBody){
7471 // Sends an HTTP request with the given method. If the request has an
7472 // HTTP body, then pass true for hasBody. The method argument should be uppercase.
7473 // Also look at dojo.xhrGet(), xhrPost(), xhrPut() and dojo.xhrDelete() for shortcuts
7474 // for those HTTP methods. There are also methods for "raw" PUT and POST methods
7475 // via dojo.rawXhrPut() and dojo.rawXhrPost() respectively.
7476 var dfd = _makeXhrDeferred(args);
7478 _d._ioAddQueryToUrl(dfd.ioArgs);
7480 return _doIt(method, dfd); // dojo.Deferred
7483 dojo.xhrGet = function(/*dojo.__XhrArgs*/ args){
7485 // Sends an HTTP GET request to the server.
7486 return _d.xhr("GET", args); //dojo.Deferred
7489 dojo.xhrPost = function(/*dojo.__XhrArgs*/ args){
7491 // Sends an HTTP POST request to the server.
7492 return _d.xhr("POST", args, true); // dojo.Deferred
7495 dojo.rawXhrPost = function(/*dojo.__XhrArgs*/ args){
7497 // Sends an HTTP POST request to the server. In addtion to the properties
7498 // listed for the dojo.__XhrArgs type, the following property is allowed:
7500 // String. The raw data to send in the body of the POST request.
7501 var dfd = _makeXhrDeferred(args);
7502 dfd.ioArgs.query = args.postData;
7503 return _doIt("POST", dfd); // dojo.Deferred
7506 dojo.xhrPut = function(/*dojo.__XhrArgs*/ args){
7508 // Sends an HTTP PUT request to the server.
7509 return _d.xhr("PUT", args, true); // dojo.Deferred
7512 dojo.rawXhrPut = function(/*dojo.__XhrArgs*/ args){
7514 // Sends an HTTP PUT request to the server. In addtion to the properties
7515 // listed for the dojo.__XhrArgs type, the following property is allowed:
7517 // String. The raw data to send in the body of the PUT request.
7518 var dfd = _makeXhrDeferred(args);
7519 var ioArgs = dfd.ioArgs;
7521 ioArgs.query = args.putData;
7522 args.putData = null;
7524 return _doIt("PUT", dfd); // dojo.Deferred
7527 dojo.xhrDelete = function(/*dojo.__XhrArgs*/ args){
7529 // Sends an HTTP DELETE request to the server.
7530 return _d.xhr("DELETE", args); //dojo.Deferred
7534 dojo.wrapForm = function(formNode){
7536 // A replacement for FormBind, but not implemented yet.
7538 // FIXME: need to think harder about what extensions to this we might
7539 // want. What should we allow folks to do w/ this? What events to
7541 throw new Error("dojo.wrapForm not yet implemented");
7548 if(!dojo._hasResource["dojo._base.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
7549 dojo._hasResource["dojo._base.fx"] = true;
7550 dojo.provide("dojo._base.fx");
7558 Animation losely package based on Dan Pupius' work, contributed under CLA:
7559 http://pupius.co.uk/js/Toolkit.Drawing.js
7565 dojo._Line = function(/*int*/ start, /*int*/ end){
7567 // dojo._Line is the object used to generate values from a start value
7570 // Beginning value for range
7572 // Ending value for range
7575 this.getValue = function(/*float*/ n){
7576 // summary: returns the point on the line
7577 // n: a floating point number greater than 0 and less than 1
7578 return ((this.end - this.start) * n) + this.start; // Decimal
7582 d.declare("dojo._Animation", null, {
7584 // A generic animation class that fires callbacks into its handlers
7585 // object at various states. Nearly all dojo animation functions
7586 // return an instance of this method, usually without calling the
7587 // .play() method beforehand. Therefore, you will likely need to
7588 // call .play() on instances of dojo._Animation when one is
7590 constructor: function(/*Object*/ args){
7591 d.mixin(this, args);
7592 if(d.isArray(this.curve)){
7595 this.curve = new d._Line(this.curve[0], this.curve[1]);
7599 // duration: Integer
7600 // The time in milliseonds the animation will take to run
7604 // curve: dojo._Line||Array
7605 // A two element array of start and end values, or a dojo._Line instance to be
7606 // used in the Animation.
7610 // A Function to adjust the acceleration (or deceleration) of the progress
7611 // across a dojo._Line
7616 // The number of times to loop the animation
7620 // the time in milliseconds to wait before advancing to next frame
7621 // (used as a fps timer: rate/1000 = fps)
7622 rate: 10 /* 100 fps */,
7626 // The time in milliseconds to wait before starting animation after it has been .play()'ed
7631 // beforeBegin: Event
7632 // Synthetic event fired before a dojo._Animation begins playing (synchronous)
7636 // Synthetic event fired as a dojo._Animation begins playing (useful?)
7640 // Synthetic event fired at each interval of a dojo._Animation
7644 // Synthetic event fired after the final frame of a dojo._Animation
7648 // Synthetic event fired any time a dojo._Animation is play()'ed
7652 // Synthetic event fired when a dojo._Animation is paused
7656 // Synthetic event fires when a dojo._Animation is stopped
7662 _startRepeatCount: 0,
7664 _fire: function(/*Event*/ evt, /*Array?*/ args){
7666 // Convenience function. Fire event "evt" and pass it the
7667 // arguments specified in "args".
7669 // The event to fire.
7671 // The arguments to pass to the event.
7674 this[evt].apply(this, args||[]);
7677 // squelch and log because we shouldn't allow exceptions in
7678 // synthetic event handlers to cause the internal timer to run
7679 // amuck, potentially pegging the CPU. I'm not a fan of this
7680 // squelch, but hopefully logging will make it clear what's
7682 console.error("exception in animation handler for:", evt);
7685 return this; // dojo._Animation
7688 play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
7690 // Start the animation.
7692 // How many milliseconds to delay before starting.
7694 // If true, starts the animation from the beginning; otherwise,
7695 // starts it from its current position.
7699 _t._active = _t._paused = false;
7701 }else if(_t._active && !_t._paused){
7702 return _t; // dojo._Animation
7705 _t._fire("beforeBegin");
7707 var de = delay||_t.delay;
7708 var _p = dojo.hitch(_t, "_play", gotoStart);
7711 return _t; // dojo._Animation
7717 _play: function(gotoStart){
7719 _t._startTime = new Date().valueOf();
7721 _t._startTime -= _t.duration * _t._percent;
7723 _t._endTime = _t._startTime + _t.duration;
7728 var value = _t.curve.getValue(_t._percent);
7730 if(!_t._startRepeatCount){
7731 _t._startRepeatCount = _t.repeat;
7733 _t._fire("onBegin", [value]);
7736 _t._fire("onPlay", [value]);
7739 return _t; // dojo._Animation
7743 // summary: Pauses a running animation.
7745 if(!this._active){ return this; /*dojo._Animation*/ }
7746 this._paused = true;
7747 this._fire("onPause", [this.curve.getValue(this._percent)]);
7748 return this; // dojo._Animation
7751 gotoPercent: function(/*Decimal*/ percent, /*Boolean?*/ andPlay){
7753 // Sets the progress of the animation.
7755 // A percentage in decimal notation (between and including 0.0 and 1.0).
7757 // If true, play the animation after setting the progress.
7759 this._active = this._paused = true;
7760 this._percent = percent;
7761 if(andPlay){ this.play(); }
7762 return this; // dojo._Animation
7765 stop: function(/*boolean?*/ gotoEnd){
7766 // summary: Stops a running animation.
7767 // gotoEnd: If true, the animation will end.
7768 if(!this._timer){ return this; /* dojo._Animation */ }
7773 this._fire("onStop", [this.curve.getValue(this._percent)]);
7774 this._active = this._paused = false;
7775 return this; // dojo._Animation
7779 // summary: Returns a string token representation of the status of
7780 // the animation, one of: "paused", "playing", "stopped"
7782 return this._paused ? "paused" : "playing"; // String
7784 return "stopped"; // String
7790 var curr = new Date().valueOf();
7791 var step = (curr - _t._startTime) / (_t._endTime - _t._startTime);
7800 step = _t.easing(step);
7803 _t._fire("onAnimate", [_t.curve.getValue(step)]);
7805 if(_t._percent < 1){
7812 _t.play(null, true);
7813 }else if(_t.repeat == -1){
7814 _t.play(null, true);
7816 if(_t._startRepeatCount){
7817 _t.repeat = _t._startRepeatCount;
7818 _t._startRepeatCount = 0;
7826 return _t; // dojo._Animation
7831 var _globalTimerList = [];
7836 dojo._Animation.prototype._startTimer = function(){
7837 // this._timer = setTimeout(dojo.hitch(this, "_cycle"), this.rate);
7839 this._timer = d.connect(runner, "run", this, "_cycle");
7843 timer = setInterval(d.hitch(runner, "run"), this.rate);
7847 dojo._Animation.prototype._stopTimer = function(){
7849 d.disconnect(this._timer);
7854 clearInterval(timer);
7860 var _makeFadeable = (d.isIE) ? function(node){
7861 // only set the zoom if the "tickle" value would be the same as the
7863 var ns = node.style;
7864 if(!ns.zoom.length && d.style(node, "zoom") == "normal"){
7865 // make sure the node "hasLayout"
7866 // NOTE: this has been tested with larger and smaller user-set text
7867 // sizes and works fine
7869 // node.style.zoom = "normal";
7871 // don't set the width to auto if it didn't already cascade that way.
7872 // We don't want to f anyones designs
7873 if(!ns.width.length && d.style(node, "width") == "auto"){
7878 dojo._fade = function(/*Object*/ args){
7880 // Returns an animation that will fade the node defined by
7881 // args.node from the start to end values passed (args.start
7882 // args.end) (end is mandatory, start is optional)
7884 args.node = d.byId(args.node);
7885 var fArgs = d.mixin({ properties: {} }, args);
7886 var props = (fArgs.properties.opacity = {});
7887 props.start = !("start" in fArgs) ?
7889 return Number(d.style(fArgs.node, "opacity"));
7891 props.end = fArgs.end;
7893 var anim = d.animateProperty(fArgs);
7894 d.connect(anim, "beforeBegin", d.partial(_makeFadeable, fArgs.node));
7896 return anim; // dojo._Animation
7900 dojo.__FadeArgs = function(node, duration, easing){
7901 // node: DOMNode|String
7902 // The node referenced in the animation
7903 // duration: Integer?
7904 // Duration of the animation in milliseconds.
7905 // easing: Function?
7906 // An easing function.
7908 this.duration = duration;
7909 this.easing = easing;
7913 dojo.fadeIn = function(/*dojo.__FadeArgs*/ args){
7915 // Returns an animation that will fade node defined in 'args' from
7916 // its current opacity to fully opaque.
7917 return d._fade(d.mixin({ end: 1 }, args)); // dojo._Animation
7920 dojo.fadeOut = function(/*dojo.__FadeArgs*/ args){
7922 // Returns an animation that will fade node defined in 'args'
7923 // from its current opacity to fully transparent.
7924 return d._fade(d.mixin({ end: 0 }, args)); // dojo._Animation
7927 dojo._defaultEasing = function(/*Decimal?*/ n){
7928 // summary: The default easing function for dojo._Animation(s)
7929 return 0.5 + ((Math.sin((n + 1.5) * Math.PI))/2);
7932 var PropLine = function(properties){
7933 // PropLine is an internal class which is used to model the values of
7934 // an a group of CSS properties across an animation lifecycle. In
7935 // particular, the "getValue" function handles getting interpolated
7936 // values between start and end for a particular CSS value.
7937 this._properties = properties;
7938 for(var p in properties){
7939 var prop = properties[p];
7940 if(prop.start instanceof d.Color){
7941 // create a reusable temp color object to keep intermediate results
7942 prop.tempColor = new d.Color();
7945 this.getValue = function(r){
7947 for(var p in this._properties){
7948 var prop = this._properties[p];
7949 var start = prop.start;
7950 if(start instanceof d.Color){
7951 ret[p] = d.blendColors(start, prop.end, r, prop.tempColor).toCss();
7952 }else if(!d.isArray(start)){
7953 ret[p] = ((prop.end - start) * r) + start + (p != "opacity" ? prop.units||"px" : "");
7961 dojo.declare("dojo.__AnimArgs", [dojo.__FadeArgs], {
7962 // Properties: Object?
7963 // A hash map of style properties to Objects describing the transition,
7964 // such as the properties of dojo._Line with an additional 'unit' property
7967 //TODOC: add event callbacks
7971 dojo.animateProperty = function(/*dojo.__AnimArgs*/ args){
7973 // Returns an animation that will transition the properties of
7974 // node defined in 'args' depending how they are defined in
7975 // 'args.properties'
7978 // dojo.animateProperty is the foundation of most dojo.fx
7979 // animations. It takes an object of "properties" corresponding to
7980 // style properties, and animates them in parallel over a set
7984 // A simple animation that changes the width of the specified node.
7985 // | dojo.animateProperty({
7986 // | node: "nodeId",
7987 // | properties: { width: 400 },
7989 // Dojo figures out the start value for the width and converts the
7990 // integer specified for the width to the more expressive but
7991 // verbose form `{ width: { end: '400', units: 'px' } }` which you
7992 // can also specify directly
7994 // animate width, height, and padding over 2 seconds...the
7996 // | dojo.animateProperty({ node: node, duration:2000,
7998 // | width: { start: '200', end: '400', unit:"px" },
7999 // | height: { start:'200', end: '400', unit:"px" },
8000 // | paddingTop: { start:'5', end:'50', unit:"px" }
8005 // plug in a different easing function and register a callback for
8006 // when the animation ends. Easing functions accept values between
8007 // zero and one and return a value on that basis. In this case, an
8008 // exponential-in curve.
8009 // | dojo.animateProperty({
8010 // | node: "nodeId",
8011 // | // dojo figures out the start value
8012 // | properties: { width: { end: 400 } },
8013 // | easing: function(n){
8014 // | return (n==0) ? 0 : Math.pow(2, 10 * (n - 1));
8016 // | onEnd: function(){
8017 // | // called when the animation finishes
8019 // | }).play(500); // delay playing half a second
8021 args.node = d.byId(args.node);
8022 if(!args.easing){ args.easing = d._defaultEasing; }
8024 var anim = new d._Animation(args);
8025 d.connect(anim, "beforeBegin", anim, function(){
8027 for(var p in this.properties){
8028 // Make shallow copy of properties into pm because we overwrite
8029 // some values below. In particular if start/end are functions
8030 // we don't want to overwrite them or the functions won't be
8031 // called if the animation is reused.
8032 if(p == "width" || p == "height"){
8033 this.node.display = "block";
8035 var prop = this.properties[p];
8036 prop = pm[p] = d.mixin({}, (d.isObject(prop) ? prop: { end: prop }));
8038 if(d.isFunction(prop.start)){
8039 prop.start = prop.start();
8041 if(d.isFunction(prop.end)){
8042 prop.end = prop.end();
8044 var isColor = (p.toLowerCase().indexOf("color") >= 0);
8045 function getStyle(node, p){
8046 // dojo.style(node, "height") can return "auto" or "" on IE; this is more reliable:
8047 var v = ({height: node.offsetHeight, width: node.offsetWidth})[p];
8048 if(v !== undefined){ return v; }
8049 v = d.style(node, p);
8050 return (p=="opacity") ? Number(v) : (isColor ? v : parseFloat(v));
8052 if(!("end" in prop)){
8053 prop.end = getStyle(this.node, p);
8054 }else if(!("start" in prop)){
8055 prop.start = getStyle(this.node, p);
8059 prop.start = new d.Color(prop.start);
8060 prop.end = new d.Color(prop.end);
8062 prop.start = (p == "opacity") ? Number(prop.start) : parseFloat(prop.start);
8065 this.curve = new PropLine(pm);
8067 d.connect(anim, "onAnimate", anim, function(propValues){
8069 for(var s in propValues){
8070 d.style(this.node, s, propValues[s]);
8071 // this.node.style[s] = propValues[s];
8074 return anim; // dojo._Animation
8077 dojo.anim = function( /*DOMNode|String*/ node,
8078 /*Object*/ properties,
8079 /*Integer?*/ duration,
8080 /*Function?*/ easing,
8081 /*Function?*/ onEnd,
8082 /*Integer?*/ delay){
8084 // A simpler interface to `dojo.animateProperty()`, also returns
8085 // an instance of `dojo._Animation` but begins the animation
8086 // immediately, unlike nearly every other Dojo animation API.
8088 // `dojo.anim` is a simpler (but somewhat less powerful) version
8089 // of `dojo.animateProperty`. It uses defaults for many basic properties
8090 // and allows for positional parameters to be used in place of the
8091 // packed "property bag" which is used for other Dojo animation
8094 // The `dojo._Animation` object returned from `dojo.anim` will be
8095 // already playing when it is returned from this function, so
8096 // calling play() on it again is (usually) a no-op.
8098 // a DOM node or the id of a node to animate CSS properties on
8100 // The number of milliseconds over which the animation
8101 // should run. Defaults to the global animation default duration
8104 // An easing function over which to calculate acceleration
8105 // and deceleration of the animation through its duration.
8106 // A default easing algorithm is provided, but you may
8107 // plug in any you wish. A large selection of easing algorithms
8108 // are available in `dojox.fx.easing`.
8110 // A function to be called when the animation finishes
8113 // The number of milliseconds to delay beginning the
8114 // animation by. The default is 0.
8117 // | dojo.anim("id", { opacity: 0 });
8119 // Fade out a node over a full second
8120 // | dojo.anim("id", { opacity: 0 }, 1000);
8121 return d.animateProperty({
8123 duration: duration||d._Animation.prototype.duration,
8124 properties: properties,
8133 if(!dojo._hasResource["dojo._base.browser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
8134 dojo._hasResource["dojo._base.browser"] = true;
8135 dojo.provide("dojo._base.browser");
8145 //Need this to be the last code segment in base, so do not place any
8146 //dojo.requireIf calls in this file. Otherwise, due to how the build system
8147 //puts all requireIf dependencies after the current file, the require calls
8148 //could be called before all of base is defined.
8149 if(dojo.config.require){
8150 dojo.forEach(dojo.config.require, "dojo['require'](item);");
8156 if(dojo.config.afterOnLoad && dojo.isBrowser){
8157 //Dojo is being added to the page after page load, so just trigger
8158 //the init sequence after a timeout. Using a timeout so the rest of this
8159 //script gets evaluated properly. This work needs to happen after the
8160 //dojo.config.require work done in dojo._base.
8161 window.setTimeout(dojo._fakeLoadInit, 1000);