| /* |
| Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. |
| Available via Academic Free License >= 2.1 OR the modified BSD license. |
| see: http://dojotoolkit.org/license for details |
| */ |
| |
| /* |
| This is an optimized version of Dojo, built for deployment and not for |
| development. To get sources and documentation, please visit: |
| |
| http://dojotoolkit.org |
| */ |
| |
| ;(function(){ |
| |
| /* |
| dojo, dijit, and dojox must always be the first three, and in that order. |
| djConfig.scopeMap = [ |
| ["dojo", "fojo"], |
| ["dijit", "fijit"], |
| ["dojox", "fojox"] |
| |
| ] |
| */ |
| |
| /**Build will replace this comment with a scoped djConfig **/ |
| |
| //The null below can be relaced by a build-time value used instead of djConfig.scopeMap. |
| var sMap = null; |
| |
| //See if new scopes need to be defined. |
| if((sMap || (typeof djConfig != "undefined" && djConfig.scopeMap)) && (typeof window != "undefined")){ |
| var scopeDef = "", scopePrefix = "", scopeSuffix = "", scopeMap = {}, scopeMapRev = {}; |
| sMap = sMap || djConfig.scopeMap; |
| for(var i = 0; i < sMap.length; i++){ |
| //Make local variables, then global variables that use the locals. |
| var newScope = sMap[i]; |
| scopeDef += "var " + newScope[0] + " = {}; " + newScope[1] + " = " + newScope[0] + ";" + newScope[1] + "._scopeName = '" + newScope[1] + "';"; |
| scopePrefix += (i == 0 ? "" : ",") + newScope[0]; |
| scopeSuffix += (i == 0 ? "" : ",") + newScope[1]; |
| scopeMap[newScope[0]] = newScope[1]; |
| scopeMapRev[newScope[1]] = newScope[0]; |
| } |
| |
| eval(scopeDef + "dojo._scopeArgs = [" + scopeSuffix + "];"); |
| |
| dojo._scopePrefixArgs = scopePrefix; |
| dojo._scopePrefix = "(function(" + scopePrefix + "){"; |
| dojo._scopeSuffix = "})(" + scopeSuffix + ")"; |
| dojo._scopeMap = scopeMap; |
| dojo._scopeMapRev = scopeMapRev; |
| } |
| |
| /*===== |
| // note: |
| // 'djConfig' does not exist under 'dojo.*' so that it can be set before the |
| // 'dojo' variable exists. |
| // note: |
| // Setting any of these variables *after* the library has loaded does |
| // nothing at all. |
| |
| djConfig = { |
| // summary: |
| // Application code can set the global 'djConfig' prior to loading |
| // the library to override certain global settings for how dojo works. |
| // |
| // isDebug: Boolean |
| // Defaults to `false`. If set to `true`, ensures that Dojo provides |
| // extended debugging feedback via Firebug. If Firebug is not available |
| // on your platform, setting `isDebug` to `true` will force Dojo to |
| // pull in (and display) the version of Firebug Lite which is |
| // integrated into the Dojo distribution, thereby always providing a |
| // debugging/logging console when `isDebug` is enabled. Note that |
| // Firebug's `console.*` methods are ALWAYS defined by Dojo. If |
| // `isDebug` is false and you are on a platform without Firebug, these |
| // methods will be defined as no-ops. |
| isDebug: false, |
| // debugAtAllCosts: Boolean |
| // Defaults to `false`. If set to `true`, this triggers an alternate |
| // mode of the package system in which dependencies are detected and |
| // only then are resources evaluated in dependency order via |
| // `<script>` tag inclusion. This may double-request resources and |
| // cause problems with scripts which expect `dojo.require()` to |
| // preform synchronously. `debugAtAllCosts` can be an invaluable |
| // debugging aid, but when using it, ensure that all code which |
| // depends on Dojo modules is wrapped in `dojo.addOnLoad()` handlers. |
| // Due to the somewhat unpredictable side-effects of using |
| // `debugAtAllCosts`, it is strongly recommended that you enable this |
| // flag as a last resort. `debugAtAllCosts` has no effect when loading |
| // resources across domains. For usage information, see the |
| // [Dojo Book](http://dojotoolkit.org/book/book-dojo/part-4-meta-dojo-making-your-dojo-code-run-faster-and-better/debugging-facilities/deb) |
| debugAtAllCosts: false, |
| // locale: String |
| // The locale to assume for loading localized resources in this page, |
| // specified according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt). |
| // Must be specified entirely in lowercase, e.g. `en-us` and `zh-cn`. |
| // See the documentation for `dojo.i18n` and `dojo.requireLocalization` |
| // for details on loading localized resources. If no locale is specified, |
| // Dojo assumes the locale of the user agent, according to `navigator.userLanguage` |
| // or `navigator.language` properties. |
| locale: undefined, |
| // extraLocale: Array |
| // No default value. Specifies additional locales whose |
| // resources should also be loaded alongside the default locale when |
| // calls to `dojo.requireLocalization()` are processed. |
| extraLocale: undefined, |
| // baseUrl: String |
| // The directory in which `dojo.js` is located. Under normal |
| // conditions, Dojo auto-detects the correct location from which it |
| // was loaded. You may need to manually configure `baseUrl` in cases |
| // where you have renamed `dojo.js` or in which `<base>` tags confuse |
| // some browsers (e.g. IE 6). The variable `dojo.baseUrl` is assigned |
| // either the value of `djConfig.baseUrl` if one is provided or the |
| // auto-detected root if not. Other modules are located relative to |
| // this path. The path should end in a slash. |
| baseUrl: undefined, |
| // modulePaths: Object |
| // A map of module names to paths relative to `dojo.baseUrl`. The |
| // key/value pairs correspond directly to the arguments which |
| // `dojo.registerModulePath` accepts. Specifiying |
| // `djConfig.modulePaths = { "foo": "../../bar" }` is the equivalent |
| // of calling `dojo.registerModulePath("foo", "../../bar");`. Multiple |
| // modules may be configured via `djConfig.modulePaths`. |
| modulePaths: {}, |
| // afterOnLoad: Boolean |
| // Indicates Dojo was added to the page after the page load. In this case |
| // Dojo will not wait for the page DOMContentLoad/load events and fire |
| // its dojo.addOnLoad callbacks after making sure all outstanding |
| // dojo.required modules have loaded. Only works with a built dojo.js, |
| // it does not work the dojo.js directly from source control. |
| afterOnLoad: false, |
| // addOnLoad: Function or Array |
| // Adds a callback via dojo.addOnLoad. Useful when Dojo is added after |
| // the page loads and djConfig.afterOnLoad is true. Supports the same |
| // arguments as dojo.addOnLoad. When using a function reference, use |
| // `djConfig.addOnLoad = function(){};`. For object with function name use |
| // `djConfig.addOnLoad = [myObject, "functionName"];` and for object with |
| // function reference use |
| // `djConfig.addOnLoad = [myObject, function(){}];` |
| addOnLoad: null, |
| // require: Array |
| // An array of module names to be loaded immediately after dojo.js has been included |
| // in a page. |
| require: [], |
| // defaultDuration: Array |
| // Default duration, in milliseconds, for wipe and fade animations within dijits. |
| // Assigned to dijit.defaultDuration. |
| defaultDuration: 200, |
| // dojoBlankHtmlUrl: String |
| // Used by some modules to configure an empty iframe. Used by dojo.io.iframe and |
| // dojo.back, and dijit popup support in IE where an iframe is needed to make sure native |
| // controls do not bleed through the popups. Normally this configuration variable |
| // does not need to be set, except when using cross-domain/CDN Dojo builds. |
| // Save dojo/resources/blank.html to your domain and set `djConfig.dojoBlankHtmlUrl` |
| // to the path on your domain your copy of blank.html. |
| dojoBlankHtmlUrl: undefined, |
| // ioPublish: Boolean? |
| // Set this to true to enable publishing of topics for the different phases of |
| // IO operations. Publishing is done via dojo.publish. See dojo.__IoPublish for a list |
| // of topics that are published. |
| ioPublish: false, |
| // useCustomLogger: Anything? |
| // If set to a value that evaluates to true such as a string or array and |
| // isDebug is true and Firebug is not available or running, then it bypasses |
| // the creation of Firebug Lite allowing you to define your own console object. |
| useCustomLogger: undefined, |
| // transparentColor: Array |
| // Array containing the r, g, b components used as transparent color in dojo.Color; |
| // if undefined, [255,255,255] (white) will be used. |
| transparentColor: undefined, |
| // skipIeDomLoaded: Boolean |
| // For IE only, skip the DOMContentLoaded hack used. Sometimes it can cause an Operation |
| // Aborted error if the rest of the page triggers script defers before the DOM is ready. |
| // If this is config value is set to true, then dojo.addOnLoad callbacks will not be |
| // triggered until the page load event, which is after images and iframes load. If you |
| // want to trigger the callbacks sooner, you can put a script block in the bottom of |
| // your HTML that calls dojo._loadInit();. If you are using multiversion support, change |
| // "dojo." to the appropriate scope name for dojo. |
| skipIeDomLoaded: false |
| } |
| =====*/ |
| |
| (function(){ |
| // firebug stubs |
| |
| if(typeof this["loadFirebugConsole"] == "function"){ |
| // for Firebug 1.2 |
| this["loadFirebugConsole"](); |
| }else{ |
| this.console = this.console || {}; |
| |
| // Be careful to leave 'log' always at the end |
| var cn = [ |
| "assert", "count", "debug", "dir", "dirxml", "error", "group", |
| "groupEnd", "info", "profile", "profileEnd", "time", "timeEnd", |
| "trace", "warn", "log" |
| ]; |
| var i = 0, tn; |
| while((tn=cn[i++])){ |
| if(!console[tn]){ |
| (function(){ |
| var tcn = tn+""; |
| console[tcn] = ('log' in console) ? function(){ |
| var a = Array.apply({}, arguments); |
| a.unshift(tcn+":"); |
| console["log"](a.join(" ")); |
| } : function(){} |
| console[tcn]._fake = true; |
| })(); |
| } |
| } |
| } |
| |
| //TODOC: HOW TO DOC THIS? |
| // dojo is the root variable of (almost all) our public symbols -- make sure it is defined. |
| if(typeof dojo == "undefined"){ |
| dojo = { |
| _scopeName: "dojo", |
| _scopePrefix: "", |
| _scopePrefixArgs: "", |
| _scopeSuffix: "", |
| _scopeMap: {}, |
| _scopeMapRev: {} |
| }; |
| } |
| |
| var d = dojo; |
| |
| //Need placeholders for dijit and dojox for scoping code. |
| if(typeof dijit == "undefined"){ |
| dijit = {_scopeName: "dijit"}; |
| } |
| if(typeof dojox == "undefined"){ |
| dojox = {_scopeName: "dojox"}; |
| } |
| |
| if(!d._scopeArgs){ |
| d._scopeArgs = [dojo, dijit, dojox]; |
| } |
| |
| /*===== |
| dojo.global = { |
| // summary: |
| // Alias for the global scope |
| // (e.g. the window object in a browser). |
| // description: |
| // Refer to 'dojo.global' rather than referring to window to ensure your |
| // code runs correctly in contexts other than web browsers (e.g. Rhino on a server). |
| } |
| =====*/ |
| d.global = this; |
| |
| d.config =/*===== djConfig = =====*/{ |
| isDebug: false, |
| debugAtAllCosts: false |
| }; |
| |
| // FIXME: 2.0, drop djConfig support. Use dojoConfig exclusively for global config. |
| var cfg = typeof djConfig != "undefined" ? djConfig : |
| typeof dojoConfig != "undefined" ? dojoConfig : null; |
| |
| if(cfg){ |
| for(var c in cfg){ |
| d.config[c] = cfg[c]; |
| } |
| } |
| |
| /*===== |
| // Override locale setting, if specified |
| dojo.locale = { |
| // summary: the locale as defined by Dojo (read-only) |
| }; |
| =====*/ |
| dojo.locale = d.config.locale; |
| |
| var rev = "$Rev: 24595 $".match(/\d+/); |
| |
| /*===== |
| dojo.version = function(){ |
| // summary: |
| // Version number of the Dojo Toolkit |
| // major: Integer |
| // Major version. If total version is "1.2.0beta1", will be 1 |
| // minor: Integer |
| // Minor version. If total version is "1.2.0beta1", will be 2 |
| // patch: Integer |
| // Patch version. If total version is "1.2.0beta1", will be 0 |
| // flag: String |
| // Descriptor flag. If total version is "1.2.0beta1", will be "beta1" |
| // revision: Number |
| // The SVN rev from which dojo was pulled |
| this.major = 0; |
| this.minor = 0; |
| this.patch = 0; |
| this.flag = ""; |
| this.revision = 0; |
| } |
| =====*/ |
| dojo.version = { |
| major: 1, minor: 6, patch: 1, flag: "", |
| revision: rev ? +rev[0] : NaN, |
| toString: function(){ |
| with(d.version){ |
| return major + "." + minor + "." + patch + flag + " (" + revision + ")"; // String |
| } |
| } |
| } |
| |
| // Register with the OpenAjax hub |
| if(typeof OpenAjax != "undefined"){ |
| OpenAjax.hub.registerLibrary(dojo._scopeName, "http://dojotoolkit.org", d.version.toString()); |
| } |
| |
| var extraNames, extraLen, empty = {}; |
| for(var i in {toString: 1}){ extraNames = []; break; } |
| dojo._extraNames = extraNames = extraNames || ["hasOwnProperty", "valueOf", "isPrototypeOf", |
| "propertyIsEnumerable", "toLocaleString", "toString", "constructor"]; |
| extraLen = extraNames.length; |
| |
| dojo._mixin = function(/*Object*/ target, /*Object*/ source){ |
| // summary: |
| // Adds all properties and methods of source to target. This addition |
| // is "prototype extension safe", so that instances of objects |
| // will not pass along prototype defaults. |
| var name, s, i; |
| for(name in source){ |
| // the "tobj" condition avoid copying properties in "source" |
| // inherited from Object.prototype. For example, if target has a custom |
| // toString() method, don't overwrite it with the toString() method |
| // that source inherited from Object.prototype |
| s = source[name]; |
| if(!(name in target) || (target[name] !== s && (!(name in empty) || empty[name] !== s))){ |
| target[name] = s; |
| } |
| } |
| // IE doesn't recognize some custom functions in for..in |
| if(extraLen && source){ |
| for(i = 0; i < extraLen; ++i){ |
| name = extraNames[i]; |
| s = source[name]; |
| if(!(name in target) || (target[name] !== s && (!(name in empty) || empty[name] !== s))){ |
| target[name] = s; |
| } |
| } |
| } |
| return target; // Object |
| } |
| |
| dojo.mixin = function(/*Object*/obj, /*Object...*/props){ |
| // summary: |
| // Adds all properties and methods of props to obj and returns the |
| // (now modified) obj. |
| // description: |
| // `dojo.mixin` can mix multiple source objects into a |
| // destination object which is then returned. Unlike regular |
| // `for...in` iteration, `dojo.mixin` is also smart about avoiding |
| // extensions which other toolkits may unwisely add to the root |
| // object prototype |
| // obj: |
| // The object to mix properties into. Also the return value. |
| // props: |
| // One or more objects whose values are successively copied into |
| // obj. If more than one of these objects contain the same value, |
| // the one specified last in the function call will "win". |
| // example: |
| // make a shallow copy of an object |
| // | var copy = dojo.mixin({}, source); |
| // example: |
| // many class constructors often take an object which specifies |
| // values to be configured on the object. In this case, it is |
| // often simplest to call `dojo.mixin` on the `this` object: |
| // | dojo.declare("acme.Base", null, { |
| // | constructor: function(properties){ |
| // | // property configuration: |
| // | dojo.mixin(this, properties); |
| // | |
| // | console.log(this.quip); |
| // | // ... |
| // | }, |
| // | quip: "I wasn't born yesterday, you know - I've seen movies.", |
| // | // ... |
| // | }); |
| // | |
| // | // create an instance of the class and configure it |
| // | var b = new acme.Base({quip: "That's what it does!" }); |
| // example: |
| // copy in properties from multiple objects |
| // | var flattened = dojo.mixin( |
| // | { |
| // | name: "Frylock", |
| // | braces: true |
| // | }, |
| // | { |
| // | name: "Carl Brutanananadilewski" |
| // | } |
| // | ); |
| // | |
| // | // will print "Carl Brutanananadilewski" |
| // | console.log(flattened.name); |
| // | // will print "true" |
| // | console.log(flattened.braces); |
| if(!obj){ obj = {}; } |
| for(var i=1, l=arguments.length; i<l; i++){ |
| d._mixin(obj, arguments[i]); |
| } |
| return obj; // Object |
| } |
| |
| dojo._getProp = function(/*Array*/parts, /*Boolean*/create, /*Object*/context){ |
| var obj=context || d.global; |
| for(var i=0, p; obj && (p=parts[i]); i++){ |
| if(i == 0 && d._scopeMap[p]){ |
| p = d._scopeMap[p]; |
| } |
| obj = (p in obj ? obj[p] : (create ? obj[p]={} : undefined)); |
| } |
| return obj; // mixed |
| } |
| |
| dojo.setObject = function(/*String*/name, /*Object*/value, /*Object?*/context){ |
| // summary: |
| // Set a property from a dot-separated string, such as "A.B.C" |
| // description: |
| // Useful for longer api chains where you have to test each object in |
| // the chain, or when you have an object reference in string format. |
| // Objects are created as needed along `path`. Returns the passed |
| // value if setting is successful or `undefined` if not. |
| // name: |
| // Path to a property, in the form "A.B.C". |
| // context: |
| // Optional. Object to use as root of path. Defaults to |
| // `dojo.global`. |
| // example: |
| // set the value of `foo.bar.baz`, regardless of whether |
| // intermediate objects already exist: |
| // | dojo.setObject("foo.bar.baz", value); |
| // example: |
| // without `dojo.setObject`, we often see code like this: |
| // | // ensure that intermediate objects are available |
| // | if(!obj["parent"]){ obj.parent = {}; } |
| // | if(!obj.parent["child"]){ obj.parent.child= {}; } |
| // | // now we can safely set the property |
| // | obj.parent.child.prop = "some value"; |
| // wheras with `dojo.setObject`, we can shorten that to: |
| // | dojo.setObject("parent.child.prop", "some value", obj); |
| var parts=name.split("."), p=parts.pop(), obj=d._getProp(parts, true, context); |
| return obj && p ? (obj[p]=value) : undefined; // Object |
| } |
| |
| dojo.getObject = function(/*String*/name, /*Boolean?*/create, /*Object?*/context){ |
| // summary: |
| // Get a property from a dot-separated string, such as "A.B.C" |
| // description: |
| // Useful for longer api chains where you have to test each object in |
| // the chain, or when you have an object reference in string format. |
| // name: |
| // Path to an property, in the form "A.B.C". |
| // create: |
| // Optional. Defaults to `false`. If `true`, Objects will be |
| // created at any point along the 'path' that is undefined. |
| // context: |
| // Optional. Object to use as root of path. Defaults to |
| // 'dojo.global'. Null may be passed. |
| return d._getProp(name.split("."), create, context); // Object |
| } |
| |
| dojo.exists = function(/*String*/name, /*Object?*/obj){ |
| // summary: |
| // determine if an object supports a given method |
| // description: |
| // useful for longer api chains where you have to test each object in |
| // the chain. Useful for object and method detection. |
| // name: |
| // Path to an object, in the form "A.B.C". |
| // obj: |
| // Object to use as root of path. Defaults to |
| // 'dojo.global'. Null may be passed. |
| // example: |
| // | // define an object |
| // | var foo = { |
| // | bar: { } |
| // | }; |
| // | |
| // | // search the global scope |
| // | dojo.exists("foo.bar"); // true |
| // | dojo.exists("foo.bar.baz"); // false |
| // | |
| // | // search from a particular scope |
| // | dojo.exists("bar", foo); // true |
| // | dojo.exists("bar.baz", foo); // false |
| return d.getObject(name, false, obj) !== undefined; // Boolean |
| } |
| |
| dojo["eval"] = function(/*String*/ scriptFragment){ |
| // summary: |
| // A legacy method created for use exclusively by internal Dojo methods. Do not use |
| // this method directly, the behavior of this eval will differ from the normal |
| // browser eval. |
| // description: |
| // Placed in a separate function to minimize size of trapped |
| // exceptions. Calling eval() directly from some other scope may |
| // complicate tracebacks on some platforms. |
| // returns: |
| // The result of the evaluation. Often `undefined` |
| return d.global.eval ? d.global.eval(scriptFragment) : eval(scriptFragment); // Object |
| } |
| |
| /*===== |
| dojo.deprecated = function(behaviour, extra, removal){ |
| // summary: |
| // Log a debug message to indicate that a behavior has been |
| // deprecated. |
| // behaviour: String |
| // The API or behavior being deprecated. Usually in the form |
| // of "myApp.someFunction()". |
| // extra: String? |
| // Text to append to the message. Often provides advice on a |
| // new function or facility to achieve the same goal during |
| // the deprecation period. |
| // removal: String? |
| // Text to indicate when in the future the behavior will be |
| // removed. Usually a version number. |
| // example: |
| // | dojo.deprecated("myApp.getTemp()", "use myApp.getLocaleTemp() instead", "1.0"); |
| } |
| |
| dojo.experimental = function(moduleName, extra){ |
| // summary: Marks code as experimental. |
| // description: |
| // This can be used to mark a function, file, or module as |
| // experimental. Experimental code is not ready to be used, and the |
| // APIs are subject to change without notice. Experimental code may be |
| // completed deleted without going through the normal deprecation |
| // process. |
| // moduleName: String |
| // The name of a module, or the name of a module file or a specific |
| // function |
| // extra: String? |
| // some additional message for the user |
| // example: |
| // | dojo.experimental("dojo.data.Result"); |
| // example: |
| // | dojo.experimental("dojo.weather.toKelvin()", "PENDING approval from NOAA"); |
| } |
| =====*/ |
| |
| //Real functions declared in dojo._firebug.firebug. |
| d.deprecated = d.experimental = function(){}; |
| |
| })(); |
| // vim:ai:ts=4:noet |
| |
| /* |
| * loader.js - A bootstrap module. Runs before the hostenv_*.js file. Contains |
| * all of the package loading methods. |
| */ |
| (function(){ |
| var d = dojo, currentModule; |
| |
| d.mixin(d, { |
| _loadedModules: {}, |
| _inFlightCount: 0, |
| _hasResource: {}, |
| |
| _modulePrefixes: { |
| dojo: { name: "dojo", value: "." }, |
| // dojox: { name: "dojox", value: "../dojox" }, |
| // dijit: { name: "dijit", value: "../dijit" }, |
| doh: { name: "doh", value: "../util/doh" }, |
| tests: { name: "tests", value: "tests" } |
| }, |
| |
| _moduleHasPrefix: function(/*String*/module){ |
| // summary: checks to see if module has been established |
| var mp = d._modulePrefixes; |
| return !!(mp[module] && mp[module].value); // Boolean |
| }, |
| |
| _getModulePrefix: function(/*String*/module){ |
| // summary: gets the prefix associated with module |
| var mp = d._modulePrefixes; |
| if(d._moduleHasPrefix(module)){ |
| return mp[module].value; // String |
| } |
| return module; // String |
| }, |
| |
| _loadedUrls: [], |
| |
| //WARNING: |
| // This variable is referenced by packages outside of bootstrap: |
| // FloatingPane.js and undo/browser.js |
| _postLoad: false, |
| |
| //Egad! Lots of test files push on this directly instead of using dojo.addOnLoad. |
| _loaders: [], |
| _unloaders: [], |
| _loadNotifying: false |
| }); |
| |
| |
| dojo._loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){ |
| // summary: |
| // Load a Javascript module given a relative path |
| // |
| // description: |
| // Loads and interprets the script located at relpath, which is |
| // relative to the script root directory. If the script is found but |
| // its interpretation causes a runtime exception, that exception is |
| // not caught by us, so the caller will see it. We return a true |
| // value if and only if the script is found. |
| // |
| // relpath: |
| // A relative path to a script (no leading '/', and typically ending |
| // in '.js'). |
| // module: |
| // A module whose existance to check for after loading a path. Can be |
| // used to determine success or failure of the load. |
| // cb: |
| // a callback function to pass the result of evaluating the script |
| |
| var uri = ((relpath.charAt(0) == '/' || relpath.match(/^\w+:/)) ? "" : d.baseUrl) + relpath; |
| try{ |
| currentModule = module; |
| return !module ? d._loadUri(uri, cb) : d._loadUriAndCheck(uri, module, cb); // Boolean |
| }catch(e){ |
| console.error(e); |
| return false; // Boolean |
| }finally{ |
| currentModule = null; |
| } |
| } |
| |
| dojo._loadUri = function(/*String*/uri, /*Function?*/cb){ |
| // summary: |
| // Loads JavaScript from a URI |
| // description: |
| // Reads the contents of the URI, and evaluates the contents. This is |
| // used to load modules as well as resource bundles. Returns true if |
| // it succeeded. Returns false if the URI reading failed. Throws if |
| // the evaluation throws. |
| // uri: a uri which points at the script to be loaded |
| // cb: |
| // a callback function to process the result of evaluating the script |
| // as an expression, typically used by the resource bundle loader to |
| // load JSON-style resources |
| |
| if(d._loadedUrls[uri]){ |
| return true; // Boolean |
| } |
| d._inFlightCount++; // block addOnLoad calls that arrive while we're busy downloading |
| var contents = d._getText(uri, true); |
| if(contents){ // not 404, et al |
| d._loadedUrls[uri] = true; |
| d._loadedUrls.push(uri); |
| if(cb){ |
| //conditional to support script-inject i18n bundle format |
| contents = /^define\(/.test(contents) ? contents : '('+contents+')'; |
| }else{ |
| //Only do the scoping if no callback. If a callback is specified, |
| //it is most likely the i18n bundle stuff. |
| contents = d._scopePrefix + contents + d._scopeSuffix; |
| } |
| if(!d.isIE){ contents += "\r\n//@ sourceURL=" + uri; } // debugging assist for Firebug |
| var value = d["eval"](contents); |
| if(cb){ cb(value); } |
| } |
| // Check to see if we need to call _callLoaded() due to an addOnLoad() that arrived while we were busy downloading |
| if(--d._inFlightCount == 0 && d._postLoad && d._loaders.length){ |
| // We shouldn't be allowed to get here but Firefox allows an event |
| // (mouse, keybd, async xhrGet) to interrupt a synchronous xhrGet. |
| // If the current script block contains multiple require() statements, then after each |
| // require() returns, inFlightCount == 0, but we want to hold the _callLoaded() until |
| // all require()s are done since the out-of-sequence addOnLoad() presumably needs them all. |
| // setTimeout allows the next require() to start (if needed), and then we check this again. |
| setTimeout(function(){ |
| // If inFlightCount > 0, then multiple require()s are running sequentially and |
| // the next require() started after setTimeout() was executed but before we got here. |
| if(d._inFlightCount == 0){ |
| d._callLoaded(); |
| } |
| }, 0); |
| } |
| return !!contents; // Boolean: contents? true : false |
| } |
| |
| // FIXME: probably need to add logging to this method |
| dojo._loadUriAndCheck = function(/*String*/uri, /*String*/moduleName, /*Function?*/cb){ |
| // summary: calls loadUri then findModule and returns true if both succeed |
| var ok = false; |
| try{ |
| ok = d._loadUri(uri, cb); |
| }catch(e){ |
| console.error("failed loading " + uri + " with error: " + e); |
| } |
| return !!(ok && d._loadedModules[moduleName]); // Boolean |
| } |
| |
| dojo.loaded = function(){ |
| // summary: |
| // signal fired when initial environment and package loading is |
| // complete. You should use dojo.addOnLoad() instead of doing a |
| // direct dojo.connect() to this method in order to handle |
| // initialization tasks that require the environment to be |
| // initialized. In a browser host, declarative widgets will |
| // be constructed when this function finishes runing. |
| d._loadNotifying = true; |
| d._postLoad = true; |
| var mll = d._loaders; |
| |
| //Clear listeners so new ones can be added |
| //For other xdomain package loads after the initial load. |
| d._loaders = []; |
| |
| for(var x = 0; x < mll.length; x++){ |
| mll[x](); |
| } |
| |
| d._loadNotifying = false; |
| |
| //Make sure nothing else got added to the onload queue |
| //after this first run. If something did, and we are not waiting for any |
| //more inflight resources, run again. |
| if(d._postLoad && d._inFlightCount == 0 && mll.length){ |
| d._callLoaded(); |
| } |
| } |
| |
| dojo.unloaded = function(){ |
| // summary: |
| // signal fired by impending environment destruction. You should use |
| // dojo.addOnUnload() instead of doing a direct dojo.connect() to this |
| // method to perform page/application cleanup methods. See |
| // dojo.addOnUnload for more info. |
| var mll = d._unloaders; |
| while(mll.length){ |
| (mll.pop())(); |
| } |
| } |
| |
| d._onto = function(arr, obj, fn){ |
| if(!fn){ |
| arr.push(obj); |
| }else if(fn){ |
| var func = (typeof fn == "string") ? obj[fn] : fn; |
| arr.push(function(){ func.call(obj); }); |
| } |
| } |
| |
| dojo.ready = dojo.addOnLoad = function(/*Object*/obj, /*String|Function?*/functionName){ |
| // summary: |
| // Registers a function to be triggered after the DOM and dojo.require() calls |
| // have finished loading. |
| // |
| // description: |
| // Registers a function to be triggered after the DOM has finished |
| // loading and `dojo.require` modules have loaded. Widgets declared in markup |
| // have been instantiated if `djConfig.parseOnLoad` is true when this fires. |
| // |
| // Images and CSS files may or may not have finished downloading when |
| // the specified function is called. (Note that widgets' CSS and HTML |
| // code is guaranteed to be downloaded before said widgets are |
| // instantiated, though including css resouces BEFORE any script elements |
| // is highly recommended). |
| // |
| // example: |
| // Register an anonymous function to run when everything is ready |
| // | dojo.addOnLoad(function(){ doStuff(); }); |
| // |
| // example: |
| // Register a function to run when everything is ready by pointer: |
| // | var init = function(){ doStuff(); } |
| // | dojo.addOnLoad(init); |
| // |
| // example: |
| // Register a function to run scoped to `object`, either by name or anonymously: |
| // | dojo.addOnLoad(object, "functionName"); |
| // | dojo.addOnLoad(object, function(){ doStuff(); }); |
| |
| d._onto(d._loaders, obj, functionName); |
| |
| //Added for xdomain loading. dojo.addOnLoad is used to |
| //indicate callbacks after doing some dojo.require() statements. |
| //In the xdomain case, if all the requires are loaded (after initial |
| //page load), then immediately call any listeners. |
| if(d._postLoad && d._inFlightCount == 0 && !d._loadNotifying){ |
| d._callLoaded(); |
| } |
| } |
| |
| //Support calling dojo.addOnLoad via djConfig.addOnLoad. Support all the |
| //call permutations of dojo.addOnLoad. Mainly useful when dojo is added |
| //to the page after the page has loaded. |
| var dca = d.config.addOnLoad; |
| if(dca){ |
| d.addOnLoad[(dca instanceof Array ? "apply" : "call")](d, dca); |
| } |
| |
| dojo._modulesLoaded = function(){ |
| if(d._postLoad){ return; } |
| if(d._inFlightCount > 0){ |
| console.warn("files still in flight!"); |
| return; |
| } |
| d._callLoaded(); |
| } |
| |
| dojo._callLoaded = function(){ |
| |
| // The "object" check is for IE, and the other opera check fixes an |
| // issue in Opera where it could not find the body element in some |
| // widget test cases. For 0.9, maybe route all browsers through the |
| // setTimeout (need protection still for non-browser environments |
| // though). This might also help the issue with FF 2.0 and freezing |
| // issues where we try to do sync xhr while background css images are |
| // being loaded (trac #2572)? Consider for 0.9. |
| if(typeof setTimeout == "object" || (d.config.useXDomain && d.isOpera)){ |
| setTimeout( |
| d.isAIR ? function(){ d.loaded(); } : d._scopeName + ".loaded();", |
| 0); |
| }else{ |
| d.loaded(); |
| } |
| } |
| |
| dojo._getModuleSymbols = function(/*String*/modulename){ |
| // summary: |
| // Converts a module name in dotted JS notation to an array |
| // representing the path in the source tree |
| var syms = modulename.split("."); |
| for(var i = syms.length; i>0; i--){ |
| var parentModule = syms.slice(0, i).join("."); |
| if(i == 1 && !d._moduleHasPrefix(parentModule)){ |
| // Support default module directory (sibling of dojo) for top-level modules |
| syms[0] = "../" + syms[0]; |
| }else{ |
| var parentModulePath = d._getModulePrefix(parentModule); |
| if(parentModulePath != parentModule){ |
| syms.splice(0, i, parentModulePath); |
| break; |
| } |
| } |
| } |
| return syms; // Array |
| } |
| |
| dojo._global_omit_module_check = false; |
| |
| dojo.loadInit = function(/*Function*/init){ |
| // summary: |
| // Executes a function that needs to be executed for the loader's dojo.requireIf |
| // resolutions to work. This is needed mostly for the xdomain loader case where |
| // a function needs to be executed to set up the possible values for a dojo.requireIf |
| // call. |
| // init: |
| // a function reference. Executed immediately. |
| // description: This function is mainly a marker for the xdomain loader to know parts of |
| // code that needs be executed outside the function wrappper that is placed around modules. |
| // The init function could be executed more than once, and it should make no assumptions |
| // on what is loaded, or what modules are available. Only the functionality in Dojo Base |
| // is allowed to be used. Avoid using this method. For a valid use case, |
| // see the source for dojox.gfx. |
| init(); |
| } |
| |
| dojo._loadModule = dojo.require = function(/*String*/moduleName, /*Boolean?*/omitModuleCheck){ |
| // summary: |
| // loads a Javascript module from the appropriate URI |
| // |
| // moduleName: String |
| // module name to load, using periods for separators, |
| // e.g. "dojo.date.locale". Module paths are de-referenced by dojo's |
| // internal mapping of locations to names and are disambiguated by |
| // longest prefix. See `dojo.registerModulePath()` for details on |
| // registering new modules. |
| // |
| // omitModuleCheck: Boolean? |
| // if `true`, omitModuleCheck skips the step of ensuring that the |
| // loaded file actually defines the symbol it is referenced by. |
| // For example if it called as `dojo.require("a.b.c")` and the |
| // file located at `a/b/c.js` does not define an object `a.b.c`, |
| // and exception will be throws whereas no exception is raised |
| // when called as `dojo.require("a.b.c", true)` |
| // |
| // description: |
| // Modules are loaded via dojo.require by using one of two loaders: the normal loader |
| // and the xdomain loader. The xdomain loader is used when dojo was built with a |
| // custom build that specified loader=xdomain and the module lives on a modulePath |
| // that is a whole URL, with protocol and a domain. The versions of Dojo that are on |
| // the Google and AOL CDNs use the xdomain loader. |
| // |
| // If the module is loaded via the xdomain loader, it is an asynchronous load, since |
| // the module is added via a dynamically created script tag. This |
| // means that dojo.require() can return before the module has loaded. However, this |
| // should only happen in the case where you do dojo.require calls in the top-level |
| // HTML page, or if you purposely avoid the loader checking for dojo.require |
| // dependencies in your module by using a syntax like dojo["require"] to load the module. |
| // |
| // Sometimes it is useful to not have the loader detect the dojo.require calls in the |
| // module so that you can dynamically load the modules as a result of an action on the |
| // page, instead of right at module load time. |
| // |
| // Also, for script blocks in an HTML page, the loader does not pre-process them, so |
| // it does not know to download the modules before the dojo.require calls occur. |
| // |
| // So, in those two cases, when you want on-the-fly module loading or for script blocks |
| // in the HTML page, special care must be taken if the dojo.required code is loaded |
| // asynchronously. To make sure you can execute code that depends on the dojo.required |
| // modules, be sure to add the code that depends on the modules in a dojo.addOnLoad() |
| // callback. dojo.addOnLoad waits for all outstanding modules to finish loading before |
| // executing. |
| // |
| // This type of syntax works with both xdomain and normal loaders, so it is good |
| // practice to always use this idiom for on-the-fly code loading and in HTML script |
| // blocks. If at some point you change loaders and where the code is loaded from, |
| // it will all still work. |
| // |
| // More on how dojo.require |
| // `dojo.require("A.B")` first checks to see if symbol A.B is |
| // defined. If it is, it is simply returned (nothing to do). |
| // |
| // If it is not defined, it will look for `A/B.js` in the script root |
| // directory. |
| // |
| // `dojo.require` throws an exception if it cannot find a file |
| // to load, or if the symbol `A.B` is not defined after loading. |
| // |
| // It returns the object `A.B`, but note the caveats above about on-the-fly loading and |
| // HTML script blocks when the xdomain loader is loading a module. |
| // |
| // `dojo.require()` does nothing about importing symbols into |
| // the current namespace. It is presumed that the caller will |
| // take care of that. |
| // |
| // example: |
| // To use dojo.require in conjunction with dojo.ready: |
| // |
| // | dojo.require("foo"); |
| // | dojo.require("bar"); |
| // | dojo.addOnLoad(function(){ |
| // | //you can now safely do something with foo and bar |
| // | }); |
| // |
| // example: |
| // For example, to import all symbols into a local block, you might write: |
| // |
| // | with (dojo.require("A.B")) { |
| // | ... |
| // | } |
| // |
| // And to import just the leaf symbol to a local variable: |
| // |
| // | var B = dojo.require("A.B"); |
| // | ... |
| // |
| // returns: |
| // the required namespace object |
| omitModuleCheck = d._global_omit_module_check || omitModuleCheck; |
| |
| //Check if it is already loaded. |
| var module = d._loadedModules[moduleName]; |
| if(module){ |
| return module; |
| } |
| |
| // convert periods to slashes |
| var relpath = d._getModuleSymbols(moduleName).join("/") + '.js'; |
| var modArg = !omitModuleCheck ? moduleName : null; |
| var ok = d._loadPath(relpath, modArg); |
| if(!ok && !omitModuleCheck){ |
| throw new Error("Could not load '" + moduleName + "'; last tried '" + relpath + "'"); |
| } |
| |
| // check that the symbol was defined |
| // Don't bother if we're doing xdomain (asynchronous) loading. |
| if(!omitModuleCheck && !d._isXDomain){ |
| // pass in false so we can give better error |
| module = d._loadedModules[moduleName]; |
| if(!module){ |
| throw new Error("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'"); |
| } |
| } |
| |
| return module; |
| } |
| |
| dojo.provide = function(/*String*/ resourceName){ |
| // summary: |
| // Register a resource with the package system. Works in conjunction with `dojo.require` |
| // |
| // description: |
| // Each javascript source file is called a resource. When a |
| // resource is loaded by the browser, `dojo.provide()` registers |
| // that it has been loaded. |
| // |
| // Each javascript source file must have at least one |
| // `dojo.provide()` call at the top of the file, corresponding to |
| // the file name. For example, `js/dojo/foo.js` must have |
| // `dojo.provide("dojo.foo");` before any calls to |
| // `dojo.require()` are made. |
| // |
| // For backwards compatibility reasons, in addition to registering |
| // the resource, `dojo.provide()` also ensures that the javascript |
| // object for the module exists. For example, |
| // `dojo.provide("dojox.data.FlickrStore")`, in addition to |
| // registering that `FlickrStore.js` is a resource for the |
| // `dojox.data` module, will ensure that the `dojox.data` |
| // javascript object exists, so that calls like |
| // `dojo.data.foo = function(){ ... }` don't fail. |
| // |
| // In the case of a build where multiple javascript source files |
| // are combined into one bigger file (similar to a .lib or .jar |
| // file), that file may contain multiple dojo.provide() calls, to |
| // note that it includes multiple resources. |
| // |
| // resourceName: String |
| // A dot-sperated string identifying a resource. |
| // |
| // example: |
| // Safely create a `my` object, and make dojo.require("my.CustomModule") work |
| // | dojo.provide("my.CustomModule"); |
| |
| //Make sure we have a string. |
| resourceName = resourceName + ""; |
| return (d._loadedModules[resourceName] = d.getObject(resourceName, true)); // Object |
| } |
| |
| //Start of old bootstrap2: |
| |
| dojo.platformRequire = function(/*Object*/modMap){ |
| // summary: |
| // require one or more modules based on which host environment |
| // Dojo is currently operating in |
| // description: |
| // This method takes a "map" of arrays which one can use to |
| // optionally load dojo modules. The map is indexed by the |
| // possible dojo.name_ values, with two additional values: |
| // "default" and "common". The items in the "default" array will |
| // be loaded if none of the other items have been choosen based on |
| // dojo.name_, set by your host environment. The items in the |
| // "common" array will *always* be loaded, regardless of which |
| // list is chosen. |
| // example: |
| // | dojo.platformRequire({ |
| // | browser: [ |
| // | "foo.sample", // simple module |
| // | "foo.test", |
| // | ["foo.bar.baz", true] // skip object check in _loadModule (dojo.require) |
| // | ], |
| // | default: [ "foo.sample._base" ], |
| // | common: [ "important.module.common" ] |
| // | }); |
| |
| var common = modMap.common || []; |
| var result = common.concat(modMap[d._name] || modMap["default"] || []); |
| |
| for(var x=0; x<result.length; x++){ |
| var curr = result[x]; |
| if(curr.constructor == Array){ |
| d._loadModule.apply(d, curr); |
| }else{ |
| d._loadModule(curr); |
| } |
| } |
| } |
| |
| dojo.requireIf = function(/*Boolean*/ condition, /*String*/ resourceName){ |
| // summary: |
| // If the condition is true then call `dojo.require()` for the specified |
| // resource |
| // |
| // example: |
| // | dojo.requireIf(dojo.isBrowser, "my.special.Module"); |
| |
| if(condition === true){ |
| // FIXME: why do we support chained require()'s here? does the build system? |
| var args = []; |
| for(var i = 1; i < arguments.length; i++){ |
| args.push(arguments[i]); |
| } |
| d.require.apply(d, args); |
| } |
| } |
| |
| dojo.requireAfterIf = d.requireIf; |
| |
| dojo.registerModulePath = function(/*String*/module, /*String*/prefix){ |
| // summary: |
| // Maps a module name to a path |
| // description: |
| // An unregistered module is given the default path of ../[module], |
| // relative to Dojo root. For example, module acme is mapped to |
| // ../acme. If you want to use a different module name, use |
| // dojo.registerModulePath. |
| // example: |
| // If your dojo.js is located at this location in the web root: |
| // | /myapp/js/dojo/dojo/dojo.js |
| // and your modules are located at: |
| // | /myapp/js/foo/bar.js |
| // | /myapp/js/foo/baz.js |
| // | /myapp/js/foo/thud/xyzzy.js |
| // Your application can tell Dojo to locate the "foo" namespace by calling: |
| // | dojo.registerModulePath("foo", "../../foo"); |
| // At which point you can then use dojo.require() to load the |
| // modules (assuming they provide() the same things which are |
| // required). The full code might be: |
| // | <script type="text/javascript" |
| // | src="/myapp/js/dojo/dojo/dojo.js"></script> |
| // | <script type="text/javascript"> |
| // | dojo.registerModulePath("foo", "../../foo"); |
| // | dojo.require("foo.bar"); |
| // | dojo.require("foo.baz"); |
| // | dojo.require("foo.thud.xyzzy"); |
| // | </script> |
| d._modulePrefixes[module] = { name: module, value: prefix }; |
| }; |
| |
| dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale, /*String?*/availableFlatLocales){ |
| // summary: |
| // Declares translated resources and loads them if necessary, in the |
| // same style as dojo.require. Contents of the resource bundle are |
| // typically strings, but may be any name/value pair, represented in |
| // JSON format. See also `dojo.i18n.getLocalization`. |
| // |
| // description: |
| // Load translated resource bundles provided underneath the "nls" |
| // directory within a package. Translated resources may be located in |
| // different packages throughout the source tree. |
| // |
| // Each directory is named for a locale as specified by RFC 3066, |
| // (http://www.ietf.org/rfc/rfc3066.txt), normalized in lowercase. |
| // Note that the two bundles in the example do not define all the |
| // same variants. For a given locale, bundles will be loaded for |
| // that locale and all more general locales above it, including a |
| // fallback at the root directory. For example, a declaration for |
| // the "de-at" locale will first load `nls/de-at/bundleone.js`, |
| // then `nls/de/bundleone.js` and finally `nls/bundleone.js`. The |
| // data will be flattened into a single Object so that lookups |
| // will follow this cascading pattern. An optional build step can |
| // preload the bundles to avoid data redundancy and the multiple |
| // network hits normally required to load these resources. |
| // |
| // moduleName: |
| // name of the package containing the "nls" directory in which the |
| // bundle is found |
| // |
| // bundleName: |
| // bundle name, i.e. the filename without the '.js' suffix. Using "nls" as a |
| // a bundle name is not supported, since "nls" is the name of the folder |
| // that holds bundles. Using "nls" as the bundle name will cause problems |
| // with the custom build. |
| // |
| // locale: |
| // the locale to load (optional) By default, the browser's user |
| // locale as defined by dojo.locale |
| // |
| // availableFlatLocales: |
| // A comma-separated list of the available, flattened locales for this |
| // bundle. This argument should only be set by the build process. |
| // |
| // example: |
| // A particular widget may define one or more resource bundles, |
| // structured in a program as follows, where moduleName is |
| // mycode.mywidget and bundleNames available include bundleone and |
| // bundletwo: |
| // | ... |
| // | mycode/ |
| // | mywidget/ |
| // | nls/ |
| // | bundleone.js (the fallback translation, English in this example) |
| // | bundletwo.js (also a fallback translation) |
| // | de/ |
| // | bundleone.js |
| // | bundletwo.js |
| // | de-at/ |
| // | bundleone.js |
| // | en/ |
| // | (empty; use the fallback translation) |
| // | en-us/ |
| // | bundleone.js |
| // | en-gb/ |
| // | bundleone.js |
| // | es/ |
| // | bundleone.js |
| // | bundletwo.js |
| // | ...etc |
| // | ... |
| // |
| |
| d.require("dojo.i18n"); |
| d.i18n._requireLocalization.apply(d.hostenv, arguments); |
| }; |
| |
| |
| var ore = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"), |
| ire = new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"); |
| |
| dojo._Url = function(/*dojo._Url|String...*/){ |
| // summary: |
| // Constructor to create an object representing a URL. |
| // It is marked as private, since we might consider removing |
| // or simplifying it. |
| // description: |
| // Each argument is evaluated in order relative to the next until |
| // a canonical uri is produced. To get an absolute Uri relative to |
| // the current document use: |
| // new dojo._Url(document.baseURI, url) |
| |
| var n = null, |
| _a = arguments, |
| uri = [_a[0]]; |
| // resolve uri components relative to each other |
| for(var i = 1; i<_a.length; i++){ |
| if(!_a[i]){ continue; } |
| |
| // Safari doesn't support this.constructor so we have to be explicit |
| // FIXME: Tracked (and fixed) in Webkit bug 3537. |
| // http://bugs.webkit.org/show_bug.cgi?id=3537 |
| var relobj = new d._Url(_a[i]+""), |
| uriobj = new d._Url(uri[0]+""); |
| |
| if( |
| relobj.path == "" && |
| !relobj.scheme && |
| !relobj.authority && |
| !relobj.query |
| ){ |
| if(relobj.fragment != n){ |
| uriobj.fragment = relobj.fragment; |
| } |
| relobj = uriobj; |
| }else if(!relobj.scheme){ |
| relobj.scheme = uriobj.scheme; |
| |
| if(!relobj.authority){ |
| relobj.authority = uriobj.authority; |
| |
| if(relobj.path.charAt(0) != "/"){ |
| var path = uriobj.path.substring(0, |
| uriobj.path.lastIndexOf("/") + 1) + relobj.path; |
| |
| var segs = path.split("/"); |
| for(var j = 0; j < segs.length; j++){ |
| if(segs[j] == "."){ |
| // flatten "./" references |
| if(j == segs.length - 1){ |
| segs[j] = ""; |
| }else{ |
| segs.splice(j, 1); |
| j--; |
| } |
| }else if(j > 0 && !(j == 1 && segs[0] == "") && |
| segs[j] == ".." && segs[j-1] != ".."){ |
| // flatten "../" references |
| if(j == (segs.length - 1)){ |
| segs.splice(j, 1); |
| segs[j - 1] = ""; |
| }else{ |
| segs.splice(j - 1, 2); |
| j -= 2; |
| } |
| } |
| } |
| relobj.path = segs.join("/"); |
| } |
| } |
| } |
| |
| uri = []; |
| if(relobj.scheme){ |
| uri.push(relobj.scheme, ":"); |
| } |
| if(relobj.authority){ |
| uri.push("//", relobj.authority); |
| } |
| uri.push(relobj.path); |
| if(relobj.query){ |
| uri.push("?", relobj.query); |
| } |
| if(relobj.fragment){ |
| uri.push("#", relobj.fragment); |
| } |
| } |
| |
| this.uri = uri.join(""); |
| |
| // break the uri into its main components |
| var r = this.uri.match(ore); |
| |
| this.scheme = r[2] || (r[1] ? "" : n); |
| this.authority = r[4] || (r[3] ? "" : n); |
| this.path = r[5]; // can never be undefined |
| this.query = r[7] || (r[6] ? "" : n); |
| this.fragment = r[9] || (r[8] ? "" : n); |
| |
| if(this.authority != n){ |
| // server based naming authority |
| r = this.authority.match(ire); |
| |
| this.user = r[3] || n; |
| this.password = r[4] || n; |
| this.host = r[6] || r[7]; // ipv6 || ipv4 |
| this.port = r[9] || n; |
| } |
| } |
| |
| dojo._Url.prototype.toString = function(){ return this.uri; }; |
| |
| dojo.moduleUrl = function(/*String*/module, /*dojo._Url||String*/url){ |
| // summary: |
| // Returns a `dojo._Url` object relative to a module. |
| // example: |
| // | var pngPath = dojo.moduleUrl("acme","images/small.png"); |
| // | console.dir(pngPath); // list the object properties |
| // | // create an image and set it's source to pngPath's value: |
| // | var img = document.createElement("img"); |
| // | // NOTE: we assign the string representation of the url object |
| // | img.src = pngPath.toString(); |
| // | // add our image to the document |
| // | dojo.body().appendChild(img); |
| // example: |
| // you may de-reference as far as you like down the package |
| // hierarchy. This is sometimes handy to avoid lenghty relative |
| // urls or for building portable sub-packages. In this example, |
| // the `acme.widget` and `acme.util` directories may be located |
| // under different roots (see `dojo.registerModulePath`) but the |
| // the modules which reference them can be unaware of their |
| // relative locations on the filesystem: |
| // | // somewhere in a configuration block |
| // | dojo.registerModulePath("acme.widget", "../../acme/widget"); |
| // | dojo.registerModulePath("acme.util", "../../util"); |
| // | |
| // | // ... |
| // | |
| // | // code in a module using acme resources |
| // | var tmpltPath = dojo.moduleUrl("acme.widget","templates/template.html"); |
| // | var dataPath = dojo.moduleUrl("acme.util","resources/data.json"); |
| |
| var loc = d._getModuleSymbols(module).join('/'); |
| if(!loc){ return null; } |
| if(loc.lastIndexOf("/") != loc.length-1){ |
| loc += "/"; |
| } |
| |
| //If the path is an absolute path (starts with a / or is on another |
| //domain/xdomain) then don't add the baseUrl. |
| var colonIndex = loc.indexOf(":"); |
| if(loc.charAt(0) != "/" && (colonIndex == -1 || colonIndex > loc.indexOf("/"))){ |
| loc = d.baseUrl + loc; |
| } |
| |
| return new d._Url(loc, url); // dojo._Url |
| }; |
| |
| |
| |
| })(); |
| |
| /*===== |
| dojo.isBrowser = { |
| // example: |
| // | if(dojo.isBrowser){ ... } |
| }; |
| |
| dojo.isFF = { |
| // example: |
| // | if(dojo.isFF > 1){ ... } |
| }; |
| |
| dojo.isIE = { |
| // example: |
| // | if(dojo.isIE > 6){ |
| // | // we are IE7 |
| // | } |
| }; |
| |
| dojo.isSafari = { |
| // example: |
| // | if(dojo.isSafari){ ... } |
| // example: |
| // Detect iPhone: |
| // | if(dojo.isSafari && navigator.userAgent.indexOf("iPhone") != -1){ |
| // | // we are iPhone. Note, iPod touch reports "iPod" above and fails this test. |
| // | } |
| }; |
| |
| dojo = { |
| // isBrowser: Boolean |
| // True if the client is a web-browser |
| isBrowser: true, |
| // isFF: Number | undefined |
| // Version as a Number if client is FireFox. undefined otherwise. Corresponds to |
| // major detected FireFox version (1.5, 2, 3, etc.) |
| isFF: 2, |
| // isIE: Number | undefined |
| // Version as a Number if client is MSIE(PC). undefined otherwise. Corresponds to |
| // major detected IE version (6, 7, 8, etc.) |
| isIE: 6, |
| // isKhtml: Number | undefined |
| // Version as a Number if client is a KHTML browser. undefined otherwise. Corresponds to major |
| // detected version. |
| isKhtml: 0, |
| // isWebKit: Number | undefined |
| // Version as a Number if client is a WebKit-derived browser (Konqueror, |
| // Safari, Chrome, etc.). undefined otherwise. |
| isWebKit: 0, |
| // isMozilla: Number | undefined |
| // Version as a Number if client is a Mozilla-based browser (Firefox, |
| // SeaMonkey). undefined otherwise. Corresponds to major detected version. |
| isMozilla: 0, |
| // isOpera: Number | undefined |
| // Version as a Number if client is Opera. undefined otherwise. Corresponds to |
| // major detected version. |
| isOpera: 0, |
| // isSafari: Number | undefined |
| // Version as a Number if client is Safari or iPhone. undefined otherwise. |
| isSafari: 0, |
| // isChrome: Number | undefined |
| // Version as a Number if client is Chrome browser. undefined otherwise. |
| isChrome: 0 |
| // isMac: Boolean |
| // True if the client runs on Mac |
| } |
| =====*/ |
| if(typeof window != 'undefined'){ |
| dojo.isBrowser = true; |
| dojo._name = "browser"; |
| |
| |
| // attempt to figure out the path to dojo if it isn't set in the config |
| (function(){ |
| var d = dojo; |
| |
| // this is a scope protection closure. We set browser versions and grab |
| // the URL we were loaded from here. |
| |
| // grab the node we were loaded from |
| if(document && document.getElementsByTagName){ |
| var scripts = document.getElementsByTagName("script"); |
| var rePkg = /dojo(\.xd)?\.js(\W|$)/i; |
| for(var i = 0; i < scripts.length; i++){ |
| var src = scripts[i].getAttribute("src"); |
| if(!src){ continue; } |
| var m = src.match(rePkg); |
| if(m){ |
| // find out where we came from |
| if(!d.config.baseUrl){ |
| d.config.baseUrl = src.substring(0, m.index); |
| } |
| // and find out if we need to modify our behavior |
| var cfg = (scripts[i].getAttribute("djConfig") || scripts[i].getAttribute("data-dojo-config")); |
| if(cfg){ |
| var cfgo = eval("({ "+cfg+" })"); |
| for(var x in cfgo){ |
| dojo.config[x] = cfgo[x]; |
| } |
| } |
| break; // "first Dojo wins" |
| } |
| } |
| } |
| d.baseUrl = d.config.baseUrl; |
| |
| // fill in the rendering support information in dojo.render.* |
| var n = navigator; |
| var dua = n.userAgent, |
| dav = n.appVersion, |
| tv = parseFloat(dav); |
| |
| if(dua.indexOf("Opera") >= 0){ d.isOpera = tv; } |
| if(dua.indexOf("AdobeAIR") >= 0){ d.isAIR = 1; } |
| d.isKhtml = (dav.indexOf("Konqueror") >= 0) ? tv : 0; |
| d.isWebKit = parseFloat(dua.split("WebKit/")[1]) || undefined; |
| d.isChrome = parseFloat(dua.split("Chrome/")[1]) || undefined; |
| d.isMac = dav.indexOf("Macintosh") >= 0; |
| |
| // safari detection derived from: |
| // http://developer.apple.com/internet/safari/faq.html#anchor2 |
| // http://developer.apple.com/internet/safari/uamatrix.html |
| var index = Math.max(dav.indexOf("WebKit"), dav.indexOf("Safari"), 0); |
| if(index && !dojo.isChrome){ |
| // try to grab the explicit Safari version first. If we don't get |
| // one, look for less than 419.3 as the indication that we're on something |
| // "Safari 2-ish". |
| d.isSafari = parseFloat(dav.split("Version/")[1]); |
| if(!d.isSafari || parseFloat(dav.substr(index + 7)) <= 419.3){ |
| d.isSafari = 2; |
| } |
| } |
| |
| if(dua.indexOf("Gecko") >= 0 && !d.isKhtml && !d.isWebKit){ d.isMozilla = d.isMoz = tv; } |
| if(d.isMoz){ |
| //We really need to get away from this. Consider a sane isGecko approach for the future. |
| d.isFF = parseFloat(dua.split("Firefox/")[1] || dua.split("Minefield/")[1]) || undefined; |
| } |
| if(document.all && !d.isOpera){ |
| d.isIE = parseFloat(dav.split("MSIE ")[1]) || undefined; |
| //In cases where the page has an HTTP header or META tag with |
| //X-UA-Compatible, then it is in emulation mode. |
| //Make sure isIE reflects the desired version. |
| //document.documentMode of 5 means quirks mode. |
| //Only switch the value if documentMode's major version |
| //is different from isIE's major version. |
| var mode = document.documentMode; |
| if(mode && mode != 5 && Math.floor(d.isIE) != mode){ |
| d.isIE = mode; |
| } |
| } |
| |
| //Workaround to get local file loads of dojo to work on IE 7 |
| //by forcing to not use native xhr. |
| if(dojo.isIE && window.location.protocol === "file:"){ |
| dojo.config.ieForceActiveXXhr=true; |
| } |
| |
| d.isQuirks = document.compatMode == "BackCompat"; |
| |
| // TODO: is the HTML LANG attribute relevant? |
| d.locale = dojo.config.locale || (d.isIE ? n.userLanguage : n.language).toLowerCase(); |
| |
| // These are in order of decreasing likelihood; this will change in time. |
| d._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; |
| |
| d._xhrObj = function(){ |
| // summary: |
| // does the work of portably generating a new XMLHTTPRequest object. |
| var http, last_e; |
| if(!dojo.isIE || !dojo.config.ieForceActiveXXhr){ |
| try{ http = new XMLHttpRequest(); }catch(e){} |
| } |
| if(!http){ |
| for(var i=0; i<3; ++i){ |
| var progid = d._XMLHTTP_PROGIDS[i]; |
| try{ |
| http = new ActiveXObject(progid); |
| }catch(e){ |
| last_e = e; |
| } |
| |
| if(http){ |
| d._XMLHTTP_PROGIDS = [progid]; // so faster next time |
| break; |
| } |
| } |
| } |
| |
| if(!http){ |
| throw new Error("XMLHTTP not available: "+last_e); |
| } |
| |
| return http; // XMLHTTPRequest instance |
| } |
| |
| d._isDocumentOk = function(http){ |
| var stat = http.status || 0, |
| lp = location.protocol; |
| return (stat >= 200 && stat < 300) || // Boolean |
| stat == 304 || // allow any 2XX response code |
| stat == 1223 || // get it out of the cache |
| // Internet Explorer mangled the status code |
| // Internet Explorer mangled the status code OR we're Titanium/browser chrome/chrome extension requesting a local file |
| (!stat && (lp == "file:" || lp == "chrome:" || lp == "chrome-extension:" || lp == "app:")); |
| } |
| |
| //See if base tag is in use. |
| //This is to fix http://trac.dojotoolkit.org/ticket/3973, |
| //but really, we need to find out how to get rid of the dojo._Url reference |
| //below and still have DOH work with the dojo.i18n test following some other |
| //test that uses the test frame to load a document (trac #2757). |
| //Opera still has problems, but perhaps a larger issue of base tag support |
| //with XHR requests (hasBase is true, but the request is still made to document |
| //path, not base path). |
| var owloc = window.location+""; |
| var base = document.getElementsByTagName("base"); |
| var hasBase = (base && base.length > 0); |
| |
| d._getText = function(/*URI*/ uri, /*Boolean*/ fail_ok){ |
| // summary: Read the contents of the specified uri and return those contents. |
| // uri: |
| // A relative or absolute uri. If absolute, it still must be in |
| // the same "domain" as we are. |
| // fail_ok: |
| // Default false. If fail_ok and loading fails, return null |
| // instead of throwing. |
| // returns: The response text. null is returned when there is a |
| // failure and failure is okay (an exception otherwise) |
| |
| // NOTE: must be declared before scope switches ie. this._xhrObj() |
| var http = d._xhrObj(); |
| |
| if(!hasBase && dojo._Url){ |
| uri = (new dojo._Url(owloc, uri)).toString(); |
| } |
| |
| if(d.config.cacheBust){ |
| //Make sure we have a string before string methods are used on uri |
| uri += ""; |
| uri += (uri.indexOf("?") == -1 ? "?" : "&") + String(d.config.cacheBust).replace(/\W+/g,""); |
| } |
| |
| http.open('GET', uri, false); |
| try{ |
| http.send(null); |
| if(!d._isDocumentOk(http)){ |
| var err = Error("Unable to load "+uri+" status:"+ http.status); |
| err.status = http.status; |
| err.responseText = http.responseText; |
| throw err; |
| } |
| }catch(e){ |
| if(fail_ok){ return null; } // null |
| // rethrow the exception |
| throw e; |
| } |
| return http.responseText; // String |
| } |
| |
| |
| var _w = window; |
| var _handleNodeEvent = function(/*String*/evtName, /*Function*/fp){ |
| // summary: |
| // non-destructively adds the specified function to the node's |
| // evtName handler. |
| // evtName: should be in the form "onclick" for "onclick" handlers. |
| // Make sure you pass in the "on" part. |
| var _a = _w.attachEvent || _w.addEventListener; |
| evtName = _w.attachEvent ? evtName : evtName.substring(2); |
| _a(evtName, function(){ |
| fp.apply(_w, arguments); |
| }, false); |
| }; |
| |
| |
| d._windowUnloaders = []; |
| |
| d.windowUnloaded = function(){ |
| // summary: |
| // signal fired by impending window destruction. You may use |
| // dojo.addOnWindowUnload() to register a listener for this |
| // event. NOTE: if you wish to dojo.connect() to this method |
| // to perform page/application cleanup, be aware that this |
| // event WILL NOT fire if no handler has been registered with |
| // dojo.addOnWindowUnload. This behavior started in Dojo 1.3. |
| // Previous versions always triggered dojo.windowUnloaded. See |
| // dojo.addOnWindowUnload for more info. |
| var mll = d._windowUnloaders; |
| while(mll.length){ |
| (mll.pop())(); |
| } |
| d = null; |
| }; |
| |
| var _onWindowUnloadAttached = 0; |
| d.addOnWindowUnload = function(/*Object?|Function?*/obj, /*String|Function?*/functionName){ |
| // summary: |
| // registers a function to be triggered when window.onunload |
| // fires. |
| // description: |
| // The first time that addOnWindowUnload is called Dojo |
| // will register a page listener to trigger your unload |
| // handler with. Note that registering these handlers may |
| // destory "fastback" page caching in browsers that support |
| // it. Be careful trying to modify the DOM or access |
| // JavaScript properties during this phase of page unloading: |
| // they may not always be available. Consider |
| // dojo.addOnUnload() if you need to modify the DOM or do |
| // heavy JavaScript work since it fires at the eqivalent of |
| // the page's "onbeforeunload" event. |
| // example: |
| // | dojo.addOnWindowUnload(functionPointer) |
| // | dojo.addOnWindowUnload(object, "functionName"); |
| // | dojo.addOnWindowUnload(object, function(){ /* ... */}); |
| |
| d._onto(d._windowUnloaders, obj, functionName); |
| if(!_onWindowUnloadAttached){ |
| _onWindowUnloadAttached = 1; |
| _handleNodeEvent("onunload", d.windowUnloaded); |
| } |
| }; |
| |
| var _onUnloadAttached = 0; |
| d.addOnUnload = function(/*Object?|Function?*/obj, /*String|Function?*/functionName){ |
| // summary: |
| // registers a function to be triggered when the page unloads. |
| // description: |
| // The first time that addOnUnload is called Dojo will |
| // register a page listener to trigger your unload handler |
| // with. |
| // |
| // In a browser enviroment, the functions will be triggered |
| // during the window.onbeforeunload event. Be careful of doing |
| // too much work in an unload handler. onbeforeunload can be |
| // triggered if a link to download a file is clicked, or if |
| // the link is a javascript: link. In these cases, the |
| // onbeforeunload event fires, but the document is not |
| // actually destroyed. So be careful about doing destructive |
| // operations in a dojo.addOnUnload callback. |
| // |
| // Further note that calling dojo.addOnUnload will prevent |
| // browsers from using a "fast back" cache to make page |
| // loading via back button instantaneous. |
| // example: |
| // | dojo.addOnUnload(functionPointer) |
| // | dojo.addOnUnload(object, "functionName") |
| // | dojo.addOnUnload(object, function(){ /* ... */}); |
| |
| d._onto(d._unloaders, obj, functionName); |
| if(!_onUnloadAttached){ |
| _onUnloadAttached = 1; |
| _handleNodeEvent("onbeforeunload", dojo.unloaded); |
| } |
| }; |
| |
| })(); |
| |
| //START DOMContentLoaded |
| dojo._initFired = false; |
| dojo._loadInit = function(e){ |
| if(dojo._scrollIntervalId){ |
| clearInterval(dojo._scrollIntervalId); |
| dojo._scrollIntervalId = 0; |
| } |
| |
| if(!dojo._initFired){ |
| dojo._initFired = true; |
| |
| //Help out IE to avoid memory leak. |
| if(!dojo.config.afterOnLoad && window.detachEvent){ |
| window.detachEvent("onload", dojo._loadInit); |
| } |
| |
| if(dojo._inFlightCount == 0){ |
| dojo._modulesLoaded(); |
| } |
| } |
| } |
| |
| if(!dojo.config.afterOnLoad){ |
| if(document.addEventListener){ |
| //Standards. Hooray! Assumption here that if standards based, |
| //it knows about DOMContentLoaded. It is OK if it does not, the fall through |
| //to window onload should be good enough. |
| document.addEventListener("DOMContentLoaded", dojo._loadInit, false); |
| window.addEventListener("load", dojo._loadInit, false); |
| }else if(window.attachEvent){ |
| window.attachEvent("onload", dojo._loadInit); |
| |
| //DOMContentLoaded approximation. Diego Perini found this MSDN article |
| //that indicates doScroll is available after DOM ready, so do a setTimeout |
| //to check when it is available. |
| //http://msdn.microsoft.com/en-us/library/ms531426.aspx |
| if(!dojo.config.skipIeDomLoaded && self === self.top){ |
| dojo._scrollIntervalId = setInterval(function (){ |
| try{ |
| //When dojo is loaded into an iframe in an IE HTML Application |
| //(HTA), such as in a selenium test, javascript in the iframe |
| //can't see anything outside of it, so self===self.top is true, |
| //but the iframe is not the top window and doScroll will be |
| //available before document.body is set. Test document.body |
| //before trying the doScroll trick |
| if(document.body){ |
| document.documentElement.doScroll("left"); |
| dojo._loadInit(); |
| } |
| }catch (e){} |
| }, 30); |
| } |
| } |
| } |
| |
| if(dojo.isIE){ |
| try{ |
| (function(){ |
| document.namespaces.add("v", "urn:schemas-microsoft-com:vml"); |
| var vmlElems = ["*", "group", "roundrect", "oval", "shape", "rect", "imagedata", "path", "textpath", "text"], |
| i = 0, l = 1, s = document.createStyleSheet(); |
| if(dojo.isIE >= 8){ |
| i = 1; |
| l = vmlElems.length; |
| } |
| for(; i < l; ++i){ |
| s.addRule("v\\:" + vmlElems[i], "behavior:url(#default#VML); display:inline-block"); |
| } |
| })(); |
| }catch(e){} |
| } |
| //END DOMContentLoaded |
| |
| |
| /* |
| OpenAjax.subscribe("OpenAjax", "onload", function(){ |
| if(dojo._inFlightCount == 0){ |
| dojo._modulesLoaded(); |
| } |
| }); |
| |
| OpenAjax.subscribe("OpenAjax", "onunload", function(){ |
| dojo.unloaded(); |
| }); |
| */ |
| } //if (typeof window != 'undefined') |
| |
| //Register any module paths set up in djConfig. Need to do this |
| //in the hostenvs since hostenv_browser can read djConfig from a |
| //script tag's attribute. |
| (function(){ |
| var mp = dojo.config["modulePaths"]; |
| if(mp){ |
| for(var param in mp){ |
| dojo.registerModulePath(param, mp[param]); |
| } |
| } |
| })(); |
| |
| //Load debug code if necessary. |
| if(dojo.config.isDebug){ |
| dojo.require("dojo._firebug.firebug"); |
| } |
| |
| if(dojo.config.debugAtAllCosts){ |
| // this breaks the new AMD based module loader. The XDomain won't be necessary |
| // anyway if you switch to the asynchronous loader |
| //dojo.config.useXDomain = true; |
| //dojo.require("dojo._base._loader.loader_xd"); |
| dojo.require("dojo._base._loader.loader_debug"); |
| dojo.require("dojo.i18n"); |
| } |
| |
| |
| if(!dojo._hasResource["dojo._base.lang"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.lang"] = true; |
| dojo.provide("dojo._base.lang"); |
| |
| |
| (function(){ |
| var d = dojo, opts = Object.prototype.toString; |
| |
| // Crockford (ish) functions |
| |
| dojo.isString = function(/*anything*/ it){ |
| // summary: |
| // Return true if it is a String |
| return (typeof it == "string" || it instanceof String); // Boolean |
| }; |
| |
| dojo.isArray = function(/*anything*/ it){ |
| // summary: |
| // Return true if it is an Array. |
| // Does not work on Arrays created in other windows. |
| return it && (it instanceof Array || typeof it == "array"); // Boolean |
| }; |
| |
| dojo.isFunction = function(/*anything*/ it){ |
| // summary: |
| // Return true if it is a Function |
| return opts.call(it) === "[object Function]"; |
| }; |
| |
| dojo.isObject = function(/*anything*/ it){ |
| // summary: |
| // Returns true if it is a JavaScript object (or an Array, a Function |
| // or null) |
| return it !== undefined && |
| (it === null || typeof it == "object" || d.isArray(it) || d.isFunction(it)); // Boolean |
| }; |
| |
| dojo.isArrayLike = function(/*anything*/ it){ |
| // summary: |
| // similar to dojo.isArray() but more permissive |
| // description: |
| // Doesn't strongly test for "arrayness". Instead, settles for "isn't |
| // a string or number and has a length property". Arguments objects |
| // and DOM collections will return true when passed to |
| // dojo.isArrayLike(), but will return false when passed to |
| // dojo.isArray(). |
| // returns: |
| // If it walks like a duck and quacks like a duck, return `true` |
| return it && it !== undefined && // Boolean |
| // keep out built-in constructors (Number, String, ...) which have length |
| // properties |
| !d.isString(it) && !d.isFunction(it) && |
| !(it.tagName && it.tagName.toLowerCase() == 'form') && |
| (d.isArray(it) || isFinite(it.length)); |
| }; |
| |
| dojo.isAlien = function(/*anything*/ it){ |
| // summary: |
| // Returns true if it is a built-in function or some other kind of |
| // oddball that *should* report as a function but doesn't |
| return it && !d.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean |
| }; |
| |
| dojo.extend = function(/*Object*/ constructor, /*Object...*/ props){ |
| // summary: |
| // Adds all properties and methods of props to constructor's |
| // prototype, making them available to all instances created with |
| // constructor. |
| for(var i=1, l=arguments.length; i<l; i++){ |
| d._mixin(constructor.prototype, arguments[i]); |
| } |
| return constructor; // Object |
| }; |
| |
| dojo._hitchArgs = function(scope, method /*,...*/){ |
| var pre = d._toArray(arguments, 2); |
| var named = d.isString(method); |
| return function(){ |
| // arrayify arguments |
| var args = d._toArray(arguments); |
| // locate our method |
| var f = named ? (scope||d.global)[method] : method; |
| // invoke with collected args |
| return f && f.apply(scope || this, pre.concat(args)); // mixed |
| }; // Function |
| }; |
| |
| dojo.hitch = function(/*Object*/scope, /*Function|String*/method /*,...*/){ |
| // summary: |
| // Returns a function that will only ever execute in the a given scope. |
| // This allows for easy use of object member functions |
| // in callbacks and other places in which the "this" keyword may |
| // otherwise not reference the expected scope. |
| // Any number of default positional arguments may be passed as parameters |
| // beyond "method". |
| // Each of these values will be used to "placehold" (similar to curry) |
| // for the hitched function. |
| // scope: |
| // The scope to use when method executes. If method is a string, |
| // scope is also the object containing method. |
| // method: |
| // A function to be hitched to scope, or the name of the method in |
| // scope to be hitched. |
| // example: |
| // | dojo.hitch(foo, "bar")(); |
| // runs foo.bar() in the scope of foo |
| // example: |
| // | dojo.hitch(foo, myFunction); |
| // returns a function that runs myFunction in the scope of foo |
| // example: |
| // Expansion on the default positional arguments passed along from |
| // hitch. Passed args are mixed first, additional args after. |
| // | var foo = { bar: function(a, b, c){ console.log(a, b, c); } }; |
| // | var fn = dojo.hitch(foo, "bar", 1, 2); |
| // | fn(3); // logs "1, 2, 3" |
| // example: |
| // | var foo = { bar: 2 }; |
| // | dojo.hitch(foo, function(){ this.bar = 10; })(); |
| // execute an anonymous function in scope of foo |
| |
| if(arguments.length > 2){ |
| return d._hitchArgs.apply(d, arguments); // Function |
| } |
| if(!method){ |
| method = scope; |
| scope = null; |
| } |
| if(d.isString(method)){ |
| scope = scope || d.global; |
| if(!scope[method]){ throw(['dojo.hitch: scope["', method, '"] is null (scope="', scope, '")'].join('')); } |
| return function(){ return scope[method].apply(scope, arguments || []); }; // Function |
| } |
| return !scope ? method : function(){ return method.apply(scope, arguments || []); }; // Function |
| }; |
| |
| /*===== |
| dojo.delegate = function(obj, props){ |
| // summary: |
| // Returns a new object which "looks" to obj for properties which it |
| // does not have a value for. Optionally takes a bag of properties to |
| // seed the returned object with initially. |
| // description: |
| // This is a small implementaton of the Boodman/Crockford delegation |
| // pattern in JavaScript. An intermediate object constructor mediates |
| // the prototype chain for the returned object, using it to delegate |
| // down to obj for property lookup when object-local lookup fails. |
| // This can be thought of similarly to ES4's "wrap", save that it does |
| // not act on types but rather on pure objects. |
| // obj: |
| // The object to delegate to for properties not found directly on the |
| // return object or in props. |
| // props: |
| // an object containing properties to assign to the returned object |
| // returns: |
| // an Object of anonymous type |
| // example: |
| // | var foo = { bar: "baz" }; |
| // | var thinger = dojo.delegate(foo, { thud: "xyzzy"}); |
| // | thinger.bar == "baz"; // delegated to foo |
| // | foo.thud == undefined; // by definition |
| // | thinger.thud == "xyzzy"; // mixed in from props |
| // | foo.bar = "thonk"; |
| // | thinger.bar == "thonk"; // still delegated to foo's bar |
| } |
| =====*/ |
| |
| dojo.delegate = dojo._delegate = (function(){ |
| // boodman/crockford delegation w/ cornford optimization |
| function TMP(){} |
| return function(obj, props){ |
| TMP.prototype = obj; |
| var tmp = new TMP(); |
| TMP.prototype = null; |
| if(props){ |
| d._mixin(tmp, props); |
| } |
| return tmp; // Object |
| }; |
| })(); |
| |
| /*===== |
| dojo._toArray = function(obj, offset, startWith){ |
| // summary: |
| // Converts an array-like object (i.e. arguments, DOMCollection) to an |
| // array. Returns a new Array with the elements of obj. |
| // obj: Object |
| // the object to "arrayify". We expect the object to have, at a |
| // minimum, a length property which corresponds to integer-indexed |
| // properties. |
| // offset: Number? |
| // the location in obj to start iterating from. Defaults to 0. |
| // Optional. |
| // startWith: Array? |
| // An array to pack with the properties of obj. If provided, |
| // properties in obj are appended at the end of startWith and |
| // startWith is the returned array. |
| } |
| =====*/ |
| |
| var efficient = function(obj, offset, startWith){ |
| return (startWith||[]).concat(Array.prototype.slice.call(obj, offset||0)); |
| }; |
| |
| var slow = function(obj, offset, startWith){ |
| var arr = startWith||[]; |
| for(var x = offset || 0; x < obj.length; x++){ |
| arr.push(obj[x]); |
| } |
| return arr; |
| }; |
| |
| dojo._toArray = |
| d.isIE ? function(obj){ |
| return ((obj.item) ? slow : efficient).apply(this, arguments); |
| } : |
| efficient; |
| |
| dojo.partial = function(/*Function|String*/method /*, ...*/){ |
| // summary: |
| // similar to hitch() except that the scope object is left to be |
| // whatever the execution context eventually becomes. |
| // description: |
| // Calling dojo.partial is the functional equivalent of calling: |
| // | dojo.hitch(null, funcName, ...); |
| var arr = [ null ]; |
| return d.hitch.apply(d, arr.concat(d._toArray(arguments))); // Function |
| }; |
| |
| var extraNames = d._extraNames, extraLen = extraNames.length, empty = {}; |
| |
| dojo.clone = function(/*anything*/ o){ |
| // summary: |
| // Clones objects (including DOM nodes) and all children. |
| // Warning: do not clone cyclic structures. |
| if(!o || typeof o != "object" || d.isFunction(o)){ |
| // null, undefined, any non-object, or function |
| return o; // anything |
| } |
| if(o.nodeType && "cloneNode" in o){ |
| // DOM Node |
| return o.cloneNode(true); // Node |
| } |
| if(o instanceof Date){ |
| // Date |
| return new Date(o.getTime()); // Date |
| } |
| if(o instanceof RegExp){ |
| // RegExp |
| return new RegExp(o); // RegExp |
| } |
| var r, i, l, s, name; |
| if(d.isArray(o)){ |
| // array |
| r = []; |
| for(i = 0, l = o.length; i < l; ++i){ |
| if(i in o){ |
| r.push(d.clone(o[i])); |
| } |
| } |
| // we don't clone functions for performance reasons |
| // }else if(d.isFunction(o)){ |
| // // function |
| // r = function(){ return o.apply(this, arguments); }; |
| }else{ |
| // generic objects |
| r = o.constructor ? new o.constructor() : {}; |
| } |
| for(name in o){ |
| // the "tobj" condition avoid copying properties in "source" |
| // inherited from Object.prototype. For example, if target has a custom |
| // toString() method, don't overwrite it with the toString() method |
| // that source inherited from Object.prototype |
| s = o[name]; |
| if(!(name in r) || (r[name] !== s && (!(name in empty) || empty[name] !== s))){ |
| r[name] = d.clone(s); |
| } |
| } |
| // IE doesn't recognize some custom functions in for..in |
| if(extraLen){ |
| for(i = 0; i < extraLen; ++i){ |
| name = extraNames[i]; |
| s = o[name]; |
| if(!(name in r) || (r[name] !== s && (!(name in empty) || empty[name] !== s))){ |
| r[name] = s; // functions only, we don't clone them |
| } |
| } |
| } |
| return r; // Object |
| }; |
| |
| /*===== |
| dojo.trim = function(str){ |
| // summary: |
| // Trims whitespace from both sides of the string |
| // str: String |
| // String to be trimmed |
| // returns: String |
| // Returns the trimmed string |
| // description: |
| // This version of trim() was selected for inclusion into the base due |
| // to its compact size and relatively good performance |
| // (see [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript) |
| // Uses String.prototype.trim instead, if available. |
| // The fastest but longest version of this function is located at |
| // dojo.string.trim() |
| return ""; // String |
| } |
| =====*/ |
| |
| dojo.trim = String.prototype.trim ? |
| function(str){ return str.trim(); } : |
| function(str){ return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); }; |
| |
| /*===== |
| dojo.replace = function(tmpl, map, pattern){ |
| // summary: |
| // Performs parameterized substitutions on a string. Throws an |
| // exception if any parameter is unmatched. |
| // tmpl: String |
| // String to be used as a template. |
| // map: Object|Function |
| // If an object, it is used as a dictionary to look up substitutions. |
| // If a function, it is called for every substitution with following |
| // parameters: a whole match, a name, an offset, and the whole template |
| // string (see https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/replace |
| // for more details). |
| // pattern: RegEx? |
| // Optional regular expression objects that overrides the default pattern. |
| // Must be global and match one item. The default is: /\{([^\}]+)\}/g, |
| // which matches patterns like that: "{xxx}", where "xxx" is any sequence |
| // of characters, which doesn't include "}". |
| // returns: String |
| // Returns the substituted string. |
| // example: |
| // | // uses a dictionary for substitutions: |
| // | dojo.replace("Hello, {name.first} {name.last} AKA {nick}!", |
| // | { |
| // | nick: "Bob", |
| // | name: { |
| // | first: "Robert", |
| // | middle: "X", |
| // | last: "Cringely" |
| // | } |
| // | }); |
| // | // returns: Hello, Robert Cringely AKA Bob! |
| // example: |
| // | // uses an array for substitutions: |
| // | dojo.replace("Hello, {0} {2}!", |
| // | ["Robert", "X", "Cringely"]); |
| // | // returns: Hello, Robert Cringely! |
| // example: |
| // | // uses a function for substitutions: |
| // | function sum(a){ |
| // | var t = 0; |
| // | dojo.forEach(a, function(x){ t += x; }); |
| // | return t; |
| // | } |
| // | dojo.replace( |
| // | "{count} payments averaging {avg} USD per payment.", |
| // | dojo.hitch( |
| // | { payments: [11, 16, 12] }, |
| // | function(_, key){ |
| // | switch(key){ |
| // | case "count": return this.payments.length; |
| // | case "min": return Math.min.apply(Math, this.payments); |
| // | case "max": return Math.max.apply(Math, this.payments); |
| // | case "sum": return sum(this.payments); |
| // | case "avg": return sum(this.payments) / this.payments.length; |
| // | } |
| // | } |
| // | ) |
| // | ); |
| // | // prints: 3 payments averaging 13 USD per payment. |
| // example: |
| // | // uses an alternative PHP-like pattern for substitutions: |
| // | dojo.replace("Hello, ${0} ${2}!", |
| // | ["Robert", "X", "Cringely"], /\$\{([^\}]+)\}/g); |
| // | // returns: Hello, Robert Cringely! |
| return ""; // String |
| } |
| =====*/ |
| |
| var _pattern = /\{([^\}]+)\}/g; |
| dojo.replace = function(tmpl, map, pattern){ |
| return tmpl.replace(pattern || _pattern, d.isFunction(map) ? |
| map : function(_, k){ return d.getObject(k, false, map); }); |
| }; |
| })(); |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.array"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.array"] = true; |
| dojo.provide("dojo._base.array"); |
| |
| |
| |
| (function(){ |
| var _getParts = function(arr, obj, cb){ |
| return [ |
| (typeof arr == "string") ? arr.split("") : arr, |
| obj || dojo.global, |
| // FIXME: cache the anonymous functions we create here? |
| (typeof cb == "string") ? new Function("item", "index", "array", cb) : cb |
| ]; |
| }; |
| |
| var everyOrSome = function(/*Boolean*/every, /*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){ |
| var _p = _getParts(arr, thisObject, callback); arr = _p[0]; |
| for(var i=0,l=arr.length; i<l; ++i){ |
| var result = !!_p[2].call(_p[1], arr[i], i, arr); |
| if(every ^ result){ |
| return result; // Boolean |
| } |
| } |
| return every; // Boolean |
| }; |
| |
| dojo.mixin(dojo, { |
| indexOf: function( /*Array*/ array, |
| /*Object*/ value, |
| /*Integer?*/ fromIndex, |
| /*Boolean?*/ findLast){ |
| // summary: |
| // locates the first index of the provided value in the |
| // passed array. If the value is not found, -1 is returned. |
| // description: |
| // This method corresponds to the JavaScript 1.6 Array.indexOf method, with one difference: when |
| // run over sparse arrays, the Dojo function invokes the callback for every index whereas JavaScript |
| // 1.6's indexOf skips the holes in the sparse array. |
| // For details on this method, see: |
| // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/indexOf |
| |
| var step = 1, end = array.length || 0, i = 0; |
| if(findLast){ |
| i = end - 1; |
| step = end = -1; |
| } |
| if(fromIndex != undefined){ i = fromIndex; } |
| if((findLast && i > end) || i < end){ |
| for(; i != end; i += step){ |
| if(array[i] == value){ return i; } |
| } |
| } |
| return -1; // Number |
| }, |
| |
| lastIndexOf: function(/*Array*/array, /*Object*/value, /*Integer?*/fromIndex){ |
| // summary: |
| // locates the last index of the provided value in the passed |
| // array. If the value is not found, -1 is returned. |
| // description: |
| // This method corresponds to the JavaScript 1.6 Array.lastIndexOf method, with one difference: when |
| // run over sparse arrays, the Dojo function invokes the callback for every index whereas JavaScript |
| // 1.6's lastIndexOf skips the holes in the sparse array. |
| // For details on this method, see: |
| // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/lastIndexOf |
| return dojo.indexOf(array, value, fromIndex, true); // Number |
| }, |
| |
| forEach: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){ |
| // summary: |
| // for every item in arr, callback is invoked. Return values are ignored. |
| // If you want to break out of the loop, consider using dojo.every() or dojo.some(). |
| // forEach does not allow breaking out of the loop over the items in arr. |
| // arr: |
| // the array to iterate over. If a string, operates on individual characters. |
| // callback: |
| // a function is invoked with three arguments: item, index, and array |
| // thisObject: |
| // may be used to scope the call to callback |
| // description: |
| // This function corresponds to the JavaScript 1.6 Array.forEach() method, with one difference: when |
| // run over sparse arrays, this implemenation passes the "holes" in the sparse array to |
| // the callback function with a value of undefined. JavaScript 1.6's forEach skips the holes in the sparse array. |
| // For more details, see: |
| // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/forEach |
| // example: |
| // | // log out all members of the array: |
| // | dojo.forEach( |
| // | [ "thinger", "blah", "howdy", 10 ], |
| // | function(item){ |
| // | console.log(item); |
| // | } |
| // | ); |
| // example: |
| // | // log out the members and their indexes |
| // | dojo.forEach( |
| // | [ "thinger", "blah", "howdy", 10 ], |
| // | function(item, idx, arr){ |
| // | console.log(item, "at index:", idx); |
| // | } |
| // | ); |
| // example: |
| // | // use a scoped object member as the callback |
| // | |
| // | var obj = { |
| // | prefix: "logged via obj.callback:", |
| // | callback: function(item){ |
| // | console.log(this.prefix, item); |
| // | } |
| // | }; |
| // | |
| // | // specifying the scope function executes the callback in that scope |
| // | dojo.forEach( |
| // | [ "thinger", "blah", "howdy", 10 ], |
| // | obj.callback, |
| // | obj |
| // | ); |
| // | |
| // | // alternately, we can accomplish the same thing with dojo.hitch() |
| // | dojo.forEach( |
| // | [ "thinger", "blah", "howdy", 10 ], |
| // | dojo.hitch(obj, "callback") |
| // | ); |
| |
| // match the behavior of the built-in forEach WRT empty arrs |
| if(!arr || !arr.length){ return; } |
| |
| // FIXME: there are several ways of handilng thisObject. Is |
| // dojo.global always the default context? |
| var _p = _getParts(arr, thisObject, callback); arr = _p[0]; |
| for(var i=0,l=arr.length; i<l; ++i){ |
| _p[2].call(_p[1], arr[i], i, arr); |
| } |
| }, |
| |
| every: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){ |
| // summary: |
| // Determines whether or not every item in arr satisfies the |
| // condition implemented by callback. |
| // arr: |
| // the array to iterate on. If a string, operates on individual characters. |
| // callback: |
| // a function is invoked with three arguments: item, index, |
| // and array and returns true if the condition is met. |
| // thisObject: |
| // may be used to scope the call to callback |
| // description: |
| // This function corresponds to the JavaScript 1.6 Array.every() method, with one difference: when |
| // run over sparse arrays, this implemenation passes the "holes" in the sparse array to |
| // the callback function with a value of undefined. JavaScript 1.6's every skips the holes in the sparse array. |
| // For more details, see: |
| // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/every |
| // example: |
| // | // returns false |
| // | dojo.every([1, 2, 3, 4], function(item){ return item>1; }); |
| // example: |
| // | // returns true |
| // | dojo.every([1, 2, 3, 4], function(item){ return item>0; }); |
| return everyOrSome(true, arr, callback, thisObject); // Boolean |
| }, |
| |
| some: function(/*Array|String*/arr, /*Function|String*/callback, /*Object?*/thisObject){ |
| // summary: |
| // Determines whether or not any item in arr satisfies the |
| // condition implemented by callback. |
| // arr: |
| // the array to iterate over. If a string, operates on individual characters. |
| // callback: |
| // a function is invoked with three arguments: item, index, |
| // and array and returns true if the condition is met. |
| // thisObject: |
| // may be used to scope the call to callback |
| // description: |
| // This function corresponds to the JavaScript 1.6 Array.some() method, with one difference: when |
| // run over sparse arrays, this implemenation passes the "holes" in the sparse array to |
| // the callback function with a value of undefined. JavaScript 1.6's some skips the holes in the sparse array. |
| // For more details, see: |
| // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/some |
| // example: |
| // | // is true |
| // | dojo.some([1, 2, 3, 4], function(item){ return item>1; }); |
| // example: |
| // | // is false |
| // | dojo.some([1, 2, 3, 4], function(item){ return item<1; }); |
| return everyOrSome(false, arr, callback, thisObject); // Boolean |
| }, |
| |
| map: function(/*Array|String*/arr, /*Function|String*/callback, /*Function?*/thisObject){ |
| // summary: |
| // applies callback to each element of arr and returns |
| // an Array with the results |
| // arr: |
| // the array to iterate on. If a string, operates on |
| // individual characters. |
| // callback: |
| // a function is invoked with three arguments, (item, index, |
| // array), and returns a value |
| // thisObject: |
| // may be used to scope the call to callback |
| // description: |
| // This function corresponds to the JavaScript 1.6 Array.map() method, with one difference: when |
| // run over sparse arrays, this implemenation passes the "holes" in the sparse array to |
| // the callback function with a value of undefined. JavaScript 1.6's map skips the holes in the sparse array. |
| // For more details, see: |
| // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/map |
| // example: |
| // | // returns [2, 3, 4, 5] |
| // | dojo.map([1, 2, 3, 4], function(item){ return item+1 }); |
| |
| var _p = _getParts(arr, thisObject, callback); arr = _p[0]; |
| var outArr = (arguments[3] ? (new arguments[3]()) : []); |
| for(var i=0,l=arr.length; i<l; ++i){ |
| outArr.push(_p[2].call(_p[1], arr[i], i, arr)); |
| } |
| return outArr; // Array |
| }, |
| |
| filter: function(/*Array*/arr, /*Function|String*/callback, /*Object?*/thisObject){ |
| // summary: |
| // Returns a new Array with those items from arr that match the |
| // condition implemented by callback. |
| // arr: |
| // the array to iterate over. |
| // callback: |
| // a function that is invoked with three arguments (item, |
| // index, array). The return of this function is expected to |
| // be a boolean which determines whether the passed-in item |
| // will be included in the returned array. |
| // thisObject: |
| // may be used to scope the call to callback |
| // description: |
| // This function corresponds to the JavaScript 1.6 Array.filter() method, with one difference: when |
| // run over sparse arrays, this implemenation passes the "holes" in the sparse array to |
| // the callback function with a value of undefined. JavaScript 1.6's filter skips the holes in the sparse array. |
| // For more details, see: |
| // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter |
| // example: |
| // | // returns [2, 3, 4] |
| // | dojo.filter([1, 2, 3, 4], function(item){ return item>1; }); |
| |
| var _p = _getParts(arr, thisObject, callback); arr = _p[0]; |
| var outArr = []; |
| for(var i=0,l=arr.length; i<l; ++i){ |
| if(_p[2].call(_p[1], arr[i], i, arr)){ |
| outArr.push(arr[i]); |
| } |
| } |
| return outArr; // Array |
| } |
| }); |
| })(); |
| /* |
| */ |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.declare"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.declare"] = true; |
| dojo.provide("dojo._base.declare"); |
| |
| |
| |
| |
| (function(){ |
| var d = dojo, mix = d._mixin, op = Object.prototype, opts = op.toString, |
| xtor = new Function, counter = 0, cname = "constructor"; |
| |
| function err(msg, cls){ throw new Error("declare" + (cls ? " " + cls : "") + ": " + msg); } |
| |
| // C3 Method Resolution Order (see http://www.python.org/download/releases/2.3/mro/) |
| function c3mro(bases, className){ |
| var result = [], roots = [{cls: 0, refs: []}], nameMap = {}, clsCount = 1, |
| l = bases.length, i = 0, j, lin, base, top, proto, rec, name, refs; |
| |
| // build a list of bases naming them if needed |
| for(; i < l; ++i){ |
| base = bases[i]; |
| if(!base){ |
| err("mixin #" + i + " is unknown. Did you use dojo.require to pull it in?", className); |
| }else if(opts.call(base) != "[object Function]"){ |
| err("mixin #" + i + " is not a callable constructor.", className); |
| } |
| lin = base._meta ? base._meta.bases : [base]; |
| top = 0; |
| // add bases to the name map |
| for(j = lin.length - 1; j >= 0; --j){ |
| proto = lin[j].prototype; |
| if(!proto.hasOwnProperty("declaredClass")){ |
| proto.declaredClass = "uniqName_" + (counter++); |
| } |
| name = proto.declaredClass; |
| if(!nameMap.hasOwnProperty(name)){ |
| nameMap[name] = {count: 0, refs: [], cls: lin[j]}; |
| ++clsCount; |
| } |
| rec = nameMap[name]; |
| if(top && top !== rec){ |
| rec.refs.push(top); |
| ++top.count; |
| } |
| top = rec; |
| } |
| ++top.count; |
| roots[0].refs.push(top); |
| } |
| |
| // remove classes without external references recursively |
| while(roots.length){ |
| top = roots.pop(); |
| result.push(top.cls); |
| --clsCount; |
| // optimization: follow a single-linked chain |
| while(refs = top.refs, refs.length == 1){ |
| top = refs[0]; |
| if(!top || --top.count){ |
| // branch or end of chain => do not end to roots |
| top = 0; |
| break; |
| } |
| result.push(top.cls); |
| --clsCount; |
| } |
| if(top){ |
| // branch |
| for(i = 0, l = refs.length; i < l; ++i){ |
| top = refs[i]; |
| if(!--top.count){ |
| roots.push(top); |
| } |
| } |
| } |
| } |
| if(clsCount){ |
| err("can't build consistent linearization", className); |
| } |
| |
| // calculate the superclass offset |
| base = bases[0]; |
| result[0] = base ? |
| base._meta && base === result[result.length - base._meta.bases.length] ? |
| base._meta.bases.length : 1 : 0; |
| |
| return result; |
| } |
| |
| function inherited(args, a, f){ |
| var name, chains, bases, caller, meta, base, proto, opf, pos, |
| cache = this._inherited = this._inherited || {}; |
| |
| // crack arguments |
| if(typeof args == "string"){ |
| name = args; |
| args = a; |
| a = f; |
| } |
| f = 0; |
| |
| caller = args.callee; |
| name = name || caller.nom; |
| if(!name){ |
| err("can't deduce a name to call inherited()", this.declaredClass); |
| } |
| |
| meta = this.constructor._meta; |
| bases = meta.bases; |
| |
| pos = cache.p; |
| if(name != cname){ |
| // method |
| if(cache.c !== caller){ |
| // cache bust |
| pos = 0; |
| base = bases[0]; |
| meta = base._meta; |
| if(meta.hidden[name] !== caller){ |
| // error detection |
| chains = meta.chains; |
| if(chains && typeof chains[name] == "string"){ |
| err("calling chained method with inherited: " + name, this.declaredClass); |
| } |
| // find caller |
| do{ |
| meta = base._meta; |
| proto = base.prototype; |
| if(meta && (proto[name] === caller && proto.hasOwnProperty(name) || meta.hidden[name] === caller)){ |
| break; |
| } |
| }while(base = bases[++pos]); // intentional assignment |
| pos = base ? pos : -1; |
| } |
| } |
| // find next |
| base = bases[++pos]; |
| if(base){ |
| proto = base.prototype; |
| if(base._meta && proto.hasOwnProperty(name)){ |
| f = proto[name]; |
| }else{ |
| opf = op[name]; |
| do{ |
| proto = base.prototype; |
| f = proto[name]; |
| if(f && (base._meta ? proto.hasOwnProperty(name) : f !== opf)){ |
| break; |
| } |
| }while(base = bases[++pos]); // intentional assignment |
| } |
| } |
| f = base && f || op[name]; |
| }else{ |
| // constructor |
| if(cache.c !== caller){ |
| // cache bust |
| pos = 0; |
| meta = bases[0]._meta; |
| if(meta && meta.ctor !== caller){ |
| // error detection |
| chains = meta.chains; |
| if(!chains || chains.constructor !== "manual"){ |
| err("calling chained constructor with inherited", this.declaredClass); |
| } |
| // find caller |
| while(base = bases[++pos]){ // intentional assignment |
| meta = base._meta; |
| if(meta && meta.ctor === caller){ |
| break; |
| } |
| } |
| pos = base ? pos : -1; |
| } |
| } |
| // find next |
| while(base = bases[++pos]){ // intentional assignment |
| meta = base._meta; |
| f = meta ? meta.ctor : base; |
| if(f){ |
| break; |
| } |
| } |
| f = base && f; |
| } |
| |
| // cache the found super method |
| cache.c = f; |
| cache.p = pos; |
| |
| // now we have the result |
| if(f){ |
| return a === true ? f : f.apply(this, a || args); |
| } |
| // intentionally if a super method was not found |
| } |
| |
| function getInherited(name, args){ |
| if(typeof name == "string"){ |
| return this.inherited(name, args, true); |
| } |
| return this.inherited(name, true); |
| } |
| |
| // emulation of "instanceof" |
| function isInstanceOf(cls){ |
| var bases = this.constructor._meta.bases; |
| for(var i = 0, l = bases.length; i < l; ++i){ |
| if(bases[i] === cls){ |
| return true; |
| } |
| } |
| return this instanceof cls; |
| } |
| |
| function mixOwn(target, source){ |
| var name, i = 0, l = d._extraNames.length; |
| // add props adding metadata for incoming functions skipping a constructor |
| for(name in source){ |
| if(name != cname && source.hasOwnProperty(name)){ |
| target[name] = source[name]; |
| } |
| } |
| // process unenumerable methods on IE |
| for(; i < l; ++i){ |
| name = d._extraNames[i]; |
| if(name != cname && source.hasOwnProperty(name)){ |
| target[name] = source[name]; |
| } |
| } |
| } |
| |
| // implementation of safe mixin function |
| function safeMixin(target, source){ |
| var name, t, i = 0, l = d._extraNames.length; |
| // add props adding metadata for incoming functions skipping a constructor |
| for(name in source){ |
| t = source[name]; |
| if((t !== op[name] || !(name in op)) && name != cname){ |
| if(opts.call(t) == "[object Function]"){ |
| // non-trivial function method => attach its name |
| t.nom = name; |
| } |
| target[name] = t; |
| } |
| } |
| // process unenumerable methods on IE |
| for(; i < l; ++i){ |
| name = d._extraNames[i]; |
| t = source[name]; |
| if((t !== op[name] || !(name in op)) && name != cname){ |
| if(opts.call(t) == "[object Function]"){ |
| // non-trivial function method => attach its name |
| t.nom = name; |
| } |
| target[name] = t; |
| } |
| } |
| return target; |
| } |
| |
| function extend(source){ |
| safeMixin(this.prototype, source); |
| return this; |
| } |
| |
| // chained constructor compatible with the legacy dojo.declare() |
| function chainedConstructor(bases, ctorSpecial){ |
| return function(){ |
| var a = arguments, args = a, a0 = a[0], f, i, m, |
| l = bases.length, preArgs; |
| |
| if(!(this instanceof a.callee)){ |
| // not called via new, so force it |
| return applyNew(a); |
| } |
| |
| //this._inherited = {}; |
| // perform the shaman's rituals of the original dojo.declare() |
| // 1) call two types of the preamble |
| if(ctorSpecial && (a0 && a0.preamble || this.preamble)){ |
| // full blown ritual |
| preArgs = new Array(bases.length); |
| // prepare parameters |
| preArgs[0] = a; |
| for(i = 0;;){ |
| // process the preamble of the 1st argument |
| a0 = a[0]; |
| if(a0){ |
| f = a0.preamble; |
| if(f){ |
| a = f.apply(this, a) || a; |
| } |
| } |
| // process the preamble of this class |
| f = bases[i].prototype; |
| f = f.hasOwnProperty("preamble") && f.preamble; |
| if(f){ |
| a = f.apply(this, a) || a; |
| } |
| // one peculiarity of the preamble: |
| // it is called if it is not needed, |
| // e.g., there is no constructor to call |
| // let's watch for the last constructor |
| // (see ticket #9795) |
| if(++i == l){ |
| break; |
| } |
| preArgs[i] = a; |
| } |
| } |
| // 2) call all non-trivial constructors using prepared arguments |
| for(i = l - 1; i >= 0; --i){ |
| f = bases[i]; |
| m = f._meta; |
| f = m ? m.ctor : f; |
| if(f){ |
| f.apply(this, preArgs ? preArgs[i] : a); |
| } |
| } |
| // 3) continue the original ritual: call the postscript |
| f = this.postscript; |
| if(f){ |
| f.apply(this, args); |
| } |
| }; |
| } |
| |
| |
| // chained constructor compatible with the legacy dojo.declare() |
| function singleConstructor(ctor, ctorSpecial){ |
| return function(){ |
| var a = arguments, t = a, a0 = a[0], f; |
| |
| if(!(this instanceof a.callee)){ |
| // not called via new, so force it |
| return applyNew(a); |
| } |
| |
| //this._inherited = {}; |
| // perform the shaman's rituals of the original dojo.declare() |
| // 1) call two types of the preamble |
| if(ctorSpecial){ |
| // full blown ritual |
| if(a0){ |
| // process the preamble of the 1st argument |
| f = a0.preamble; |
| if(f){ |
| t = f.apply(this, t) || t; |
| } |
| } |
| f = this.preamble; |
| if(f){ |
| // process the preamble of this class |
| f.apply(this, t); |
| // one peculiarity of the preamble: |
| // it is called even if it is not needed, |
| // e.g., there is no constructor to call |
| // let's watch for the last constructor |
| // (see ticket #9795) |
| } |
| } |
| // 2) call a constructor |
| if(ctor){ |
| ctor.apply(this, a); |
| } |
| // 3) continue the original ritual: call the postscript |
| f = this.postscript; |
| if(f){ |
| f.apply(this, a); |
| } |
| }; |
| } |
| |
| // plain vanilla constructor (can use inherited() to call its base constructor) |
| function simpleConstructor(bases){ |
| return function(){ |
| var a = arguments, i = 0, f, m; |
| |
| if(!(this instanceof a.callee)){ |
| // not called via new, so force it |
| return applyNew(a); |
| } |
| |
| //this._inherited = {}; |
| // perform the shaman's rituals of the original dojo.declare() |
| // 1) do not call the preamble |
| // 2) call the top constructor (it can use this.inherited()) |
| for(; f = bases[i]; ++i){ // intentional assignment |
| m = f._meta; |
| f = m ? m.ctor : f; |
| if(f){ |
| f.apply(this, a); |
| break; |
| } |
| } |
| // 3) call the postscript |
| f = this.postscript; |
| if(f){ |
| f.apply(this, a); |
| } |
| }; |
| } |
| |
| function chain(name, bases, reversed){ |
| return function(){ |
| var b, m, f, i = 0, step = 1; |
| if(reversed){ |
| i = bases.length - 1; |
| step = -1; |
| } |
| for(; b = bases[i]; i += step){ // intentional assignment |
| m = b._meta; |
| f = (m ? m.hidden : b.prototype)[name]; |
| if(f){ |
| f.apply(this, arguments); |
| } |
| } |
| }; |
| } |
| |
| // forceNew(ctor) |
| // return a new object that inherits from ctor.prototype but |
| // without actually running ctor on the object. |
| function forceNew(ctor){ |
| // create object with correct prototype using a do-nothing |
| // constructor |
| xtor.prototype = ctor.prototype; |
| var t = new xtor; |
| xtor.prototype = null; // clean up |
| return t; |
| } |
| |
| // applyNew(args) |
| // just like 'new ctor()' except that the constructor and its arguments come |
| // from args, which must be an array or an arguments object |
| function applyNew(args){ |
| // create an object with ctor's prototype but without |
| // calling ctor on it. |
| var ctor = args.callee, t = forceNew(ctor); |
| // execute the real constructor on the new object |
| ctor.apply(t, args); |
| return t; |
| } |
| |
| d.declare = function(className, superclass, props){ |
| // crack parameters |
| if(typeof className != "string"){ |
| props = superclass; |
| superclass = className; |
| className = ""; |
| } |
| props = props || {}; |
| |
| var proto, i, t, ctor, name, bases, chains, mixins = 1, parents = superclass; |
| |
| // build a prototype |
| if(opts.call(superclass) == "[object Array]"){ |
| // C3 MRO |
| bases = c3mro(superclass, className); |
| t = bases[0]; |
| mixins = bases.length - t; |
| superclass = bases[mixins]; |
| }else{ |
| bases = [0]; |
| if(superclass){ |
| if(opts.call(superclass) == "[object Function]"){ |
| t = superclass._meta; |
| bases = bases.concat(t ? t.bases : superclass); |
| }else{ |
| err("base class is not a callable constructor.", className); |
| } |
| }else if(superclass !== null){ |
| err("unknown base class. Did you use dojo.require to pull it in?", className); |
| } |
| } |
| if(superclass){ |
| for(i = mixins - 1;; --i){ |
| proto = forceNew(superclass); |
| if(!i){ |
| // stop if nothing to add (the last base) |
| break; |
| } |
| // mix in properties |
| t = bases[i]; |
| (t._meta ? mixOwn : mix)(proto, t.prototype); |
| // chain in new constructor |
| ctor = new Function; |
| ctor.superclass = superclass; |
| ctor.prototype = proto; |
| superclass = proto.constructor = ctor; |
| } |
| }else{ |
| proto = {}; |
| } |
| // add all properties |
| safeMixin(proto, props); |
| // add constructor |
| t = props.constructor; |
| if(t !== op.constructor){ |
| t.nom = cname; |
| proto.constructor = t; |
| } |
| |
| // collect chains and flags |
| for(i = mixins - 1; i; --i){ // intentional assignment |
| t = bases[i]._meta; |
| if(t && t.chains){ |
| chains = mix(chains || {}, t.chains); |
| } |
| } |
| if(proto["-chains-"]){ |
| chains = mix(chains || {}, proto["-chains-"]); |
| } |
| |
| // build ctor |
| t = !chains || !chains.hasOwnProperty(cname); |
| bases[0] = ctor = (chains && chains.constructor === "manual") ? simpleConstructor(bases) : |
| (bases.length == 1 ? singleConstructor(props.constructor, t) : chainedConstructor(bases, t)); |
| |
| // add meta information to the constructor |
| ctor._meta = {bases: bases, hidden: props, chains: chains, |
| parents: parents, ctor: props.constructor}; |
| ctor.superclass = superclass && superclass.prototype; |
| ctor.extend = extend; |
| ctor.prototype = proto; |
| proto.constructor = ctor; |
| |
| // add "standard" methods to the prototype |
| proto.getInherited = getInherited; |
| proto.inherited = inherited; |
| proto.isInstanceOf = isInstanceOf; |
| |
| // add name if specified |
| if(className){ |
| proto.declaredClass = className; |
| d.setObject(className, ctor); |
| } |
| |
| // build chains and add them to the prototype |
| if(chains){ |
| for(name in chains){ |
| if(proto[name] && typeof chains[name] == "string" && name != cname){ |
| t = proto[name] = chain(name, bases, chains[name] === "after"); |
| t.nom = name; |
| } |
| } |
| } |
| // chained methods do not return values |
| // no need to chain "invisible" functions |
| |
| return ctor; // Function |
| }; |
| |
| d.safeMixin = safeMixin; |
| |
| /*===== |
| dojo.declare = function(className, superclass, props){ |
| // summary: |
| // Create a feature-rich constructor from compact notation. |
| // className: String?: |
| // The optional name of the constructor (loosely, a "class") |
| // stored in the "declaredClass" property in the created prototype. |
| // It will be used as a global name for a created constructor. |
| // superclass: Function|Function[]: |
| // May be null, a Function, or an Array of Functions. This argument |
| // specifies a list of bases (the left-most one is the most deepest |
| // base). |
| // props: Object: |
| // An object whose properties are copied to the created prototype. |
| // Add an instance-initialization function by making it a property |
| // named "constructor". |
| // returns: |
| // New constructor function. |
| // description: |
| // Create a constructor using a compact notation for inheritance and |
| // prototype extension. |
| // |
| // Mixin ancestors provide a type of multiple inheritance. |
| // Prototypes of mixin ancestors are copied to the new class: |
| // changes to mixin prototypes will not affect classes to which |
| // they have been mixed in. |
| // |
| // Ancestors can be compound classes created by this version of |
| // dojo.declare. In complex cases all base classes are going to be |
| // linearized according to C3 MRO algorithm |
| // (see http://www.python.org/download/releases/2.3/mro/ for more |
| // details). |
| // |
| // "className" is cached in "declaredClass" property of the new class, |
| // if it was supplied. The immediate super class will be cached in |
| // "superclass" property of the new class. |
| // |
| // Methods in "props" will be copied and modified: "nom" property |
| // (the declared name of the method) will be added to all copied |
| // functions to help identify them for the internal machinery. Be |
| // very careful, while reusing methods: if you use the same |
| // function under different names, it can produce errors in some |
| // cases. |
| // |
| // It is possible to use constructors created "manually" (without |
| // dojo.declare) as bases. They will be called as usual during the |
| // creation of an instance, their methods will be chained, and even |
| // called by "this.inherited()". |
| // |
| // Special property "-chains-" governs how to chain methods. It is |
| // a dictionary, which uses method names as keys, and hint strings |
| // as values. If a hint string is "after", this method will be |
| // called after methods of its base classes. If a hint string is |
| // "before", this method will be called before methods of its base |
| // classes. |
| // |
| // If "constructor" is not mentioned in "-chains-" property, it will |
| // be chained using the legacy mode: using "after" chaining, |
| // calling preamble() method before each constructor, if available, |
| // and calling postscript() after all constructors were executed. |
| // If the hint is "after", it is chained as a regular method, but |
| // postscript() will be called after the chain of constructors. |
| // "constructor" cannot be chained "before", but it allows |
| // a special hint string: "manual", which means that constructors |
| // are not going to be chained in any way, and programmer will call |
| // them manually using this.inherited(). In the latter case |
| // postscript() will be called after the construction. |
| // |
| // All chaining hints are "inherited" from base classes and |
| // potentially can be overridden. Be very careful when overriding |
| // hints! Make sure that all chained methods can work in a proposed |
| // manner of chaining. |
| // |
| // Once a method was chained, it is impossible to unchain it. The |
| // only exception is "constructor". You don't need to define a |
| // method in order to supply a chaining hint. |
| // |
| // If a method is chained, it cannot use this.inherited() because |
| // all other methods in the hierarchy will be called automatically. |
| // |
| // Usually constructors and initializers of any kind are chained |
| // using "after" and destructors of any kind are chained as |
| // "before". Note that chaining assumes that chained methods do not |
| // return any value: any returned value will be discarded. |
| // |
| // example: |
| // | dojo.declare("my.classes.bar", my.classes.foo, { |
| // | // properties to be added to the class prototype |
| // | someValue: 2, |
| // | // initialization function |
| // | constructor: function(){ |
| // | this.myComplicatedObject = new ReallyComplicatedObject(); |
| // | }, |
| // | // other functions |
| // | someMethod: function(){ |
| // | doStuff(); |
| // | } |
| // | }); |
| // |
| // example: |
| // | var MyBase = dojo.declare(null, { |
| // | // constructor, properties, and methods go here |
| // | // ... |
| // | }); |
| // | var MyClass1 = dojo.declare(MyBase, { |
| // | // constructor, properties, and methods go here |
| // | // ... |
| // | }); |
| // | var MyClass2 = dojo.declare(MyBase, { |
| // | // constructor, properties, and methods go here |
| // | // ... |
| // | }); |
| // | var MyDiamond = dojo.declare([MyClass1, MyClass2], { |
| // | // constructor, properties, and methods go here |
| // | // ... |
| // | }); |
| // |
| // example: |
| // | var F = function(){ console.log("raw constructor"); }; |
| // | F.prototype.method = function(){ |
| // | console.log("raw method"); |
| // | }; |
| // | var A = dojo.declare(F, { |
| // | constructor: function(){ |
| // | console.log("A.constructor"); |
| // | }, |
| // | method: function(){ |
| // | console.log("before calling F.method..."); |
| // | this.inherited(arguments); |
| // | console.log("...back in A"); |
| // | } |
| // | }); |
| // | new A().method(); |
| // | // will print: |
| // | // raw constructor |
| // | // A.constructor |
| // | // before calling F.method... |
| // | // raw method |
| // | // ...back in A |
| // |
| // example: |
| // | var A = dojo.declare(null, { |
| // | "-chains-": { |
| // | destroy: "before" |
| // | } |
| // | }); |
| // | var B = dojo.declare(A, { |
| // | constructor: function(){ |
| // | console.log("B.constructor"); |
| // | }, |
| // | destroy: function(){ |
| // | console.log("B.destroy"); |
| // | } |
| // | }); |
| // | var C = dojo.declare(B, { |
| // | constructor: function(){ |
| // | console.log("C.constructor"); |
| // | }, |
| // | destroy: function(){ |
| // | console.log("C.destroy"); |
| // | } |
| // | }); |
| // | new C().destroy(); |
| // | // prints: |
| // | // B.constructor |
| // | // C.constructor |
| // | // C.destroy |
| // | // B.destroy |
| // |
| // example: |
| // | var A = dojo.declare(null, { |
| // | "-chains-": { |
| // | constructor: "manual" |
| // | } |
| // | }); |
| // | var B = dojo.declare(A, { |
| // | constructor: function(){ |
| // | // ... |
| // | // call the base constructor with new parameters |
| // | this.inherited(arguments, [1, 2, 3]); |
| // | // ... |
| // | } |
| // | }); |
| // |
| // example: |
| // | var A = dojo.declare(null, { |
| // | "-chains-": { |
| // | m1: "before" |
| // | }, |
| // | m1: function(){ |
| // | console.log("A.m1"); |
| // | }, |
| // | m2: function(){ |
| // | console.log("A.m2"); |
| // | } |
| // | }); |
| // | var B = dojo.declare(A, { |
| // | "-chains-": { |
| // | m2: "after" |
| // | }, |
| // | m1: function(){ |
| // | console.log("B.m1"); |
| // | }, |
| // | m2: function(){ |
| // | console.log("B.m2"); |
| // | } |
| // | }); |
| // | var x = new B(); |
| // | x.m1(); |
| // | // prints: |
| // | // B.m1 |
| // | // A.m1 |
| // | x.m2(); |
| // | // prints: |
| // | // A.m2 |
| // | // B.m2 |
| return new Function(); // Function |
| }; |
| =====*/ |
| |
| /*===== |
| dojo.safeMixin = function(target, source){ |
| // summary: |
| // Mix in properties skipping a constructor and decorating functions |
| // like it is done by dojo.declare. |
| // target: Object |
| // Target object to accept new properties. |
| // source: Object |
| // Source object for new properties. |
| // description: |
| // This function is used to mix in properties like dojo._mixin does, |
| // but it skips a constructor property and decorates functions like |
| // dojo.declare does. |
| // |
| // It is meant to be used with classes and objects produced with |
| // dojo.declare. Functions mixed in with dojo.safeMixin can use |
| // this.inherited() like normal methods. |
| // |
| // This function is used to implement extend() method of a constructor |
| // produced with dojo.declare(). |
| // |
| // example: |
| // | var A = dojo.declare(null, { |
| // | m1: function(){ |
| // | console.log("A.m1"); |
| // | }, |
| // | m2: function(){ |
| // | console.log("A.m2"); |
| // | } |
| // | }); |
| // | var B = dojo.declare(A, { |
| // | m1: function(){ |
| // | this.inherited(arguments); |
| // | console.log("B.m1"); |
| // | } |
| // | }); |
| // | B.extend({ |
| // | m2: function(){ |
| // | this.inherited(arguments); |
| // | console.log("B.m2"); |
| // | } |
| // | }); |
| // | var x = new B(); |
| // | dojo.safeMixin(x, { |
| // | m1: function(){ |
| // | this.inherited(arguments); |
| // | console.log("X.m1"); |
| // | }, |
| // | m2: function(){ |
| // | this.inherited(arguments); |
| // | console.log("X.m2"); |
| // | } |
| // | }); |
| // | x.m2(); |
| // | // prints: |
| // | // A.m1 |
| // | // B.m1 |
| // | // X.m1 |
| }; |
| =====*/ |
| |
| /*===== |
| Object.inherited = function(name, args, newArgs){ |
| // summary: |
| // Calls a super method. |
| // name: String? |
| // The optional method name. Should be the same as the caller's |
| // name. Usually "name" is specified in complex dynamic cases, when |
| // the calling method was dynamically added, undecorated by |
| // dojo.declare, and it cannot be determined. |
| // args: Arguments |
| // The caller supply this argument, which should be the original |
| // "arguments". |
| // newArgs: Object? |
| // If "true", the found function will be returned without |
| // executing it. |
| // If Array, it will be used to call a super method. Otherwise |
| // "args" will be used. |
| // returns: |
| // Whatever is returned by a super method, or a super method itself, |
| // if "true" was specified as newArgs. |
| // description: |
| // This method is used inside method of classes produced with |
| // dojo.declare to call a super method (next in the chain). It is |
| // used for manually controlled chaining. Consider using the regular |
| // chaining, because it is faster. Use "this.inherited()" only in |
| // complex cases. |
| // |
| // This method cannot me called from automatically chained |
| // constructors including the case of a special (legacy) |
| // constructor chaining. It cannot be called from chained methods. |
| // |
| // If "this.inherited()" cannot find the next-in-chain method, it |
| // does nothing and returns "undefined". The last method in chain |
| // can be a default method implemented in Object, which will be |
| // called last. |
| // |
| // If "name" is specified, it is assumed that the method that |
| // received "args" is the parent method for this call. It is looked |
| // up in the chain list and if it is found the next-in-chain method |
| // is called. If it is not found, the first-in-chain method is |
| // called. |
| // |
| // If "name" is not specified, it will be derived from the calling |
| // method (using a methoid property "nom"). |
| // |
| // example: |
| // | var B = dojo.declare(A, { |
| // | method1: function(a, b, c){ |
| // | this.inherited(arguments); |
| // | }, |
| // | method2: function(a, b){ |
| // | return this.inherited(arguments, [a + b]); |
| // | } |
| // | }); |
| // | // next method is not in the chain list because it is added |
| // | // manually after the class was created. |
| // | B.prototype.method3 = function(){ |
| // | console.log("This is a dynamically-added method."); |
| // | this.inherited("method3", arguments); |
| // | }; |
| // example: |
| // | var B = dojo.declare(A, { |
| // | method: function(a, b){ |
| // | var super = this.inherited(arguments, true); |
| // | // ... |
| // | if(!super){ |
| // | console.log("there is no super method"); |
| // | return 0; |
| // | } |
| // | return super.apply(this, arguments); |
| // | } |
| // | }); |
| return {}; // Object |
| } |
| =====*/ |
| |
| /*===== |
| Object.getInherited = function(name, args){ |
| // summary: |
| // Returns a super method. |
| // name: String? |
| // The optional method name. Should be the same as the caller's |
| // name. Usually "name" is specified in complex dynamic cases, when |
| // the calling method was dynamically added, undecorated by |
| // dojo.declare, and it cannot be determined. |
| // args: Arguments |
| // The caller supply this argument, which should be the original |
| // "arguments". |
| // returns: |
| // Returns a super method (Function) or "undefined". |
| // description: |
| // This method is a convenience method for "this.inherited()". |
| // It uses the same algorithm but instead of executing a super |
| // method, it returns it, or "undefined" if not found. |
| // |
| // example: |
| // | var B = dojo.declare(A, { |
| // | method: function(a, b){ |
| // | var super = this.getInherited(arguments); |
| // | // ... |
| // | if(!super){ |
| // | console.log("there is no super method"); |
| // | return 0; |
| // | } |
| // | return super.apply(this, arguments); |
| // | } |
| // | }); |
| return {}; // Object |
| } |
| =====*/ |
| |
| /*===== |
| Object.isInstanceOf = function(cls){ |
| // summary: |
| // Checks the inheritance chain to see if it is inherited from this |
| // class. |
| // cls: Function |
| // Class constructor. |
| // returns: |
| // "true", if this object is inherited from this class, "false" |
| // otherwise. |
| // description: |
| // This method is used with instances of classes produced with |
| // dojo.declare to determine of they support a certain interface or |
| // not. It models "instanceof" operator. |
| // |
| // example: |
| // | var A = dojo.declare(null, { |
| // | // constructor, properties, and methods go here |
| // | // ... |
| // | }); |
| // | var B = dojo.declare(null, { |
| // | // constructor, properties, and methods go here |
| // | // ... |
| // | }); |
| // | var C = dojo.declare([A, B], { |
| // | // constructor, properties, and methods go here |
| // | // ... |
| // | }); |
| // | var D = dojo.declare(A, { |
| // | // constructor, properties, and methods go here |
| // | // ... |
| // | }); |
| // | |
| // | var a = new A(), b = new B(), c = new C(), d = new D(); |
| // | |
| // | console.log(a.isInstanceOf(A)); // true |
| // | console.log(b.isInstanceOf(A)); // false |
| // | console.log(c.isInstanceOf(A)); // true |
| // | console.log(d.isInstanceOf(A)); // true |
| // | |
| // | console.log(a.isInstanceOf(B)); // false |
| // | console.log(b.isInstanceOf(B)); // true |
| // | console.log(c.isInstanceOf(B)); // true |
| // | console.log(d.isInstanceOf(B)); // false |
| // | |
| // | console.log(a.isInstanceOf(C)); // false |
| // | console.log(b.isInstanceOf(C)); // false |
| // | console.log(c.isInstanceOf(C)); // true |
| // | console.log(d.isInstanceOf(C)); // false |
| // | |
| // | console.log(a.isInstanceOf(D)); // false |
| // | console.log(b.isInstanceOf(D)); // false |
| // | console.log(c.isInstanceOf(D)); // false |
| // | console.log(d.isInstanceOf(D)); // true |
| return {}; // Object |
| } |
| =====*/ |
| |
| /*===== |
| Object.extend = function(source){ |
| // summary: |
| // Adds all properties and methods of source to constructor's |
| // prototype, making them available to all instances created with |
| // constructor. This method is specific to constructors created with |
| // dojo.declare. |
| // source: Object |
| // Source object which properties are going to be copied to the |
| // constructor's prototype. |
| // description: |
| // Adds source properties to the constructor's prototype. It can |
| // override existing properties. |
| // |
| // This method is similar to dojo.extend function, but it is specific |
| // to constructors produced by dojo.declare. It is implemented |
| // using dojo.safeMixin, and it skips a constructor property, |
| // and properly decorates copied functions. |
| // |
| // example: |
| // | var A = dojo.declare(null, { |
| // | m1: function(){}, |
| // | s1: "Popokatepetl" |
| // | }); |
| // | A.extend({ |
| // | m1: function(){}, |
| // | m2: function(){}, |
| // | f1: true, |
| // | d1: 42 |
| // | }); |
| }; |
| =====*/ |
| })(); |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.connect"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.connect"] = true; |
| dojo.provide("dojo._base.connect"); |
| |
| |
| |
| // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA |
| |
| // low-level delegation machinery |
| dojo._listener = { |
| // create a dispatcher function |
| getDispatcher: function(){ |
| // following comments pulled out-of-line to prevent cloning them |
| // in the returned function. |
| // - indices (i) that are really in the array of listeners (ls) will |
| // not be in Array.prototype. This is the 'sparse array' trick |
| // that keeps us safe from libs that take liberties with built-in |
| // objects |
| // - listener is invoked with current scope (this) |
| return function(){ |
| var ap = Array.prototype, c = arguments.callee, ls = c._listeners, t = c.target, |
| // return value comes from original target function |
| r = t && t.apply(this, arguments), |
| // make local copy of listener array so it is immutable during processing |
| i, lls = [].concat(ls) |
| ; |
| |
| // invoke listeners after target function |
| for(i in lls){ |
| if(!(i in ap)){ |
| lls[i].apply(this, arguments); |
| } |
| } |
| // return value comes from original target function |
| return r; |
| }; |
| }, |
| // add a listener to an object |
| add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){ |
| // Whenever 'method' is invoked, 'listener' will have the same scope. |
| // Trying to supporting a context object for the listener led to |
| // complexity. |
| // Non trivial to provide 'once' functionality here |
| // because listener could be the result of a dojo.hitch call, |
| // in which case two references to the same hitch target would not |
| // be equivalent. |
| source = source || dojo.global; |
| // The source method is either null, a dispatcher, or some other function |
| var f = source[method]; |
| // Ensure a dispatcher |
| if(!f || !f._listeners){ |
| var d = dojo._listener.getDispatcher(); |
| // original target function is special |
| d.target = f; |
| // dispatcher holds a list of listeners |
| d._listeners = []; |
| // redirect source to dispatcher |
| f = source[method] = d; |
| } |
| // The contract is that a handle is returned that can |
| // identify this listener for disconnect. |
| // |
| // The type of the handle is private. Here is it implemented as Integer. |
| // DOM event code has this same contract but handle is Function |
| // in non-IE browsers. |
| // |
| // We could have separate lists of before and after listeners. |
| return f._listeners.push(listener); /*Handle*/ |
| }, |
| // remove a listener from an object |
| remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){ |
| var f = (source || dojo.global)[method]; |
| // remember that handle is the index+1 (0 is not a valid handle) |
| if(f && f._listeners && handle--){ |
| delete f._listeners[handle]; |
| } |
| } |
| }; |
| |
| // Multiple delegation for arbitrary methods. |
| |
| // This unit knows nothing about DOM, but we include DOM aware documentation |
| // and dontFix argument here to help the autodocs. Actual DOM aware code is in |
| // event.js. |
| |
| dojo.connect = function(/*Object|null*/ obj, |
| /*String*/ event, |
| /*Object|null*/ context, |
| /*String|Function*/ method, |
| /*Boolean?*/ dontFix){ |
| // summary: |
| // `dojo.connect` is the core event handling and delegation method in |
| // Dojo. It allows one function to "listen in" on the execution of |
| // any other, triggering the second whenever the first is called. Many |
| // listeners may be attached to a function, and source functions may |
| // be either regular function calls or DOM events. |
| // |
| // description: |
| // Connects listeners to actions, so that after event fires, a |
| // listener is called with the same arguments passed to the original |
| // function. |
| // |
| // Since `dojo.connect` allows the source of events to be either a |
| // "regular" JavaScript function or a DOM event, it provides a uniform |
| // interface for listening to all the types of events that an |
| // application is likely to deal with though a single, unified |
| // interface. DOM programmers may want to think of it as |
| // "addEventListener for everything and anything". |
| // |
| // When setting up a connection, the `event` parameter must be a |
| // string that is the name of the method/event to be listened for. If |
| // `obj` is null, `dojo.global` is assumed, meaning that connections |
| // to global methods are supported but also that you may inadvertently |
| // connect to a global by passing an incorrect object name or invalid |
| // reference. |
| // |
| // `dojo.connect` generally is forgiving. If you pass the name of a |
| // function or method that does not yet exist on `obj`, connect will |
| // not fail, but will instead set up a stub method. Similarly, null |
| // arguments may simply be omitted such that fewer than 4 arguments |
| // may be required to set up a connection See the examples for details. |
| // |
| // The return value is a handle that is needed to |
| // remove this connection with `dojo.disconnect`. |
| // |
| // obj: |
| // The source object for the event function. |
| // Defaults to `dojo.global` if null. |
| // If obj is a DOM node, the connection is delegated |
| // to the DOM event manager (unless dontFix is true). |
| // |
| // event: |
| // String name of the event function in obj. |
| // I.e. identifies a property `obj[event]`. |
| // |
| // context: |
| // The object that method will receive as "this". |
| // |
| // If context is null and method is a function, then method |
| // inherits the context of event. |
| // |
| // If method is a string then context must be the source |
| // object object for method (context[method]). If context is null, |
| // dojo.global is used. |
| // |
| // method: |
| // A function reference, or name of a function in context. |
| // The function identified by method fires after event does. |
| // method receives the same arguments as the event. |
| // See context argument comments for information on method's scope. |
| // |
| // dontFix: |
| // If obj is a DOM node, set dontFix to true to prevent delegation |
| // of this connection to the DOM event manager. |
| // |
| // example: |
| // When obj.onchange(), do ui.update(): |
| // | dojo.connect(obj, "onchange", ui, "update"); |
| // | dojo.connect(obj, "onchange", ui, ui.update); // same |
| // |
| // example: |
| // Using return value for disconnect: |
| // | var link = dojo.connect(obj, "onchange", ui, "update"); |
| // | ... |
| // | dojo.disconnect(link); |
| // |
| // example: |
| // When onglobalevent executes, watcher.handler is invoked: |
| // | dojo.connect(null, "onglobalevent", watcher, "handler"); |
| // |
| // example: |
| // When ob.onCustomEvent executes, customEventHandler is invoked: |
| // | dojo.connect(ob, "onCustomEvent", null, "customEventHandler"); |
| // | dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same |
| // |
| // example: |
| // When ob.onCustomEvent executes, customEventHandler is invoked |
| // with the same scope (this): |
| // | dojo.connect(ob, "onCustomEvent", null, customEventHandler); |
| // | dojo.connect(ob, "onCustomEvent", customEventHandler); // same |
| // |
| // example: |
| // When globalEvent executes, globalHandler is invoked |
| // with the same scope (this): |
| // | dojo.connect(null, "globalEvent", null, globalHandler); |
| // | dojo.connect("globalEvent", globalHandler); // same |
| |
| // normalize arguments |
| var a=arguments, args=[], i=0; |
| // if a[0] is a String, obj was omitted |
| args.push(dojo.isString(a[0]) ? null : a[i++], a[i++]); |
| // if the arg-after-next is a String or Function, context was NOT omitted |
| var a1 = a[i+1]; |
| args.push(dojo.isString(a1)||dojo.isFunction(a1) ? a[i++] : null, a[i++]); |
| // absorb any additional arguments |
| for(var l=a.length; i<l; i++){ args.push(a[i]); } |
| // do the actual work |
| return dojo._connect.apply(this, args); /*Handle*/ |
| } |
| |
| // used by non-browser hostenvs. always overriden by event.js |
| dojo._connect = function(obj, event, context, method){ |
| var l=dojo._listener, h=l.add(obj, event, dojo.hitch(context, method)); |
| return [obj, event, h, l]; // Handle |
| }; |
| |
| dojo.disconnect = function(/*Handle*/ handle){ |
| // summary: |
| // Remove a link created by dojo.connect. |
| // description: |
| // Removes the connection between event and the method referenced by handle. |
| // handle: |
| // the return value of the dojo.connect call that created the connection. |
| if(handle && handle[0] !== undefined){ |
| dojo._disconnect.apply(this, handle); |
| // let's not keep this reference |
| delete handle[0]; |
| } |
| }; |
| |
| dojo._disconnect = function(obj, event, handle, listener){ |
| listener.remove(obj, event, handle); |
| }; |
| |
| // topic publish/subscribe |
| |
| dojo._topics = {}; |
| |
| dojo.subscribe = function(/*String*/ topic, /*Object|null*/ context, /*String|Function*/ method){ |
| // summary: |
| // Attach a listener to a named topic. The listener function is invoked whenever the |
| // named topic is published (see: dojo.publish). |
| // Returns a handle which is needed to unsubscribe this listener. |
| // context: |
| // Scope in which method will be invoked, or null for default scope. |
| // method: |
| // The name of a function in context, or a function reference. This is the function that |
| // is invoked when topic is published. |
| // example: |
| // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }); |
| // | dojo.publish("alerts", [ "read this", "hello world" ]); |
| |
| // support for 2 argument invocation (omitting context) depends on hitch |
| return [topic, dojo._listener.add(dojo._topics, topic, dojo.hitch(context, method))]; /*Handle*/ |
| }; |
| |
| dojo.unsubscribe = function(/*Handle*/ handle){ |
| // summary: |
| // Remove a topic listener. |
| // handle: |
| // The handle returned from a call to subscribe. |
| // example: |
| // | var alerter = dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }; |
| // | ... |
| // | dojo.unsubscribe(alerter); |
| if(handle){ |
| dojo._listener.remove(dojo._topics, handle[0], handle[1]); |
| } |
| }; |
| |
| dojo.publish = function(/*String*/ topic, /*Array*/ args){ |
| // summary: |
| // Invoke all listener method subscribed to topic. |
| // topic: |
| // The name of the topic to publish. |
| // args: |
| // An array of arguments. The arguments will be applied |
| // to each topic subscriber (as first class parameters, via apply). |
| // example: |
| // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }; |
| // | dojo.publish("alerts", [ "read this", "hello world" ]); |
| |
| // Note that args is an array, which is more efficient vs variable length |
| // argument list. Ideally, var args would be implemented via Array |
| // throughout the APIs. |
| var f = dojo._topics[topic]; |
| if(f){ |
| f.apply(this, args||[]); |
| } |
| }; |
| |
| dojo.connectPublisher = function( /*String*/ topic, |
| /*Object|null*/ obj, |
| /*String*/ event){ |
| // summary: |
| // Ensure that every time obj.event() is called, a message is published |
| // on the topic. Returns a handle which can be passed to |
| // dojo.disconnect() to disable subsequent automatic publication on |
| // the topic. |
| // topic: |
| // The name of the topic to publish. |
| // obj: |
| // The source object for the event function. Defaults to dojo.global |
| // if null. |
| // event: |
| // The name of the event function in obj. |
| // I.e. identifies a property obj[event]. |
| // example: |
| // | dojo.connectPublisher("/ajax/start", dojo, "xhrGet"); |
| var pf = function(){ dojo.publish(topic, arguments); } |
| return event ? dojo.connect(obj, event, pf) : dojo.connect(obj, pf); //Handle |
| }; |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.Deferred"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.Deferred"] = true; |
| dojo.provide("dojo._base.Deferred"); |
| |
| |
| |
| (function(){ |
| var mutator = function(){}; |
| var freeze = Object.freeze || function(){}; |
| // A deferred provides an API for creating and resolving a promise. |
| dojo.Deferred = function(/*Function?*/canceller){ |
| // summary: |
| // Deferreds provide a generic means for encapsulating an asynchronous |
| // operation and notifying users of the completion and result of the operation. |
| // description: |
| // The dojo.Deferred API is based on the concept of promises that provide a |
| // generic interface into the eventual completion of an asynchronous action. |
| // The motivation for promises fundamentally is about creating a |
| // separation of concerns that allows one to achieve the same type of |
| // call patterns and logical data flow in asynchronous code as can be |
| // achieved in synchronous code. Promises allows one |
| // to be able to call a function purely with arguments needed for |
| // execution, without conflating the call with concerns of whether it is |
| // sync or async. One shouldn't need to alter a call's arguments if the |
| // implementation switches from sync to async (or vice versa). By having |
| // async functions return promises, the concerns of making the call are |
| // separated from the concerns of asynchronous interaction (which are |
| // handled by the promise). |
| // |
| // The dojo.Deferred is a type of promise that provides methods for fulfilling the |
| // promise with a successful result or an error. The most important method for |
| // working with Dojo's promises is the then() method, which follows the |
| // CommonJS proposed promise API. An example of using a Dojo promise: |
| // |
| // | var resultingPromise = someAsyncOperation.then(function(result){ |
| // | ... handle result ... |
| // | }, |
| // | function(error){ |
| // | ... handle error ... |
| // | }); |
| // |
| // The .then() call returns a new promise that represents the result of the |
| // execution of the callback. The callbacks will never affect the original promises value. |
| // |
| // The dojo.Deferred instances also provide the following functions for backwards compatibility: |
| // |
| // * addCallback(handler) |
| // * addErrback(handler) |
| // * callback(result) |
| // * errback(result) |
| // |
| // Callbacks are allowed to return promises themselves, so |
| // you can build complicated sequences of events with ease. |
| // |
| // The creator of the Deferred may specify a canceller. The canceller |
| // is a function that will be called if Deferred.cancel is called |
| // before the Deferred fires. You can use this to implement clean |
| // aborting of an XMLHttpRequest, etc. Note that cancel will fire the |
| // deferred with a CancelledError (unless your canceller returns |
| // another kind of error), so the errbacks should be prepared to |
| // handle that error for cancellable Deferreds. |
| // example: |
| // | var deferred = new dojo.Deferred(); |
| // | setTimeout(function(){ deferred.callback({success: true}); }, 1000); |
| // | return deferred; |
| // example: |
| // Deferred objects are often used when making code asynchronous. It |
| // may be easiest to write functions in a synchronous manner and then |
| // split code using a deferred to trigger a response to a long-lived |
| // operation. For example, instead of register a callback function to |
| // denote when a rendering operation completes, the function can |
| // simply return a deferred: |
| // |
| // | // callback style: |
| // | function renderLotsOfData(data, callback){ |
| // | var success = false |
| // | try{ |
| // | for(var x in data){ |
| // | renderDataitem(data[x]); |
| // | } |
| // | success = true; |
| // | }catch(e){ } |
| // | if(callback){ |
| // | callback(success); |
| // | } |
| // | } |
| // |
| // | // using callback style |
| // | renderLotsOfData(someDataObj, function(success){ |
| // | // handles success or failure |
| // | if(!success){ |
| // | promptUserToRecover(); |
| // | } |
| // | }); |
| // | // NOTE: no way to add another callback here!! |
| // example: |
| // Using a Deferred doesn't simplify the sending code any, but it |
| // provides a standard interface for callers and senders alike, |
| // providing both with a simple way to service multiple callbacks for |
| // an operation and freeing both sides from worrying about details |
| // such as "did this get called already?". With Deferreds, new |
| // callbacks can be added at any time. |
| // |
| // | // Deferred style: |
| // | function renderLotsOfData(data){ |
| // | var d = new dojo.Deferred(); |
| // | try{ |
| // | for(var x in data){ |
| // | renderDataitem(data[x]); |
| // | } |
| // | d.callback(true); |
| // | }catch(e){ |
| // | d.errback(new Error("rendering failed")); |
| // | } |
| // | return d; |
| // | } |
| // |
| // | // using Deferred style |
| // | renderLotsOfData(someDataObj).then(null, function(){ |
| // | promptUserToRecover(); |
| // | }); |
| // | // NOTE: addErrback and addCallback both return the Deferred |
| // | // again, so we could chain adding callbacks or save the |
| // | // deferred for later should we need to be notified again. |
| // example: |
| // In this example, renderLotsOfData is synchronous and so both |
| // versions are pretty artificial. Putting the data display on a |
| // timeout helps show why Deferreds rock: |
| // |
| // | // Deferred style and async func |
| // | function renderLotsOfData(data){ |
| // | var d = new dojo.Deferred(); |
| // | setTimeout(function(){ |
| // | try{ |
| // | for(var x in data){ |
| // | renderDataitem(data[x]); |
| // | } |
| // | d.callback(true); |
| // | }catch(e){ |
| // | d.errback(new Error("rendering failed")); |
| // | } |
| // | }, 100); |
| // | return d; |
| // | } |
| // |
| // | // using Deferred style |
| // | renderLotsOfData(someDataObj).then(null, function(){ |
| // | promptUserToRecover(); |
| // | }); |
| // |
| // Note that the caller doesn't have to change his code at all to |
| // handle the asynchronous case. |
| var result, finished, isError, head, nextListener; |
| var promise = (this.promise = {}); |
| |
| function complete(value){ |
| if(finished){ |
| throw new Error("This deferred has already been resolved"); |
| } |
| result = value; |
| finished = true; |
| notify(); |
| } |
| function notify(){ |
| var mutated; |
| while(!mutated && nextListener){ |
| var listener = nextListener; |
| nextListener = nextListener.next; |
| if((mutated = (listener.progress == mutator))){ // assignment and check |
| finished = false; |
| } |
| var func = (isError ? listener.error : listener.resolved); |
| if (func) { |
| try { |
| var newResult = func(result); |
| if (newResult && typeof newResult.then === "function") { |
| newResult.then(dojo.hitch(listener.deferred, "resolve"), dojo.hitch(listener.deferred, "reject")); |
| continue; |
| } |
| var unchanged = mutated && newResult === undefined; |
| if(mutated && !unchanged){ |
| isError = newResult instanceof Error; |
| } |
| listener.deferred[unchanged && isError ? "reject" : "resolve"](unchanged ? result : newResult); |
| } |
| catch (e) { |
| listener.deferred.reject(e); |
| } |
| }else { |
| if(isError){ |
| listener.deferred.reject(result); |
| }else{ |
| listener.deferred.resolve(result); |
| } |
| } |
| } |
| } |
| // calling resolve will resolve the promise |
| this.resolve = this.callback = function(value){ |
| // summary: |
| // Fulfills the Deferred instance successfully with the provide value |
| this.fired = 0; |
| this.results = [value, null]; |
| complete(value); |
| }; |
| |
| |
| // calling error will indicate that the promise failed |
| this.reject = this.errback = function(error){ |
| // summary: |
| // Fulfills the Deferred instance as an error with the provided error |
| isError = true; |
| this.fired = 1; |
| complete(error); |
| this.results = [null, error]; |
| if(!error || error.log !== false){ |
| (dojo.config.deferredOnError || function(x){ console.error(x); })(error); |
| } |
| }; |
| // call progress to provide updates on the progress on the completion of the promise |
| this.progress = function(update){ |
| // summary |
| // Send progress events to all listeners |
| var listener = nextListener; |
| while(listener){ |
| var progress = listener.progress; |
| progress && progress(update); |
| listener = listener.next; |
| } |
| }; |
| this.addCallbacks = function(/*Function?*/callback, /*Function?*/errback){ |
| this.then(callback, errback, mutator); |
| return this; |
| }; |
| // provide the implementation of the promise |
| this.then = promise.then = function(/*Function?*/resolvedCallback, /*Function?*/errorCallback, /*Function?*/progressCallback){ |
| // summary: |
| // Adds a fulfilledHandler, errorHandler, and progressHandler to be called for |
| // completion of a promise. The fulfilledHandler is called when the promise |
| // is fulfilled. The errorHandler is called when a promise fails. The |
| // progressHandler is called for progress events. All arguments are optional |
| // and non-function values are ignored. The progressHandler is not only an |
| // optional argument, but progress events are purely optional. Promise |
| // providers are not required to ever create progress events. |
| // |
| // This function will return a new promise that is fulfilled when the given |
| // fulfilledHandler or errorHandler callback is finished. This allows promise |
| // operations to be chained together. The value returned from the callback |
| // handler is the fulfillment value for the returned promise. If the callback |
| // throws an error, the returned promise will be moved to failed state. |
| // |
| // example: |
| // An example of using a CommonJS compliant promise: |
| // | asyncComputeTheAnswerToEverything(). |
| // | then(addTwo). |
| // | then(printResult, onError); |
| // | >44 |
| // |
| var returnDeferred = progressCallback == mutator ? this : new dojo.Deferred(promise.cancel); |
| var listener = { |
| resolved: resolvedCallback, |
| error: errorCallback, |
| progress: progressCallback, |
| deferred: returnDeferred |
| }; |
| if(nextListener){ |
| head = head.next = listener; |
| } |
| else{ |
| nextListener = head = listener; |
| } |
| if(finished){ |
| notify(); |
| } |
| return returnDeferred.promise; |
| }; |
| var deferred = this; |
| this.cancel = promise.cancel = function () { |
| // summary: |
| // Cancels the asynchronous operation |
| if(!finished){ |
| var error = canceller && canceller(deferred); |
| if(!finished){ |
| if (!(error instanceof Error)) { |
| error = new Error(error); |
| } |
| error.log = false; |
| deferred.reject(error); |
| } |
| } |
| }; |
| freeze(promise); |
| }; |
| dojo.extend(dojo.Deferred, { |
| addCallback: function (/*Function*/callback) { |
| return this.addCallbacks(dojo.hitch.apply(dojo, arguments)); |
| }, |
| |
| addErrback: function (/*Function*/errback) { |
| return this.addCallbacks(null, dojo.hitch.apply(dojo, arguments)); |
| }, |
| |
| addBoth: function (/*Function*/callback) { |
| var enclosed = dojo.hitch.apply(dojo, arguments); |
| return this.addCallbacks(enclosed, enclosed); |
| }, |
| fired: -1 |
| }); |
| })(); |
| dojo.when = function(promiseOrValue, /*Function?*/callback, /*Function?*/errback, /*Function?*/progressHandler){ |
| // summary: |
| // This provides normalization between normal synchronous values and |
| // asynchronous promises, so you can interact with them in a common way |
| // example: |
| // | function printFirstAndList(items){ |
| // | dojo.when(findFirst(items), console.log); |
| // | dojo.when(findLast(items), console.log); |
| // | } |
| // | function findFirst(items){ |
| // | return dojo.when(items, function(items){ |
| // | return items[0]; |
| // | }); |
| // | } |
| // | function findLast(items){ |
| // | return dojo.when(items, function(items){ |
| // | return items[items.length]; |
| // | }); |
| // | } |
| // And now all three of his functions can be used sync or async. |
| // | printFirstAndLast([1,2,3,4]) will work just as well as |
| // | printFirstAndLast(dojo.xhrGet(...)); |
| |
| if(promiseOrValue && typeof promiseOrValue.then === "function"){ |
| return promiseOrValue.then(callback, errback, progressHandler); |
| } |
| return callback(promiseOrValue); |
| }; |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.json"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.json"] = true; |
| dojo.provide("dojo._base.json"); |
| |
| |
| dojo.fromJson = function(/*String*/ json){ |
| // summary: |
| // Parses a [JSON](http://json.org) string to return a JavaScript object. |
| // description: |
| // Throws for invalid JSON strings, but it does not use a strict JSON parser. It |
| // delegates to eval(). The content passed to this method must therefore come |
| // from a trusted source. |
| // json: |
| // a string literal of a JSON item, for instance: |
| // `'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'` |
| |
| return eval("(" + json + ")"); // Object |
| }; |
| |
| dojo._escapeString = function(/*String*/str){ |
| //summary: |
| // Adds escape sequences for non-visual characters, double quote and |
| // backslash and surrounds with double quotes to form a valid string |
| // literal. |
| return ('"' + str.replace(/(["\\])/g, '\\$1') + '"'). |
| replace(/[\f]/g, "\\f").replace(/[\b]/g, "\\b").replace(/[\n]/g, "\\n"). |
| replace(/[\t]/g, "\\t").replace(/[\r]/g, "\\r"); // string |
| }; |
| |
| dojo.toJsonIndentStr = "\t"; |
| dojo.toJson = function(/*Object*/ it, /*Boolean?*/ prettyPrint, /*String?*/ _indentStr){ |
| // summary: |
| // Returns a [JSON](http://json.org) serialization of an object. |
| // description: |
| // Returns a [JSON](http://json.org) serialization of an object. |
| // Note that this doesn't check for infinite recursion, so don't do that! |
| // it: |
| // an object to be serialized. Objects may define their own |
| // serialization via a special "__json__" or "json" function |
| // property. If a specialized serializer has been defined, it will |
| // be used as a fallback. |
| // prettyPrint: |
| // if true, we indent objects and arrays to make the output prettier. |
| // The variable `dojo.toJsonIndentStr` is used as the indent string -- |
| // to use something other than the default (tab), change that variable |
| // before calling dojo.toJson(). |
| // _indentStr: |
| // private variable for recursive calls when pretty printing, do not use. |
| // example: |
| // simple serialization of a trivial object |
| // | var jsonStr = dojo.toJson({ howdy: "stranger!", isStrange: true }); |
| // | doh.is('{"howdy":"stranger!","isStrange":true}', jsonStr); |
| // example: |
| // a custom serializer for an objects of a particular class: |
| // | dojo.declare("Furby", null, { |
| // | furbies: "are strange", |
| // | furbyCount: 10, |
| // | __json__: function(){ |
| // | }, |
| // | }); |
| |
| if(it === undefined){ |
| return "undefined"; |
| } |
| var objtype = typeof it; |
| if(objtype == "number" || objtype == "boolean"){ |
| return it + ""; |
| } |
| if(it === null){ |
| return "null"; |
| } |
| if(dojo.isString(it)){ |
| return dojo._escapeString(it); |
| } |
| // recurse |
| var recurse = arguments.callee; |
| // short-circuit for objects that support "json" serialization |
| // if they return "self" then just pass-through... |
| var newObj; |
| _indentStr = _indentStr || ""; |
| var nextIndent = prettyPrint ? _indentStr + dojo.toJsonIndentStr : ""; |
| var tf = it.__json__||it.json; |
| if(dojo.isFunction(tf)){ |
| newObj = tf.call(it); |
| if(it !== newObj){ |
| return recurse(newObj, prettyPrint, nextIndent); |
| } |
| } |
| if(it.nodeType && it.cloneNode){ // isNode |
| // we can't seriailize DOM nodes as regular objects because they have cycles |
| // DOM nodes could be serialized with something like outerHTML, but |
| // that can be provided by users in the form of .json or .__json__ function. |
| throw new Error("Can't serialize DOM nodes"); |
| } |
| |
| var sep = prettyPrint ? " " : ""; |
| var newLine = prettyPrint ? "\n" : ""; |
| |
| // array |
| if(dojo.isArray(it)){ |
| var res = dojo.map(it, function(obj){ |
| var val = recurse(obj, prettyPrint, nextIndent); |
| if(typeof val != "string"){ |
| val = "undefined"; |
| } |
| return newLine + nextIndent + val; |
| }); |
| return "[" + res.join("," + sep) + newLine + _indentStr + "]"; |
| } |
| /* |
| // look in the registry |
| try { |
| window.o = it; |
| newObj = dojo.json.jsonRegistry.match(it); |
| return recurse(newObj, prettyPrint, nextIndent); |
| }catch(e){ |
| // console.log(e); |
| } |
| // it's a function with no adapter, skip it |
| */ |
| if(objtype == "function"){ |
| return null; // null |
| } |
| // generic object code path |
| var output = [], key; |
| for(key in it){ |
| var keyStr, val; |
| if(typeof key == "number"){ |
| keyStr = '"' + key + '"'; |
| }else if(typeof key == "string"){ |
| keyStr = dojo._escapeString(key); |
| }else{ |
| // skip non-string or number keys |
| continue; |
| } |
| val = recurse(it[key], prettyPrint, nextIndent); |
| if(typeof val != "string"){ |
| // skip non-serializable values |
| continue; |
| } |
| // FIXME: use += on Moz!! |
| // MOW NOTE: using += is a pain because you have to account for the dangling comma... |
| output.push(newLine + nextIndent + keyStr + ":" + sep + val); |
| } |
| return "{" + output.join("," + sep) + newLine + _indentStr + "}"; // String |
| }; |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.Color"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.Color"] = true; |
| dojo.provide("dojo._base.Color"); |
| |
| |
| |
| |
| (function(){ |
| |
| var d = dojo; |
| |
| dojo.Color = function(/*Array|String|Object*/ color){ |
| // summary: |
| // Takes a named string, hex string, array of rgb or rgba values, |
| // an object with r, g, b, and a properties, or another `dojo.Color` object |
| // and creates a new Color instance to work from. |
| // |
| // example: |
| // Work with a Color instance: |
| // | var c = new dojo.Color(); |
| // | c.setColor([0,0,0]); // black |
| // | var hex = c.toHex(); // #000000 |
| // |
| // example: |
| // Work with a node's color: |
| // | var color = dojo.style("someNode", "backgroundColor"); |
| // | var n = new dojo.Color(color); |
| // | // adjust the color some |
| // | n.r *= .5; |
| // | console.log(n.toString()); // rgb(128, 255, 255); |
| if(color){ this.setColor(color); } |
| }; |
| |
| // FIXME: |
| // there's got to be a more space-efficient way to encode or discover |
| // these!! Use hex? |
| dojo.Color.named = { |
| black: [0,0,0], |
| silver: [192,192,192], |
| gray: [128,128,128], |
| white: [255,255,255], |
| maroon: [128,0,0], |
| red: [255,0,0], |
| purple: [128,0,128], |
| fuchsia: [255,0,255], |
| green: [0,128,0], |
| lime: [0,255,0], |
| olive: [128,128,0], |
| yellow: [255,255,0], |
| navy: [0,0,128], |
| blue: [0,0,255], |
| teal: [0,128,128], |
| aqua: [0,255,255], |
| transparent: d.config.transparentColor || [255,255,255] |
| }; |
| |
| dojo.extend(dojo.Color, { |
| r: 255, g: 255, b: 255, a: 1, |
| _set: function(r, g, b, a){ |
| var t = this; t.r = r; t.g = g; t.b = b; t.a = a; |
| }, |
| setColor: function(/*Array|String|Object*/ color){ |
| // summary: |
| // Takes a named string, hex string, array of rgb or rgba values, |
| // an object with r, g, b, and a properties, or another `dojo.Color` object |
| // and sets this color instance to that value. |
| // |
| // example: |
| // | var c = new dojo.Color(); // no color |
| // | c.setColor("#ededed"); // greyish |
| if(d.isString(color)){ |
| d.colorFromString(color, this); |
| }else if(d.isArray(color)){ |
| d.colorFromArray(color, this); |
| }else{ |
| this._set(color.r, color.g, color.b, color.a); |
| if(!(color instanceof d.Color)){ this.sanitize(); } |
| } |
| return this; // dojo.Color |
| }, |
| sanitize: function(){ |
| // summary: |
| // Ensures the object has correct attributes |
| // description: |
| // the default implementation does nothing, include dojo.colors to |
| // augment it with real checks |
| return this; // dojo.Color |
| }, |
| toRgb: function(){ |
| // summary: |
| // Returns 3 component array of rgb values |
| // example: |
| // | var c = new dojo.Color("#000000"); |
| // | console.log(c.toRgb()); // [0,0,0] |
| var t = this; |
| return [t.r, t.g, t.b]; // Array |
| }, |
| toRgba: function(){ |
| // summary: |
| // Returns a 4 component array of rgba values from the color |
| // represented by this object. |
| var t = this; |
| return [t.r, t.g, t.b, t.a]; // Array |
| }, |
| toHex: function(){ |
| // summary: |
| // Returns a CSS color string in hexadecimal representation |
| // example: |
| // | console.log(new dojo.Color([0,0,0]).toHex()); // #000000 |
| var arr = d.map(["r", "g", "b"], function(x){ |
| var s = this[x].toString(16); |
| return s.length < 2 ? "0" + s : s; |
| }, this); |
| return "#" + arr.join(""); // String |
| }, |
| toCss: function(/*Boolean?*/ includeAlpha){ |
| // summary: |
| // Returns a css color string in rgb(a) representation |
| // example: |
| // | var c = new dojo.Color("#FFF").toCss(); |
| // | console.log(c); // rgb('255','255','255') |
| var t = this, rgb = t.r + ", " + t.g + ", " + t.b; |
| return (includeAlpha ? "rgba(" + rgb + ", " + t.a : "rgb(" + rgb) + ")"; // String |
| }, |
| toString: function(){ |
| // summary: |
| // Returns a visual representation of the color |
| return this.toCss(true); // String |
| } |
| }); |
| |
| dojo.blendColors = function( |
| /*dojo.Color*/ start, |
| /*dojo.Color*/ end, |
| /*Number*/ weight, |
| /*dojo.Color?*/ obj |
| ){ |
| // summary: |
| // Blend colors end and start with weight from 0 to 1, 0.5 being a 50/50 blend, |
| // can reuse a previously allocated dojo.Color object for the result |
| var t = obj || new d.Color(); |
| d.forEach(["r", "g", "b", "a"], function(x){ |
| t[x] = start[x] + (end[x] - start[x]) * weight; |
| if(x != "a"){ t[x] = Math.round(t[x]); } |
| }); |
| return t.sanitize(); // dojo.Color |
| }; |
| |
| dojo.colorFromRgb = function(/*String*/ color, /*dojo.Color?*/ obj){ |
| // summary: |
| // Returns a `dojo.Color` instance from a string of the form |
| // "rgb(...)" or "rgba(...)". Optionally accepts a `dojo.Color` |
| // object to update with the parsed value and return instead of |
| // creating a new object. |
| // returns: |
| // A dojo.Color object. If obj is passed, it will be the return value. |
| var m = color.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/); |
| return m && dojo.colorFromArray(m[1].split(/\s*,\s*/), obj); // dojo.Color |
| }; |
| |
| dojo.colorFromHex = function(/*String*/ color, /*dojo.Color?*/ obj){ |
| // summary: |
| // Converts a hex string with a '#' prefix to a color object. |
| // Supports 12-bit #rgb shorthand. Optionally accepts a |
| // `dojo.Color` object to update with the parsed value. |
| // |
| // returns: |
| // A dojo.Color object. If obj is passed, it will be the return value. |
| // |
| // example: |
| // | var thing = dojo.colorFromHex("#ededed"); // grey, longhand |
| // |
| // example: |
| // | var thing = dojo.colorFromHex("#000"); // black, shorthand |
| var t = obj || new d.Color(), |
| bits = (color.length == 4) ? 4 : 8, |
| mask = (1 << bits) - 1; |
| color = Number("0x" + color.substr(1)); |
| if(isNaN(color)){ |
| return null; // dojo.Color |
| } |
| d.forEach(["b", "g", "r"], function(x){ |
| var c = color & mask; |
| color >>= bits; |
| t[x] = bits == 4 ? 17 * c : c; |
| }); |
| t.a = 1; |
| return t; // dojo.Color |
| }; |
| |
| dojo.colorFromArray = function(/*Array*/ a, /*dojo.Color?*/ obj){ |
| // summary: |
| // Builds a `dojo.Color` from a 3 or 4 element array, mapping each |
| // element in sequence to the rgb(a) values of the color. |
| // example: |
| // | var myColor = dojo.colorFromArray([237,237,237,0.5]); // grey, 50% alpha |
| // returns: |
| // A dojo.Color object. If obj is passed, it will be the return value. |
| var t = obj || new d.Color(); |
| t._set(Number(a[0]), Number(a[1]), Number(a[2]), Number(a[3])); |
| if(isNaN(t.a)){ t.a = 1; } |
| return t.sanitize(); // dojo.Color |
| }; |
| |
| dojo.colorFromString = function(/*String*/ str, /*dojo.Color?*/ obj){ |
| // summary: |
| // Parses `str` for a color value. Accepts hex, rgb, and rgba |
| // style color values. |
| // description: |
| // Acceptable input values for str may include arrays of any form |
| // accepted by dojo.colorFromArray, hex strings such as "#aaaaaa", or |
| // rgb or rgba strings such as "rgb(133, 200, 16)" or "rgba(10, 10, |
| // 10, 50)" |
| // returns: |
| // A dojo.Color object. If obj is passed, it will be the return value. |
| var a = d.Color.named[str]; |
| return a && d.colorFromArray(a, obj) || d.colorFromRgb(str, obj) || d.colorFromHex(str, obj); |
| }; |
| })(); |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.window"] = true; |
| dojo.provide("dojo._base.window"); |
| |
| |
| /*===== |
| dojo.doc = { |
| // summary: |
| // Alias for the current document. 'dojo.doc' can be modified |
| // for temporary context shifting. Also see dojo.withDoc(). |
| // description: |
| // Refer to dojo.doc rather |
| // than referring to 'window.document' to ensure your code runs |
| // correctly in managed contexts. |
| // example: |
| // | n.appendChild(dojo.doc.createElement('div')); |
| } |
| =====*/ |
| dojo.doc = window["document"] || null; |
| |
| dojo.body = function(){ |
| // summary: |
| // Return the body element of the document |
| // return the body object associated with dojo.doc |
| // example: |
| // | dojo.body().appendChild(dojo.doc.createElement('div')); |
| |
| // Note: document.body is not defined for a strict xhtml document |
| // Would like to memoize this, but dojo.doc can change vi dojo.withDoc(). |
| return dojo.doc.body || dojo.doc.getElementsByTagName("body")[0]; // Node |
| }; |
| |
| dojo.setContext = function(/*Object*/globalObject, /*DocumentElement*/globalDocument){ |
| // summary: |
| // changes the behavior of many core Dojo functions that deal with |
| // namespace and DOM lookup, changing them to work in a new global |
| // context (e.g., an iframe). The varibles dojo.global and dojo.doc |
| // are modified as a result of calling this function and the result of |
| // `dojo.body()` likewise differs. |
| dojo.global = globalObject; |
| dojo.doc = globalDocument; |
| }; |
| |
| dojo.withGlobal = function( /*Object*/globalObject, |
| /*Function*/callback, |
| /*Object?*/thisObject, |
| /*Array?*/cbArguments){ |
| // summary: |
| // Invoke callback with globalObject as dojo.global and |
| // globalObject.document as dojo.doc. |
| // description: |
| // Invoke callback with globalObject as dojo.global and |
| // globalObject.document as dojo.doc. If provided, globalObject |
| // will be executed in the context of object thisObject |
| // When callback() returns or throws an error, the dojo.global |
| // and dojo.doc will be restored to its previous state. |
| |
| var oldGlob = dojo.global; |
| try{ |
| dojo.global = globalObject; |
| return dojo.withDoc.call(null, globalObject.document, callback, thisObject, cbArguments); |
| }finally{ |
| dojo.global = oldGlob; |
| } |
| }; |
| |
| dojo.withDoc = function( /*DocumentElement*/documentObject, |
| /*Function*/callback, |
| /*Object?*/thisObject, |
| /*Array?*/cbArguments){ |
| // summary: |
| // Invoke callback with documentObject as dojo.doc. |
| // description: |
| // Invoke callback with documentObject as dojo.doc. If provided, |
| // callback will be executed in the context of object thisObject |
| // When callback() returns or throws an error, the dojo.doc will |
| // be restored to its previous state. |
| |
| var oldDoc = dojo.doc, |
| oldLtr = dojo._bodyLtr, |
| oldQ = dojo.isQuirks; |
| |
| try{ |
| dojo.doc = documentObject; |
| delete dojo._bodyLtr; // uncache |
| dojo.isQuirks = dojo.doc.compatMode == "BackCompat"; // no need to check for QuirksMode which was Opera 7 only |
| |
| if(thisObject && typeof callback == "string"){ |
| callback = thisObject[callback]; |
| } |
| |
| return callback.apply(thisObject, cbArguments || []); |
| }finally{ |
| dojo.doc = oldDoc; |
| delete dojo._bodyLtr; // in case it was undefined originally, and set to true/false by the alternate document |
| if(oldLtr !== undefined){ dojo._bodyLtr = oldLtr; } |
| dojo.isQuirks = oldQ; |
| } |
| }; |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.event"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.event"] = true; |
| dojo.provide("dojo._base.event"); |
| |
| |
| |
| // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA |
| |
| (function(){ |
| // DOM event listener machinery |
| var del = (dojo._event_listener = { |
| add: function(/*DOMNode*/ node, /*String*/ name, /*Function*/ fp){ |
| if(!node){return;} |
| name = del._normalizeEventName(name); |
| fp = del._fixCallback(name, fp); |
| if( |
| !dojo.isIE && |
| (name == "mouseenter" || name == "mouseleave") |
| ){ |
| var ofp = fp; |
| name = (name == "mouseenter") ? "mouseover" : "mouseout"; |
| fp = function(e){ |
| if(!dojo.isDescendant(e.relatedTarget, node)){ |
| // e.type = oname; // FIXME: doesn't take? SJM: event.type is generally immutable. |
| return ofp.call(this, e); |
| } |
| } |
| } |
| node.addEventListener(name, fp, false); |
| return fp; /*Handle*/ |
| }, |
| remove: function(/*DOMNode*/ node, /*String*/ event, /*Handle*/ handle){ |
| // summary: |
| // clobbers the listener from the node |
| // node: |
| // DOM node to attach the event to |
| // event: |
| // the name of the handler to remove the function from |
| // handle: |
| // the handle returned from add |
| if(node){ |
| event = del._normalizeEventName(event); |
| if(!dojo.isIE && (event == "mouseenter" || event == "mouseleave")){ |
| event = (event == "mouseenter") ? "mouseover" : "mouseout"; |
| } |
| |
| node.removeEventListener(event, handle, false); |
| } |
| }, |
| _normalizeEventName: function(/*String*/ name){ |
| // Generally, name should be lower case, unless it is special |
| // somehow (e.g. a Mozilla DOM event). |
| // Remove 'on'. |
| return name.slice(0,2) =="on" ? name.slice(2) : name; |
| }, |
| _fixCallback: function(/*String*/ name, fp){ |
| // By default, we only invoke _fixEvent for 'keypress' |
| // If code is added to _fixEvent for other events, we have |
| // to revisit this optimization. |
| // This also applies to _fixEvent overrides for Safari and Opera |
| // below. |
| return name != "keypress" ? fp : function(e){ return fp.call(this, del._fixEvent(e, this)); }; |
| }, |
| _fixEvent: function(evt, sender){ |
| // _fixCallback only attaches us to keypress. |
| // Switch on evt.type anyway because we might |
| // be called directly from dojo.fixEvent. |
| switch(evt.type){ |
| case "keypress": |
| del._setKeyChar(evt); |
| break; |
| } |
| return evt; |
| }, |
| _setKeyChar: function(evt){ |
| evt.keyChar = evt.charCode >= 32 ? String.fromCharCode(evt.charCode) : ''; |
| evt.charOrCode = evt.keyChar || evt.keyCode; |
| }, |
| // For IE and Safari: some ctrl-key combinations (mostly w/punctuation) do not emit a char code in IE |
| // we map those virtual key codes to ascii here |
| // not valid for all (non-US) keyboards, so maybe we shouldn't bother |
| _punctMap: { |
| 106:42, |
| 111:47, |
| 186:59, |
| 187:43, |
| 188:44, |
| 189:45, |
| 190:46, |
| 191:47, |
| 192:96, |
| 219:91, |
| 220:92, |
| 221:93, |
| 222:39 |
| } |
| }); |
| |
| // DOM events |
| |
| dojo.fixEvent = function(/*Event*/ evt, /*DOMNode*/ sender){ |
| // summary: |
| // normalizes properties on the event object including event |
| // bubbling methods, keystroke normalization, and x/y positions |
| // evt: Event |
| // native event object |
| // sender: DOMNode |
| // node to treat as "currentTarget" |
| return del._fixEvent(evt, sender); |
| }; |
| |
| dojo.stopEvent = function(/*Event*/ evt){ |
| // summary: |
| // prevents propagation and clobbers the default action of the |
| // passed event |
| // evt: Event |
| // The event object. If omitted, window.event is used on IE. |
| evt.preventDefault(); |
| evt.stopPropagation(); |
| // NOTE: below, this method is overridden for IE |
| }; |
| |
| // the default listener to use on dontFix nodes, overriden for IE |
| var node_listener = dojo._listener; |
| |
| // Unify connect and event listeners |
| dojo._connect = function(obj, event, context, method, dontFix){ |
| // FIXME: need a more strict test |
| var isNode = obj && (obj.nodeType||obj.attachEvent||obj.addEventListener); |
| // choose one of three listener options: raw (connect.js), DOM event on a Node, custom event on a Node |
| // we need the third option to provide leak prevention on broken browsers (IE) |
| var lid = isNode ? (dontFix ? 2 : 1) : 0, l = [dojo._listener, del, node_listener][lid]; |
| // create a listener |
| var h = l.add(obj, event, dojo.hitch(context, method)); |
| // formerly, the disconnect package contained "l" directly, but if client code |
| // leaks the disconnect package (by connecting it to a node), referencing "l" |
| // compounds the problem. |
| // instead we return a listener id, which requires custom _disconnect below. |
| // return disconnect package |
| return [ obj, event, h, lid ]; |
| }; |
| |
| dojo._disconnect = function(obj, event, handle, listener){ |
| ([dojo._listener, del, node_listener][listener]).remove(obj, event, handle); |
| }; |
| |
| // Constants |
| |
| // Public: client code should test |
| // keyCode against these named constants, as the |
| // actual codes can vary by browser. |
| dojo.keys = { |
| // summary: |
| // Definitions for common key values |
| BACKSPACE: 8, |
| TAB: 9, |
| CLEAR: 12, |
| ENTER: 13, |
| SHIFT: 16, |
| CTRL: 17, |
| ALT: 18, |
| META: dojo.isSafari ? 91 : 224, // the apple key on macs |
| PAUSE: 19, |
| CAPS_LOCK: 20, |
| ESCAPE: 27, |
| SPACE: 32, |
| PAGE_UP: 33, |
| PAGE_DOWN: 34, |
| END: 35, |
| HOME: 36, |
| LEFT_ARROW: 37, |
| UP_ARROW: 38, |
| RIGHT_ARROW: 39, |
| DOWN_ARROW: 40, |
| INSERT: 45, |
| DELETE: 46, |
| HELP: 47, |
| LEFT_WINDOW: 91, |
| RIGHT_WINDOW: 92, |
| SELECT: 93, |
| NUMPAD_0: 96, |
| NUMPAD_1: 97, |
| NUMPAD_2: 98, |
| NUMPAD_3: 99, |
| NUMPAD_4: 100, |
| NUMPAD_5: 101, |
| NUMPAD_6: 102, |
| NUMPAD_7: 103, |
| NUMPAD_8: 104, |
| NUMPAD_9: 105, |
| NUMPAD_MULTIPLY: 106, |
| NUMPAD_PLUS: 107, |
| NUMPAD_ENTER: 108, |
| NUMPAD_MINUS: 109, |
| NUMPAD_PERIOD: 110, |
| NUMPAD_DIVIDE: 111, |
| F1: 112, |
| F2: 113, |
| F3: 114, |
| F4: 115, |
| F5: 116, |
| F6: 117, |
| F7: 118, |
| F8: 119, |
| F9: 120, |
| F10: 121, |
| F11: 122, |
| F12: 123, |
| F13: 124, |
| F14: 125, |
| F15: 126, |
| NUM_LOCK: 144, |
| SCROLL_LOCK: 145, |
| // virtual key mapping |
| copyKey: dojo.isMac && !dojo.isAIR ? (dojo.isSafari ? 91 : 224 ) : 17 |
| }; |
| |
| var evtCopyKey = dojo.isMac ? "metaKey" : "ctrlKey"; |
| |
| dojo.isCopyKey = function(e){ |
| // summary: |
| // Checks an event for the copy key (meta on Mac, and ctrl anywhere else) |
| // e: Event |
| // Event object to examine |
| return e[evtCopyKey]; // Boolean |
| }; |
| |
| // Public: decoding mouse buttons from events |
| |
| /*===== |
| dojo.mouseButtons = { |
| // LEFT: Number |
| // Numeric value of the left mouse button for the platform. |
| LEFT: 0, |
| // MIDDLE: Number |
| // Numeric value of the middle mouse button for the platform. |
| MIDDLE: 1, |
| // RIGHT: Number |
| // Numeric value of the right mouse button for the platform. |
| RIGHT: 2, |
| |
| isButton: function(e, button){ |
| // summary: |
| // Checks an event object for a pressed button |
| // e: Event |
| // Event object to examine |
| // button: Number |
| // The button value (example: dojo.mouseButton.LEFT) |
| return e.button == button; // Boolean |
| }, |
| isLeft: function(e){ |
| // summary: |
| // Checks an event object for the pressed left button |
| // e: Event |
| // Event object to examine |
| return e.button == 0; // Boolean |
| }, |
| isMiddle: function(e){ |
| // summary: |
| // Checks an event object for the pressed middle button |
| // e: Event |
| // Event object to examine |
| return e.button == 1; // Boolean |
| }, |
| isRight: function(e){ |
| // summary: |
| // Checks an event object for the pressed right button |
| // e: Event |
| // Event object to examine |
| return e.button == 2; // Boolean |
| } |
| }; |
| =====*/ |
| |
| if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){ |
| dojo.mouseButtons = { |
| LEFT: 1, |
| MIDDLE: 4, |
| RIGHT: 2, |
| // helper functions |
| isButton: function(e, button){ return e.button & button; }, |
| isLeft: function(e){ return e.button & 1; }, |
| isMiddle: function(e){ return e.button & 4; }, |
| isRight: function(e){ return e.button & 2; } |
| }; |
| }else{ |
| dojo.mouseButtons = { |
| LEFT: 0, |
| MIDDLE: 1, |
| RIGHT: 2, |
| // helper functions |
| isButton: function(e, button){ return e.button == button; }, |
| isLeft: function(e){ return e.button == 0; }, |
| isMiddle: function(e){ return e.button == 1; }, |
| isRight: function(e){ return e.button == 2; } |
| }; |
| } |
| |
| // IE event normalization |
| if(dojo.isIE){ |
| var _trySetKeyCode = function(e, code){ |
| try{ |
| // squelch errors when keyCode is read-only |
| // (e.g. if keyCode is ctrl or shift) |
| return (e.keyCode = code); |
| }catch(e){ |
| return 0; |
| } |
| }; |
| |
| // by default, use the standard listener |
| var iel = dojo._listener; |
| var listenersName = (dojo._ieListenersName = "_" + dojo._scopeName + "_listeners"); |
| // dispatcher tracking property |
| if(!dojo.config._allow_leaks){ |
| // custom listener that handles leak protection for DOM events |
| node_listener = iel = dojo._ie_listener = { |
| // support handler indirection: event handler functions are |
| // referenced here. Event dispatchers hold only indices. |
| handlers: [], |
| // add a listener to an object |
| add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){ |
| source = source || dojo.global; |
| var f = source[method]; |
| if(!f||!f[listenersName]){ |
| var d = dojo._getIeDispatcher(); |
| // original target function is special |
| d.target = f && (ieh.push(f) - 1); |
| // dispatcher holds a list of indices into handlers table |
| d[listenersName] = []; |
| // redirect source to dispatcher |
| f = source[method] = d; |
| } |
| return f[listenersName].push(ieh.push(listener) - 1) ; /*Handle*/ |
| }, |
| // remove a listener from an object |
| remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){ |
| var f = (source||dojo.global)[method], l = f && f[listenersName]; |
| if(f && l && handle--){ |
| delete ieh[l[handle]]; |
| delete l[handle]; |
| } |
| } |
| }; |
| // alias used above |
| var ieh = iel.handlers; |
| } |
| |
| dojo.mixin(del, { |
| add: function(/*DOMNode*/ node, /*String*/ event, /*Function*/ fp){ |
| if(!node){return;} // undefined |
| event = del._normalizeEventName(event); |
| if(event=="onkeypress"){ |
| // we need to listen to onkeydown to synthesize |
| // keypress events that otherwise won't fire |
| // on IE |
| var kd = node.onkeydown; |
| if(!kd || !kd[listenersName] || !kd._stealthKeydownHandle){ |
| var h = del.add(node, "onkeydown", del._stealthKeyDown); |
| kd = node.onkeydown; |
| kd._stealthKeydownHandle = h; |
| kd._stealthKeydownRefs = 1; |
| }else{ |
| kd._stealthKeydownRefs++; |
| } |
| } |
| return iel.add(node, event, del._fixCallback(fp)); |
| }, |
| remove: function(/*DOMNode*/ node, /*String*/ event, /*Handle*/ handle){ |
| event = del._normalizeEventName(event); |
| iel.remove(node, event, handle); |
| if(event=="onkeypress"){ |
| var kd = node.onkeydown; |
| if(--kd._stealthKeydownRefs <= 0){ |
| iel.remove(node, "onkeydown", kd._stealthKeydownHandle); |
| delete kd._stealthKeydownHandle; |
| } |
| } |
| }, |
| _normalizeEventName: function(/*String*/ eventName){ |
| // Generally, eventName should be lower case, unless it is |
| // special somehow (e.g. a Mozilla event) |
| // ensure 'on' |
| return eventName.slice(0,2) != "on" ? "on" + eventName : eventName; |
| }, |
| _nop: function(){}, |
| _fixEvent: function(/*Event*/ evt, /*DOMNode*/ sender){ |
| // summary: |
| // normalizes properties on the event object including event |
| // bubbling methods, keystroke normalization, and x/y positions |
| // evt: |
| // native event object |
| // sender: |
| // node to treat as "currentTarget" |
| if(!evt){ |
| var w = sender && (sender.ownerDocument || sender.document || sender).parentWindow || window; |
| evt = w.event; |
| } |
| if(!evt){return(evt);} |
| evt.target = evt.srcElement; |
| evt.currentTarget = (sender || evt.srcElement); |
| evt.layerX = evt.offsetX; |
| evt.layerY = evt.offsetY; |
| // FIXME: scroll position query is duped from dojo.html to |
| // avoid dependency on that entire module. Now that HTML is in |
| // Base, we should convert back to something similar there. |
| var se = evt.srcElement, doc = (se && se.ownerDocument) || document; |
| // DO NOT replace the following to use dojo.body(), in IE, document.documentElement should be used |
| // here rather than document.body |
| var docBody = ((dojo.isIE < 6) || (doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement; |
| var offset = dojo._getIeDocumentElementOffset(); |
| evt.pageX = evt.clientX + dojo._fixIeBiDiScrollLeft(docBody.scrollLeft || 0) - offset.x; |
| evt.pageY = evt.clientY + (docBody.scrollTop || 0) - offset.y; |
| if(evt.type == "mouseover"){ |
| evt.relatedTarget = evt.fromElement; |
| } |
| if(evt.type == "mouseout"){ |
| evt.relatedTarget = evt.toElement; |
| } |
| if (dojo.isIE < 9 || dojo.isQuirks) { |
| evt.stopPropagation = del._stopPropagation; |
| evt.preventDefault = del._preventDefault; |
| } |
| return del._fixKeys(evt); |
| }, |
| _fixKeys: function(evt){ |
| switch(evt.type){ |
| case "keypress": |
| var c = ("charCode" in evt ? evt.charCode : evt.keyCode); |
| if (c==10){ |
| // CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla |
| c=0; |
| evt.keyCode = 13; |
| }else if(c==13||c==27){ |
| c=0; // Mozilla considers ENTER and ESC non-printable |
| }else if(c==3){ |
| c=99; // Mozilla maps CTRL-BREAK to CTRL-c |
| } |
| // Mozilla sets keyCode to 0 when there is a charCode |
| // but that stops the event on IE. |
| evt.charCode = c; |
| del._setKeyChar(evt); |
| break; |
| } |
| return evt; |
| }, |
| _stealthKeyDown: function(evt){ |
| // IE doesn't fire keypress for most non-printable characters. |
| // other browsers do, we simulate it here. |
| var kp = evt.currentTarget.onkeypress; |
| // only works if kp exists and is a dispatcher |
| if(!kp || !kp[listenersName]){ return; } |
| // munge key/charCode |
| var k=evt.keyCode; |
| // These are Windows Virtual Key Codes |
| // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp |
| var unprintable = (k!=13 || (dojo.isIE >= 9 && !dojo.isQuirks)) && k!=32 && k!=27 && (k<48||k>90) && (k<96||k>111) && (k<186||k>192) && (k<219||k>222); |
| |
| // synthesize keypress for most unprintables and CTRL-keys |
| if(unprintable||evt.ctrlKey){ |
| var c = unprintable ? 0 : k; |
| if(evt.ctrlKey){ |
| if(k==3 || k==13){ |
| return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively |
| }else if(c>95 && c<106){ |
| c -= 48; // map CTRL-[numpad 0-9] to ASCII |
| }else if((!evt.shiftKey)&&(c>=65&&c<=90)){ |
| c += 32; // map CTRL-[A-Z] to lowercase |
| }else{ |
| c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII |
| } |
| } |
| // simulate a keypress event |
| var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c}); |
| kp.call(evt.currentTarget, faux); |
| if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){ |
| evt.cancelBubble = faux.cancelBubble; |
| } |
| evt.returnValue = faux.returnValue; |
| _trySetKeyCode(evt, faux.keyCode); |
| } |
| }, |
| // Called in Event scope |
| _stopPropagation: function(){ |
| this.cancelBubble = true; |
| }, |
| _preventDefault: function(){ |
| // Setting keyCode to 0 is the only way to prevent certain keypresses (namely |
| // ctrl-combinations that correspond to menu accelerator keys). |
| // Otoh, it prevents upstream listeners from getting this information |
| // Try to split the difference here by clobbering keyCode only for ctrl |
| // combinations. If you still need to access the key upstream, bubbledKeyCode is |
| // provided as a workaround. |
| this.bubbledKeyCode = this.keyCode; |
| if(this.ctrlKey){_trySetKeyCode(this, 0);} |
| this.returnValue = false; |
| } |
| }); |
| |
| // override stopEvent for IE |
| dojo.stopEvent = (dojo.isIE < 9 || dojo.isQuirks) ? function(evt){ |
| evt = evt || window.event; |
| del._stopPropagation.call(evt); |
| del._preventDefault.call(evt); |
| } : dojo.stopEvent; |
| } |
| |
| del._synthesizeEvent = function(evt, props){ |
| var faux = dojo.mixin({}, evt, props); |
| del._setKeyChar(faux); |
| // FIXME: would prefer to use dojo.hitch: dojo.hitch(evt, evt.preventDefault); |
| // but it throws an error when preventDefault is invoked on Safari |
| // does Event.preventDefault not support "apply" on Safari? |
| faux.preventDefault = function(){ evt.preventDefault(); }; |
| faux.stopPropagation = function(){ evt.stopPropagation(); }; |
| return faux; |
| }; |
| |
| // Opera event normalization |
| if(dojo.isOpera){ |
| dojo.mixin(del, { |
| _fixEvent: function(evt, sender){ |
| switch(evt.type){ |
| case "keypress": |
| var c = evt.which; |
| if(c==3){ |
| c=99; // Mozilla maps CTRL-BREAK to CTRL-c |
| } |
| // can't trap some keys at all, like INSERT and DELETE |
| // there is no differentiating info between DELETE and ".", or INSERT and "-" |
| c = c<41 && !evt.shiftKey ? 0 : c; |
| if(evt.ctrlKey && !evt.shiftKey && c>=65 && c<=90){ |
| // lowercase CTRL-[A-Z] keys |
| c += 32; |
| } |
| return del._synthesizeEvent(evt, { charCode: c }); |
| } |
| return evt; |
| } |
| }); |
| } |
| |
| // Webkit event normalization |
| if(dojo.isWebKit){ |
| del._add = del.add; |
| del._remove = del.remove; |
| |
| dojo.mixin(del, { |
| add: function(/*DOMNode*/ node, /*String*/ event, /*Function*/ fp){ |
| if(!node){return;} // undefined |
| var handle = del._add(node, event, fp); |
| if(del._normalizeEventName(event) == "keypress"){ |
| // we need to listen to onkeydown to synthesize |
| // keypress events that otherwise won't fire |
| // in Safari 3.1+: https://lists.webkit.org/pipermail/webkit-dev/2007-December/002992.html |
| handle._stealthKeyDownHandle = del._add(node, "keydown", function(evt){ |
| //A variation on the IE _stealthKeydown function |
| //Synthesize an onkeypress event, but only for unprintable characters. |
| var k=evt.keyCode; |
| // These are Windows Virtual Key Codes |
| // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp |
| var unprintable = k!=13 && k!=32 && (k<48 || k>90) && (k<96 || k>111) && (k<186 || k>192) && (k<219 || k>222); |
| // synthesize keypress for most unprintables and CTRL-keys |
| if(unprintable || evt.ctrlKey){ |
| var c = unprintable ? 0 : k; |
| if(evt.ctrlKey){ |
| if(k==3 || k==13){ |
| return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively |
| }else if(c>95 && c<106){ |
| c -= 48; // map CTRL-[numpad 0-9] to ASCII |
| }else if(!evt.shiftKey && c>=65 && c<=90){ |
| c += 32; // map CTRL-[A-Z] to lowercase |
| }else{ |
| c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII |
| } |
| } |
| // simulate a keypress event |
| var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c}); |
| fp.call(evt.currentTarget, faux); |
| } |
| }); |
| } |
| return handle; /*Handle*/ |
| }, |
| |
| remove: function(/*DOMNode*/ node, /*String*/ event, /*Handle*/ handle){ |
| if(node){ |
| if(handle._stealthKeyDownHandle){ |
| del._remove(node, "keydown", handle._stealthKeyDownHandle); |
| } |
| del._remove(node, event, handle); |
| } |
| }, |
| _fixEvent: function(evt, sender){ |
| switch(evt.type){ |
| case "keypress": |
| if(evt.faux){ return evt; } |
| var c = evt.charCode; |
| c = c>=32 ? c : 0; |
| return del._synthesizeEvent(evt, {charCode: c, faux: true}); |
| } |
| return evt; |
| } |
| }); |
| } |
| })(); |
| |
| if(dojo.isIE){ |
| // keep this out of the closure |
| // closing over 'iel' or 'ieh' b0rks leak prevention |
| // ls[i] is an index into the master handler array |
| dojo._ieDispatcher = function(args, sender){ |
| var ap = Array.prototype, |
| h = dojo._ie_listener.handlers, |
| c = args.callee, |
| ls = c[dojo._ieListenersName], |
| t = h[c.target]; |
| // return value comes from original target function |
| var r = t && t.apply(sender, args); |
| // make local copy of listener array so it's immutable during processing |
| var lls = [].concat(ls); |
| // invoke listeners after target function |
| for(var i in lls){ |
| var f = h[lls[i]]; |
| if(!(i in ap) && f){ |
| f.apply(sender, args); |
| } |
| } |
| return r; |
| }; |
| dojo._getIeDispatcher = function(){ |
| // ensure the returned function closes over nothing ("new Function" apparently doesn't close) |
| return new Function(dojo._scopeName + "._ieDispatcher(arguments, this)"); // function |
| }; |
| // keep this out of the closure to reduce RAM allocation |
| dojo._event_listener._fixCallback = function(fp){ |
| var f = dojo._event_listener._fixEvent; |
| return function(e){ return fp.call(this, f(e, this)); }; |
| }; |
| } |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.html"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.html"] = true; |
| dojo.provide("dojo._base.html"); |
| |
| |
| |
| // FIXME: need to add unit tests for all the semi-public methods |
| |
| try{ |
| document.execCommand("BackgroundImageCache", false, true); |
| }catch(e){ |
| // sane browsers don't have cache "issues" |
| } |
| |
| // ============================= |
| // DOM Functions |
| // ============================= |
| |
| /*===== |
| dojo.byId = function(id, doc){ |
| // summary: |
| // Returns DOM node with matching `id` attribute or `null` |
| // if not found. If `id` is a DomNode, this function is a no-op. |
| // |
| // id: String|DOMNode |
| // A string to match an HTML id attribute or a reference to a DOM Node |
| // |
| // doc: Document? |
| // Document to work in. Defaults to the current value of |
| // dojo.doc. Can be used to retrieve |
| // node references from other documents. |
| // |
| // example: |
| // Look up a node by ID: |
| // | var n = dojo.byId("foo"); |
| // |
| // example: |
| // Check if a node exists, and use it. |
| // | var n = dojo.byId("bar"); |
| // | if(n){ doStuff() ... } |
| // |
| // example: |
| // Allow string or DomNode references to be passed to a custom function: |
| // | var foo = function(nodeOrId){ |
| // | nodeOrId = dojo.byId(nodeOrId); |
| // | // ... more stuff |
| // | } |
| =====*/ |
| |
| if(dojo.isIE){ |
| dojo.byId = function(id, doc){ |
| if(typeof id != "string"){ |
| return id; |
| } |
| var _d = doc || dojo.doc, te = _d.getElementById(id); |
| // attributes.id.value is better than just id in case the |
| // user has a name=id inside a form |
| if(te && (te.attributes.id.value == id || te.id == id)){ |
| return te; |
| }else{ |
| var eles = _d.all[id]; |
| if(!eles || eles.nodeName){ |
| eles = [eles]; |
| } |
| // if more than 1, choose first with the correct id |
| var i=0; |
| while((te=eles[i++])){ |
| if((te.attributes && te.attributes.id && te.attributes.id.value == id) |
| || te.id == id){ |
| return te; |
| } |
| } |
| } |
| }; |
| }else{ |
| dojo.byId = function(id, doc){ |
| // inline'd type check. |
| // be sure to return null per documentation, to match IE branch. |
| return ((typeof id == "string") ? (doc || dojo.doc).getElementById(id) : id) || null; // DomNode |
| }; |
| } |
| /*===== |
| }; |
| =====*/ |
| |
| (function(){ |
| var d = dojo; |
| var byId = d.byId; |
| |
| var _destroyContainer = null, |
| _destroyDoc; |
| d.addOnWindowUnload(function(){ |
| _destroyContainer = null; //prevent IE leak |
| }); |
| |
| /*===== |
| dojo._destroyElement = function(node){ |
| // summary: |
| // Existing alias for `dojo.destroy`. Deprecated, will be removed |
| // in 2.0 |
| } |
| =====*/ |
| dojo._destroyElement = dojo.destroy = function(/*String|DomNode*/node){ |
| // summary: |
| // Removes a node from its parent, clobbering it and all of its |
| // children. |
| // |
| // description: |
| // Removes a node from its parent, clobbering it and all of its |
| // children. Function only works with DomNodes, and returns nothing. |
| // |
| // node: |
| // A String ID or DomNode reference of the element to be destroyed |
| // |
| // example: |
| // Destroy a node byId: |
| // | dojo.destroy("someId"); |
| // |
| // example: |
| // Destroy all nodes in a list by reference: |
| // | dojo.query(".someNode").forEach(dojo.destroy); |
| |
| node = byId(node); |
| try{ |
| var doc = node.ownerDocument; |
| // cannot use _destroyContainer.ownerDocument since this can throw an exception on IE |
| if(!_destroyContainer || _destroyDoc != doc){ |
| _destroyContainer = doc.createElement("div"); |
| _destroyDoc = doc; |
| } |
| _destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) : node); |
| // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature |
| _destroyContainer.innerHTML = ""; |
| }catch(e){ |
| /* squelch */ |
| } |
| }; |
| |
| dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor){ |
| // summary: |
| // Returns true if node is a descendant of ancestor |
| // node: string id or node reference to test |
| // ancestor: string id or node reference of potential parent to test against |
| // |
| // example: |
| // Test is node id="bar" is a descendant of node id="foo" |
| // | if(dojo.isDescendant("bar", "foo")){ ... } |
| try{ |
| node = byId(node); |
| ancestor = byId(ancestor); |
| while(node){ |
| if(node == ancestor){ |
| return true; // Boolean |
| } |
| node = node.parentNode; |
| } |
| }catch(e){ /* squelch, return false */ } |
| return false; // Boolean |
| }; |
| |
| dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable){ |
| // summary: |
| // Enable or disable selection on a node |
| // node: |
| // id or reference to node |
| // selectable: |
| // state to put the node in. false indicates unselectable, true |
| // allows selection. |
| // example: |
| // Make the node id="bar" unselectable |
| // | dojo.setSelectable("bar"); |
| // example: |
| // Make the node id="bar" selectable |
| // | dojo.setSelectable("bar", true); |
| node = byId(node); |
| if(d.isMozilla){ |
| node.style.MozUserSelect = selectable ? "" : "none"; |
| }else if(d.isKhtml || d.isWebKit){ |
| node.style.KhtmlUserSelect = selectable ? "auto" : "none"; |
| }else if(d.isIE){ |
| var v = (node.unselectable = selectable ? "" : "on"); |
| d.query("*", node).forEach("item.unselectable = '"+v+"'"); |
| } |
| //FIXME: else? Opera? |
| }; |
| |
| var _insertBefore = function(/*DomNode*/node, /*DomNode*/ref){ |
| var parent = ref.parentNode; |
| if(parent){ |
| parent.insertBefore(node, ref); |
| } |
| }; |
| |
| var _insertAfter = function(/*DomNode*/node, /*DomNode*/ref){ |
| // summary: |
| // Try to insert node after ref |
| var parent = ref.parentNode; |
| if(parent){ |
| if(parent.lastChild == ref){ |
| parent.appendChild(node); |
| }else{ |
| parent.insertBefore(node, ref.nextSibling); |
| } |
| } |
| }; |
| |
| dojo.place = function(node, refNode, position){ |
| // summary: |
| // Attempt to insert node into the DOM, choosing from various positioning options. |
| // Returns the first argument resolved to a DOM node. |
| // |
| // node: String|DomNode |
| // id or node reference, or HTML fragment starting with "<" to place relative to refNode |
| // |
| // refNode: String|DomNode |
| // id or node reference to use as basis for placement |
| // |
| // position: String|Number? |
| // string noting the position of node relative to refNode or a |
| // number indicating the location in the childNodes collection of refNode. |
| // Accepted string values are: |
| // | * before |
| // | * after |
| // | * replace |
| // | * only |
| // | * first |
| // | * last |
| // "first" and "last" indicate positions as children of refNode, "replace" replaces refNode, |
| // "only" replaces all children. position defaults to "last" if not specified |
| // |
| // returns: DomNode |
| // Returned values is the first argument resolved to a DOM node. |
| // |
| // .place() is also a method of `dojo.NodeList`, allowing `dojo.query` node lookups. |
| // |
| // example: |
| // Place a node by string id as the last child of another node by string id: |
| // | dojo.place("someNode", "anotherNode"); |
| // |
| // example: |
| // Place a node by string id before another node by string id |
| // | dojo.place("someNode", "anotherNode", "before"); |
| // |
| // example: |
| // Create a Node, and place it in the body element (last child): |
| // | dojo.place("<div></div>", dojo.body()); |
| // |
| // example: |
| // Put a new LI as the first child of a list by id: |
| // | dojo.place("<li></li>", "someUl", "first"); |
| |
| refNode = byId(refNode); |
| if(typeof node == "string"){ // inline'd type check |
| node = /^\s*</.test(node) ? d._toDom(node, refNode.ownerDocument) : byId(node); |
| } |
| if(typeof position == "number"){ // inline'd type check |
| var cn = refNode.childNodes; |
| if(!cn.length || cn.length <= position){ |
| refNode.appendChild(node); |
| }else{ |
| _insertBefore(node, cn[position < 0 ? 0 : position]); |
| } |
| }else{ |
| switch(position){ |
| case "before": |
| _insertBefore(node, refNode); |
| break; |
| case "after": |
| _insertAfter(node, refNode); |
| break; |
| case "replace": |
| refNode.parentNode.replaceChild(node, refNode); |
| break; |
| case "only": |
| d.empty(refNode); |
| refNode.appendChild(node); |
| break; |
| case "first": |
| if(refNode.firstChild){ |
| _insertBefore(node, refNode.firstChild); |
| break; |
| } |
| // else fallthrough... |
| default: // aka: last |
| refNode.appendChild(node); |
| } |
| } |
| return node; // DomNode |
| }; |
| |
| // Box functions will assume this model. |
| // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode. |
| // Can be set to change behavior of box setters. |
| |
| // can be either: |
| // "border-box" |
| // "content-box" (default) |
| dojo.boxModel = "content-box"; |
| |
| // We punt per-node box mode testing completely. |
| // If anybody cares, we can provide an additional (optional) unit |
| // that overrides existing code to include per-node box sensitivity. |
| |
| // Opera documentation claims that Opera 9 uses border-box in BackCompat mode. |
| // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box. |
| // IIRC, earlier versions of Opera did in fact use border-box. |
| // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault. |
| |
| if(d.isIE /*|| dojo.isOpera*/){ |
| // client code may have to adjust if compatMode varies across iframes |
| d.boxModel = document.compatMode == "BackCompat" ? "border-box" : "content-box"; |
| } |
| |
| // ============================= |
| // Style Functions |
| // ============================= |
| |
| // getComputedStyle drives most of the style code. |
| // Wherever possible, reuse the returned object. |
| // |
| // API functions below that need to access computed styles accept an |
| // optional computedStyle parameter. |
| // If this parameter is omitted, the functions will call getComputedStyle themselves. |
| // This way, calling code can access computedStyle once, and then pass the reference to |
| // multiple API functions. |
| |
| /*===== |
| dojo.getComputedStyle = function(node){ |
| // summary: |
| // Returns a "computed style" object. |
| // |
| // description: |
| // Gets a "computed style" object which can be used to gather |
| // information about the current state of the rendered node. |
| // |
| // Note that this may behave differently on different browsers. |
| // Values may have different formats and value encodings across |
| // browsers. |
| // |
| // Note also that this method is expensive. Wherever possible, |
| // reuse the returned object. |
| // |
| // Use the dojo.style() method for more consistent (pixelized) |
| // return values. |
| // |
| // node: DOMNode |
| // A reference to a DOM node. Does NOT support taking an |
| // ID string for speed reasons. |
| // example: |
| // | dojo.getComputedStyle(dojo.byId('foo')).borderWidth; |
| // |
| // example: |
| // Reusing the returned object, avoiding multiple lookups: |
| // | var cs = dojo.getComputedStyle(dojo.byId("someNode")); |
| // | var w = cs.width, h = cs.height; |
| return; // CSS2Properties |
| } |
| =====*/ |
| |
| // Although we normally eschew argument validation at this |
| // level, here we test argument 'node' for (duck)type, |
| // by testing nodeType, ecause 'document' is the 'parentNode' of 'body' |
| // it is frequently sent to this function even |
| // though it is not Element. |
| var gcs; |
| if(d.isWebKit){ |
| gcs = function(/*DomNode*/node){ |
| var s; |
| if(node.nodeType == 1){ |
| var dv = node.ownerDocument.defaultView; |
| s = dv.getComputedStyle(node, null); |
| if(!s && node.style){ |
| node.style.display = ""; |
| s = dv.getComputedStyle(node, null); |
| } |
| } |
| return s || {}; |
| }; |
| }else if(d.isIE){ |
| gcs = function(node){ |
| // IE (as of 7) doesn't expose Element like sane browsers |
| return node.nodeType == 1 /* ELEMENT_NODE*/ ? node.currentStyle : {}; |
| }; |
| }else{ |
| gcs = function(node){ |
| return node.nodeType == 1 ? |
| node.ownerDocument.defaultView.getComputedStyle(node, null) : {}; |
| }; |
| } |
| dojo.getComputedStyle = gcs; |
| |
| if(!d.isIE){ |
| d._toPixelValue = function(element, value){ |
| // style values can be floats, client code may want |
| // to round for integer pixels. |
| return parseFloat(value) || 0; |
| }; |
| }else{ |
| d._toPixelValue = function(element, avalue){ |
| if(!avalue){ return 0; } |
| // on IE7, medium is usually 4 pixels |
| if(avalue == "medium"){ return 4; } |
| // style values can be floats, client code may |
| // want to round this value for integer pixels. |
| if(avalue.slice && avalue.slice(-2) == 'px'){ return parseFloat(avalue); } |
| with(element){ |
| var sLeft = style.left; |
| var rsLeft = runtimeStyle.left; |
| runtimeStyle.left = currentStyle.left; |
| try{ |
| // 'avalue' may be incompatible with style.left, which can cause IE to throw |
| // this has been observed for border widths using "thin", "medium", "thick" constants |
| // those particular constants could be trapped by a lookup |
| // but perhaps there are more |
| style.left = avalue; |
| avalue = style.pixelLeft; |
| }catch(e){ |
| avalue = 0; |
| } |
| style.left = sLeft; |
| runtimeStyle.left = rsLeft; |
| } |
| return avalue; |
| }; |
| } |
| var px = d._toPixelValue; |
| |
| // FIXME: there opacity quirks on FF that we haven't ported over. Hrm. |
| /*===== |
| dojo._getOpacity = function(node){ |
| // summary: |
| // Returns the current opacity of the passed node as a |
| // floating-point value between 0 and 1. |
| // node: DomNode |
| // a reference to a DOM node. Does NOT support taking an |
| // ID string for speed reasons. |
| // returns: Number between 0 and 1 |
| return; // Number |
| } |
| =====*/ |
| |
| var astr = "DXImageTransform.Microsoft.Alpha"; |
| var af = function(n, f){ |
| try{ |
| return n.filters.item(astr); |
| }catch(e){ |
| return f ? {} : null; |
| } |
| }; |
| |
| dojo._getOpacity = |
| d.isIE < 9 ? function(node){ |
| try{ |
| return af(node).Opacity / 100; // Number |
| }catch(e){ |
| return 1; // Number |
| } |
| } : |
| function(node){ |
| return gcs(node).opacity; |
| }; |
| |
| /*===== |
| dojo._setOpacity = function(node, opacity){ |
| // summary: |
| // set the opacity of the passed node portably. Returns the |
| // new opacity of the node. |
| // node: DOMNode |
| // a reference to a DOM node. Does NOT support taking an |
| // ID string for performance reasons. |
| // opacity: Number |
| // A Number between 0 and 1. 0 specifies transparent. |
| // returns: Number between 0 and 1 |
| return; // Number |
| } |
| =====*/ |
| |
| dojo._setOpacity = |
| d.isIE < 9 ? function(/*DomNode*/node, /*Number*/opacity){ |
| var ov = opacity * 100, opaque = opacity == 1; |
| node.style.zoom = opaque ? "" : 1; |
| |
| if(!af(node)){ |
| if(opaque){ |
| return opacity; |
| } |
| node.style.filter += " progid:" + astr + "(Opacity=" + ov + ")"; |
| }else{ |
| af(node, 1).Opacity = ov; |
| } |
| |
| // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so disable it altogether (bug #2661), |
| //but still update the opacity value so we can get a correct reading if it is read later. |
| af(node, 1).Enabled = !opaque; |
| |
| if(node.nodeName.toLowerCase() == "tr"){ |
| d.query("> td", node).forEach(function(i){ |
| d._setOpacity(i, opacity); |
| }); |
| } |
| return opacity; |
| } : |
| function(node, opacity){ |
| return node.style.opacity = opacity; |
| }; |
| |
| var _pixelNamesCache = { |
| left: true, top: true |
| }; |
| var _pixelRegExp = /margin|padding|width|height|max|min|offset/; // |border |
| var _toStyleValue = function(node, type, value){ |
| type = type.toLowerCase(); // FIXME: should we really be doing string case conversion here? Should we cache it? Need to profile! |
| if(d.isIE){ |
| if(value == "auto"){ |
| if(type == "height"){ return node.offsetHeight; } |
| if(type == "width"){ return node.offsetWidth; } |
| } |
| if(type == "fontweight"){ |
| switch(value){ |
| case 700: return "bold"; |
| case 400: |
| default: return "normal"; |
| } |
| } |
| } |
| if(!(type in _pixelNamesCache)){ |
| _pixelNamesCache[type] = _pixelRegExp.test(type); |
| } |
| return _pixelNamesCache[type] ? px(node, value) : value; |
| }; |
| |
| var _floatStyle = d.isIE ? "styleFloat" : "cssFloat", |
| _floatAliases = { "cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle } |
| ; |
| |
| // public API |
| |
| dojo.style = function( /*DomNode|String*/ node, |
| /*String?|Object?*/ style, |
| /*String?*/ value){ |
| // summary: |
| // Accesses styles on a node. If 2 arguments are |
| // passed, acts as a getter. If 3 arguments are passed, acts |
| // as a setter. |
| // description: |
| // Getting the style value uses the computed style for the node, so the value |
| // will be a calculated value, not just the immediate node.style value. |
| // Also when getting values, use specific style names, |
| // like "borderBottomWidth" instead of "border" since compound values like |
| // "border" are not necessarily reflected as expected. |
| // If you want to get node dimensions, use `dojo.marginBox()`, |
| // `dojo.contentBox()` or `dojo.position()`. |
| // node: |
| // id or reference to node to get/set style for |
| // style: |
| // the style property to set in DOM-accessor format |
| // ("borderWidth", not "border-width") or an object with key/value |
| // pairs suitable for setting each property. |
| // value: |
| // If passed, sets value on the node for style, handling |
| // cross-browser concerns. When setting a pixel value, |
| // be sure to include "px" in the value. For instance, top: "200px". |
| // Otherwise, in some cases, some browsers will not apply the style. |
| // example: |
| // Passing only an ID or node returns the computed style object of |
| // the node: |
| // | dojo.style("thinger"); |
| // example: |
| // Passing a node and a style property returns the current |
| // normalized, computed value for that property: |
| // | dojo.style("thinger", "opacity"); // 1 by default |
| // |
| // example: |
| // Passing a node, a style property, and a value changes the |
| // current display of the node and returns the new computed value |
| // | dojo.style("thinger", "opacity", 0.5); // == 0.5 |
| // |
| // example: |
| // Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node: |
| // | dojo.style("thinger", { |
| // | "opacity": 0.5, |
| // | "border": "3px solid black", |
| // | "height": "300px" |
| // | }); |
| // |
| // example: |
| // When the CSS style property is hyphenated, the JavaScript property is camelCased. |
| // font-size becomes fontSize, and so on. |
| // | dojo.style("thinger",{ |
| // | fontSize:"14pt", |
| // | letterSpacing:"1.2em" |
| // | }); |
| // |
| // example: |
| // dojo.NodeList implements .style() using the same syntax, omitting the "node" parameter, calling |
| // dojo.style() on every element of the list. See: `dojo.query()` and `dojo.NodeList()` |
| // | dojo.query(".someClassName").style("visibility","hidden"); |
| // | // or |
| // | dojo.query("#baz > div").style({ |
| // | opacity:0.75, |
| // | fontSize:"13pt" |
| // | }); |
| |
| var n = byId(node), args = arguments.length, op = (style == "opacity"); |
| style = _floatAliases[style] || style; |
| if(args == 3){ |
| return op ? d._setOpacity(n, value) : n.style[style] = value; /*Number*/ |
| } |
| if(args == 2 && op){ |
| return d._getOpacity(n); |
| } |
| var s = gcs(n); |
| if(args == 2 && typeof style != "string"){ // inline'd type check |
| for(var x in style){ |
| d.style(node, x, style[x]); |
| } |
| return s; |
| } |
| return (args == 1) ? s : _toStyleValue(n, style, s[style] || n.style[style]); /* CSS2Properties||String||Number */ |
| }; |
| |
| // ============================= |
| // Box Functions |
| // ============================= |
| |
| dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle){ |
| // summary: |
| // Returns object with special values specifically useful for node |
| // fitting. |
| // description: |
| // Returns an object with `w`, `h`, `l`, `t` properties: |
| // | l/t = left/top padding (respectively) |
| // | w = the total of the left and right padding |
| // | h = the total of the top and bottom padding |
| // If 'node' has position, l/t forms the origin for child nodes. |
| // The w/h are used for calculating boxes. |
| // Normally application code will not need to invoke this |
| // directly, and will use the ...box... functions instead. |
| var |
| s = computedStyle||gcs(n), |
| l = px(n, s.paddingLeft), |
| t = px(n, s.paddingTop); |
| return { |
| l: l, |
| t: t, |
| w: l+px(n, s.paddingRight), |
| h: t+px(n, s.paddingBottom) |
| }; |
| }; |
| |
| dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){ |
| // summary: |
| // returns an object with properties useful for noting the border |
| // dimensions. |
| // description: |
| // * l/t = the sum of left/top border (respectively) |
| // * w = the sum of the left and right border |
| // * h = the sum of the top and bottom border |
| // |
| // The w/h are used for calculating boxes. |
| // Normally application code will not need to invoke this |
| // directly, and will use the ...box... functions instead. |
| var |
| ne = "none", |
| s = computedStyle||gcs(n), |
| bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0), |
| bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0); |
| return { |
| l: bl, |
| t: bt, |
| w: bl + (s.borderRightStyle!=ne ? px(n, s.borderRightWidth) : 0), |
| h: bt + (s.borderBottomStyle!=ne ? px(n, s.borderBottomWidth) : 0) |
| }; |
| }; |
| |
| dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle){ |
| // summary: |
| // Returns object with properties useful for box fitting with |
| // regards to padding. |
| // description: |
| // * l/t = the sum of left/top padding and left/top border (respectively) |
| // * w = the sum of the left and right padding and border |
| // * h = the sum of the top and bottom padding and border |
| // |
| // The w/h are used for calculating boxes. |
| // Normally application code will not need to invoke this |
| // directly, and will use the ...box... functions instead. |
| var |
| s = computedStyle||gcs(n), |
| p = d._getPadExtents(n, s), |
| b = d._getBorderExtents(n, s); |
| return { |
| l: p.l + b.l, |
| t: p.t + b.t, |
| w: p.w + b.w, |
| h: p.h + b.h |
| }; |
| }; |
| |
| dojo._getMarginExtents = function(n, computedStyle){ |
| // summary: |
| // returns object with properties useful for box fitting with |
| // regards to box margins (i.e., the outer-box). |
| // |
| // * l/t = marginLeft, marginTop, respectively |
| // * w = total width, margin inclusive |
| // * h = total height, margin inclusive |
| // |
| // The w/h are used for calculating boxes. |
| // Normally application code will not need to invoke this |
| // directly, and will use the ...box... functions instead. |
| var |
| s = computedStyle||gcs(n), |
| l = px(n, s.marginLeft), |
| t = px(n, s.marginTop), |
| r = px(n, s.marginRight), |
| b = px(n, s.marginBottom); |
| if(d.isWebKit && (s.position != "absolute")){ |
| // FIXME: Safari's version of the computed right margin |
| // is the space between our right edge and the right edge |
| // of our offsetParent. |
| // What we are looking for is the actual margin value as |
| // determined by CSS. |
| // Hack solution is to assume left/right margins are the same. |
| r = l; |
| } |
| return { |
| l: l, |
| t: t, |
| w: l+r, |
| h: t+b |
| }; |
| }; |
| |
| // Box getters work in any box context because offsetWidth/clientWidth |
| // are invariant wrt box context |
| // |
| // They do *not* work for display: inline objects that have padding styles |
| // because the user agent ignores padding (it's bogus styling in any case) |
| // |
| // Be careful with IMGs because they are inline or block depending on |
| // browser and browser mode. |
| |
| // Although it would be easier to read, there are not separate versions of |
| // _getMarginBox for each browser because: |
| // 1. the branching is not expensive |
| // 2. factoring the shared code wastes cycles (function call overhead) |
| // 3. duplicating the shared code wastes bytes |
| |
| dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle){ |
| // summary: |
| // returns an object that encodes the width, height, left and top |
| // positions of the node's margin box. |
| var s = computedStyle || gcs(node), me = d._getMarginExtents(node, s); |
| var l = node.offsetLeft - me.l, t = node.offsetTop - me.t, p = node.parentNode; |
| if(d.isMoz){ |
| // Mozilla: |
| // If offsetParent has a computed overflow != visible, the offsetLeft is decreased |
| // by the parent's border. |
| // We don't want to compute the parent's style, so instead we examine node's |
| // computed left/top which is more stable. |
| var sl = parseFloat(s.left), st = parseFloat(s.top); |
| if(!isNaN(sl) && !isNaN(st)){ |
| l = sl, t = st; |
| }else{ |
| // If child's computed left/top are not parseable as a number (e.g. "auto"), we |
| // have no choice but to examine the parent's computed style. |
| if(p && p.style){ |
| var pcs = gcs(p); |
| if(pcs.overflow != "visible"){ |
| var be = d._getBorderExtents(p, pcs); |
| l += be.l, t += be.t; |
| } |
| } |
| } |
| }else if(d.isOpera || (d.isIE > 7 && !d.isQuirks)){ |
| // On Opera and IE 8, offsetLeft/Top includes the parent's border |
| if(p){ |
| be = d._getBorderExtents(p); |
| l -= be.l; |
| t -= be.t; |
| } |
| } |
| return { |
| l: l, |
| t: t, |
| w: node.offsetWidth + me.w, |
| h: node.offsetHeight + me.h |
| }; |
| } |
| |
| dojo._getMarginSize = function(/*DomNode*/node, /*Object*/computedStyle){ |
| // summary: |
| // returns an object that encodes the width and height of |
| // the node's margin box |
| node = byId(node); |
| var me = d._getMarginExtents(node, computedStyle || gcs(node)); |
| |
| var size = node.getBoundingClientRect(); |
| return { |
| w: (size.right - size.left) + me.w, |
| h: (size.bottom - size.top) + me.h |
| } |
| } |
| |
| dojo._getContentBox = function(node, computedStyle){ |
| // summary: |
| // Returns an object that encodes the width, height, left and top |
| // positions of the node's content box, irrespective of the |
| // current box model. |
| |
| // clientWidth/Height are important since the automatically account for scrollbars |
| // fallback to offsetWidth/Height for special cases (see #3378) |
| var s = computedStyle || gcs(node), |
| pe = d._getPadExtents(node, s), |
| be = d._getBorderExtents(node, s), |
| w = node.clientWidth, |
| h |
| ; |
| if(!w){ |
| w = node.offsetWidth, h = node.offsetHeight; |
| }else{ |
| h = node.clientHeight, be.w = be.h = 0; |
| } |
| // On Opera, offsetLeft includes the parent's border |
| if(d.isOpera){ pe.l += be.l; pe.t += be.t; }; |
| return { |
| l: pe.l, |
| t: pe.t, |
| w: w - pe.w - be.w, |
| h: h - pe.h - be.h |
| }; |
| }; |
| |
| dojo._getBorderBox = function(node, computedStyle){ |
| var s = computedStyle || gcs(node), |
| pe = d._getPadExtents(node, s), |
| cb = d._getContentBox(node, s) |
| ; |
| return { |
| l: cb.l - pe.l, |
| t: cb.t - pe.t, |
| w: cb.w + pe.w, |
| h: cb.h + pe.h |
| }; |
| }; |
| |
| // Box setters depend on box context because interpretation of width/height styles |
| // vary wrt box context. |
| // |
| // The value of dojo.boxModel is used to determine box context. |
| // dojo.boxModel can be set directly to change behavior. |
| // |
| // Beware of display: inline objects that have padding styles |
| // because the user agent ignores padding (it's a bogus setup anyway) |
| // |
| // Be careful with IMGs because they are inline or block depending on |
| // browser and browser mode. |
| // |
| // Elements other than DIV may have special quirks, like built-in |
| // margins or padding, or values not detectable via computedStyle. |
| // In particular, margins on TABLE do not seems to appear |
| // at all in computedStyle on Mozilla. |
| |
| dojo._setBox = function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/h, /*String?*/u){ |
| // summary: |
| // sets width/height/left/top in the current (native) box-model |
| // dimentions. Uses the unit passed in u. |
| // node: |
| // DOM Node reference. Id string not supported for performance |
| // reasons. |
| // l: |
| // left offset from parent. |
| // t: |
| // top offset from parent. |
| // w: |
| // width in current box model. |
| // h: |
| // width in current box model. |
| // u: |
| // unit measure to use for other measures. Defaults to "px". |
| u = u || "px"; |
| var s = node.style; |
| if(!isNaN(l)){ s.left = l + u; } |
| if(!isNaN(t)){ s.top = t + u; } |
| if(w >= 0){ s.width = w + u; } |
| if(h >= 0){ s.height = h + u; } |
| }; |
| |
| dojo._isButtonTag = function(/*DomNode*/node) { |
| // summary: |
| // True if the node is BUTTON or INPUT.type="button". |
| return node.tagName == "BUTTON" |
| || node.tagName=="INPUT" && (node.getAttribute("type")||'').toUpperCase() == "BUTTON"; // boolean |
| }; |
| |
| dojo._usesBorderBox = function(/*DomNode*/node){ |
| // summary: |
| // True if the node uses border-box layout. |
| |
| // We could test the computed style of node to see if a particular box |
| // has been specified, but there are details and we choose not to bother. |
| |
| // TABLE and BUTTON (and INPUT type=button) are always border-box by default. |
| // If you have assigned a different box to either one via CSS then |
| // box functions will break. |
| |
| var n = node.tagName; |
| return d.boxModel=="border-box" || n=="TABLE" || d._isButtonTag(node); // boolean |
| }; |
| |
| dojo._setContentSize = function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle){ |
| // summary: |
| // Sets the size of the node's contents, irrespective of margins, |
| // padding, or borders. |
| if(d._usesBorderBox(node)){ |
| var pb = d._getPadBorderExtents(node, computedStyle); |
| if(widthPx >= 0){ widthPx += pb.w; } |
| if(heightPx >= 0){ heightPx += pb.h; } |
| } |
| d._setBox(node, NaN, NaN, widthPx, heightPx); |
| }; |
| |
| dojo._setMarginBox = function(/*DomNode*/node, /*Number?*/leftPx, /*Number?*/topPx, |
| /*Number?*/widthPx, /*Number?*/heightPx, |
| /*Object*/computedStyle){ |
| // summary: |
| // sets the size of the node's margin box and placement |
| // (left/top), irrespective of box model. Think of it as a |
| // passthrough to dojo._setBox that handles box-model vagaries for |
| // you. |
| |
| var s = computedStyle || gcs(node), |
| // Some elements have special padding, margin, and box-model settings. |
| // To use box functions you may need to set padding, margin explicitly. |
| // Controlling box-model is harder, in a pinch you might set dojo.boxModel. |
| bb = d._usesBorderBox(node), |
| pb = bb ? _nilExtents : d._getPadBorderExtents(node, s) |
| ; |
| if(d.isWebKit){ |
| // on Safari (3.1.2), button nodes with no explicit size have a default margin |
| // setting an explicit size eliminates the margin. |
| // We have to swizzle the width to get correct margin reading. |
| if(d._isButtonTag(node)){ |
| var ns = node.style; |
| if(widthPx >= 0 && !ns.width) { ns.width = "4px"; } |
| if(heightPx >= 0 && !ns.height) { ns.height = "4px"; } |
| } |
| } |
| var mb = d._getMarginExtents(node, s); |
| if(widthPx >= 0){ widthPx = Math.max(widthPx - pb.w - mb.w, 0); } |
| if(heightPx >= 0){ heightPx = Math.max(heightPx - pb.h - mb.h, 0); } |
| d._setBox(node, leftPx, topPx, widthPx, heightPx); |
| }; |
| |
| var _nilExtents = { l:0, t:0, w:0, h:0 }; |
| |
| // public API |
| |
| dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box){ |
| // summary: |
| // Getter/setter for the margin-box of node. |
| // description: |
| // Getter/setter for the margin-box of node. |
| // Returns an object in the expected format of box (regardless |
| // if box is passed). The object might look like: |
| // `{ l: 50, t: 200, w: 300: h: 150 }` |
| // for a node offset from its parent 50px to the left, 200px from |
| // the top with a margin width of 300px and a margin-height of |
| // 150px. |
| // node: |
| // id or reference to DOM Node to get/set box for |
| // box: |
| // If passed, denotes that dojo.marginBox() should |
| // update/set the margin box for node. Box is an object in the |
| // above format. All properties are optional if passed. |
| // example: |
| // Retrieve the marginbox of a passed node |
| // | var box = dojo.marginBox("someNodeId"); |
| // | console.dir(box); |
| // |
| // example: |
| // Set a node's marginbox to the size of another node |
| // | var box = dojo.marginBox("someNodeId"); |
| // | dojo.marginBox("someOtherNode", box); |
| |
| var n = byId(node), s = gcs(n), b = box; |
| return !b ? d._getMarginBox(n, s) : d._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object |
| }; |
| |
| dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box){ |
| // summary: |
| // Getter/setter for the content-box of node. |
| // description: |
| // Returns an object in the expected format of box (regardless if box is passed). |
| // The object might look like: |
| // `{ l: 50, t: 200, w: 300: h: 150 }` |
| // for a node offset from its parent 50px to the left, 200px from |
| // the top with a content width of 300px and a content-height of |
| // 150px. Note that the content box may have a much larger border |
| // or margin box, depending on the box model currently in use and |
| // CSS values set/inherited for node. |
| // While the getter will return top and left values, the |
| // setter only accepts setting the width and height. |
| // node: |
| // id or reference to DOM Node to get/set box for |
| // box: |
| // If passed, denotes that dojo.contentBox() should |
| // update/set the content box for node. Box is an object in the |
| // above format, but only w (width) and h (height) are supported. |
| // All properties are optional if passed. |
| var n = byId(node), s = gcs(n), b = box; |
| return !b ? d._getContentBox(n, s) : d._setContentSize(n, b.w, b.h, s); // Object |
| }; |
| |
| // ============================= |
| // Positioning |
| // ============================= |
| |
| var _sumAncestorProperties = function(node, prop){ |
| if(!(node = (node||0).parentNode)){return 0;} |
| var val, retVal = 0, _b = d.body(); |
| while(node && node.style){ |
| if(gcs(node).position == "fixed"){ |
| return 0; |
| } |
| val = node[prop]; |
| if(val){ |
| retVal += val - 0; |
| // opera and khtml #body & #html has the same values, we only |
| // need one value |
| if(node == _b){ break; } |
| } |
| node = node.parentNode; |
| } |
| return retVal; // integer |
| }; |
| |
| dojo._docScroll = function(){ |
| var n = d.global; |
| return "pageXOffset" in n |
| ? { x:n.pageXOffset, y:n.pageYOffset } |
| : (n = d.isQuirks? d.doc.body : d.doc.documentElement, { x:d._fixIeBiDiScrollLeft(n.scrollLeft || 0), y:n.scrollTop || 0 }); |
| }; |
| |
| dojo._isBodyLtr = function(){ |
| return "_bodyLtr" in d? d._bodyLtr : |
| d._bodyLtr = (d.body().dir || d.doc.documentElement.dir || "ltr").toLowerCase() == "ltr"; // Boolean |
| }; |
| |
| dojo._getIeDocumentElementOffset = function(){ |
| // summary: |
| // returns the offset in x and y from the document body to the |
| // visual edge of the page |
| // description: |
| // The following values in IE contain an offset: |
| // | event.clientX |
| // | event.clientY |
| // | node.getBoundingClientRect().left |
| // | node.getBoundingClientRect().top |
| // But other position related values do not contain this offset, |
| // such as node.offsetLeft, node.offsetTop, node.style.left and |
| // node.style.top. The offset is always (2, 2) in LTR direction. |
| // When the body is in RTL direction, the offset counts the width |
| // of left scroll bar's width. This function computes the actual |
| // offset. |
| |
| //NOTE: assumes we're being called in an IE browser |
| |
| var de = d.doc.documentElement; // only deal with HTML element here, _abs handles body/quirks |
| |
| if(d.isIE < 8){ |
| var r = de.getBoundingClientRect(); // works well for IE6+ |
| //console.debug('rect left,top = ' + r.left+','+r.top + ', html client left/top = ' + de.clientLeft+','+de.clientTop + ', rtl = ' + (!d._isBodyLtr()) + ', quirks = ' + d.isQuirks); |
| var l = r.left, |
| t = r.top; |
| if(d.isIE < 7){ |
| l += de.clientLeft; // scrollbar size in strict/RTL, or, |
| t += de.clientTop; // HTML border size in strict |
| } |
| return { |
| x: l < 0? 0 : l, // FRAME element border size can lead to inaccurate negative values |
| y: t < 0? 0 : t |
| }; |
| }else{ |
| return { |
| x: 0, |
| y: 0 |
| }; |
| } |
| |
| }; |
| |
| dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft){ |
| // In RTL direction, scrollLeft should be a negative value, but IE |
| // returns a positive one. All codes using documentElement.scrollLeft |
| // must call this function to fix this error, otherwise the position |
| // will offset to right when there is a horizontal scrollbar. |
| |
| var ie = d.isIE; |
| if(ie && !d._isBodyLtr()){ |
| var qk = d.isQuirks, |
| de = qk ? d.doc.body : d.doc.documentElement; |
| if(ie == 6 && !qk && d.global.frameElement && de.scrollHeight > de.clientHeight){ |
| scrollLeft += de.clientLeft; // workaround ie6+strict+rtl+iframe+vertical-scrollbar bug where clientWidth is too small by clientLeft pixels |
| } |
| return (ie < 8 || qk) ? (scrollLeft + de.clientWidth - de.scrollWidth) : -scrollLeft; // Integer |
| } |
| return scrollLeft; // Integer |
| }; |
| |
| // FIXME: need a setter for coords or a moveTo!! |
| dojo._abs = dojo.position = function(/*DomNode*/node, /*Boolean?*/includeScroll){ |
| // summary: |
| // Gets the position and size of the passed element relative to |
| // the viewport (if includeScroll==false), or relative to the |
| // document root (if includeScroll==true). |
| // |
| // description: |
| // Returns an object of the form: |
| // { x: 100, y: 300, w: 20, h: 15 } |
| // If includeScroll==true, the x and y values will include any |
| // document offsets that may affect the position relative to the |
| // viewport. |
| // Uses the border-box model (inclusive of border and padding but |
| // not margin). Does not act as a setter. |
| |
| node = byId(node); |
| var db = d.body(), |
| dh = db.parentNode, |
| ret = node.getBoundingClientRect(); |
| ret = { x: ret.left, y: ret.top, w: ret.right - ret.left, h: ret.bottom - ret.top }; |
| if(d.isIE){ |
| // On IE there's a 2px offset that we need to adjust for, see _getIeDocumentElementOffset() |
| var offset = d._getIeDocumentElementOffset(); |
| |
| // fixes the position in IE, quirks mode |
| ret.x -= offset.x + (d.isQuirks ? db.clientLeft+db.offsetLeft : 0); |
| ret.y -= offset.y + (d.isQuirks ? db.clientTop+db.offsetTop : 0); |
| }else if(d.isFF == 3){ |
| // In FF3 you have to subtract the document element margins. |
| // Fixed in FF3.5 though. |
| var cs = gcs(dh); |
| ret.x -= px(dh, cs.marginLeft) + px(dh, cs.borderLeftWidth); |
| ret.y -= px(dh, cs.marginTop) + px(dh, cs.borderTopWidth); |
| } |
| // account for document scrolling |
| if(includeScroll){ |
| var scroll = d._docScroll(); |
| ret.x += scroll.x; |
| ret.y += scroll.y; |
| } |
| |
| return ret; // Object |
| }; |
| |
| dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){ |
| // summary: |
| // Deprecated: Use position() for border-box x/y/w/h |
| // or marginBox() for margin-box w/h/l/t. |
| // Returns an object representing a node's size and position. |
| // |
| // description: |
| // Returns an object that measures margin-box (w)idth/(h)eight |
| // and absolute position x/y of the border-box. Also returned |
| // is computed (l)eft and (t)op values in pixels from the |
| // node's offsetParent as returned from marginBox(). |
| // Return value will be in the form: |
| //| { l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 } |
| // Does not act as a setter. If includeScroll is passed, the x and |
| // y params are affected as one would expect in dojo.position(). |
| var n = byId(node), s = gcs(n), mb = d._getMarginBox(n, s); |
| var abs = d.position(n, includeScroll); |
| mb.x = abs.x; |
| mb.y = abs.y; |
| return mb; |
| }; |
| |
| // ============================= |
| // Element attribute Functions |
| // ============================= |
| |
| // dojo.attr() should conform to http://www.w3.org/TR/DOM-Level-2-Core/ |
| |
| var _propNames = { |
| // properties renamed to avoid clashes with reserved words |
| "class": "className", |
| "for": "htmlFor", |
| // properties written as camelCase |
| tabindex: "tabIndex", |
| readonly: "readOnly", |
| colspan: "colSpan", |
| frameborder: "frameBorder", |
| rowspan: "rowSpan", |
| valuetype: "valueType" |
| }, |
| _attrNames = { |
| // original attribute names |
| classname: "class", |
| htmlfor: "for", |
| // for IE |
| tabindex: "tabIndex", |
| readonly: "readOnly" |
| }, |
| _forcePropNames = { |
| innerHTML: 1, |
| className: 1, |
| htmlFor: d.isIE, |
| value: 1 |
| }; |
| |
| var _fixAttrName = function(/*String*/ name){ |
| return _attrNames[name.toLowerCase()] || name; |
| }; |
| |
| var _hasAttr = function(node, name){ |
| var attr = node.getAttributeNode && node.getAttributeNode(name); |
| return attr && attr.specified; // Boolean |
| }; |
| |
| // There is a difference in the presence of certain properties and their default values |
| // between browsers. For example, on IE "disabled" is present on all elements, |
| // but it is value is "false"; "tabIndex" of <div> returns 0 by default on IE, yet other browsers |
| // can return -1. |
| |
| dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name){ |
| // summary: |
| // Returns true if the requested attribute is specified on the |
| // given element, and false otherwise. |
| // node: |
| // id or reference to the element to check |
| // name: |
| // the name of the attribute |
| // returns: |
| // true if the requested attribute is specified on the |
| // given element, and false otherwise |
| var lc = name.toLowerCase(); |
| return _forcePropNames[_propNames[lc] || name] || _hasAttr(byId(node), _attrNames[lc] || name); // Boolean |
| }; |
| |
| var _evtHdlrMap = {}, _ctr = 0, |
| _attrId = dojo._scopeName + "attrid", |
| // the next dictionary lists elements with read-only innerHTML on IE |
| _roInnerHtml = {col: 1, colgroup: 1, |
| // frameset: 1, head: 1, html: 1, style: 1, |
| table: 1, tbody: 1, tfoot: 1, thead: 1, tr: 1, title: 1}; |
| |
| dojo.attr = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value){ |
| // summary: |
| // Gets or sets an attribute on an HTML element. |
| // description: |
| // Handles normalized getting and setting of attributes on DOM |
| // Nodes. If 2 arguments are passed, and a the second argumnt is a |
| // string, acts as a getter. |
| // |
| // If a third argument is passed, or if the second argument is a |
| // map of attributes, acts as a setter. |
| // |
| // When passing functions as values, note that they will not be |
| // directly assigned to slots on the node, but rather the default |
| // behavior will be removed and the new behavior will be added |
| // using `dojo.connect()`, meaning that event handler properties |
| // will be normalized and that some caveats with regards to |
| // non-standard behaviors for onsubmit apply. Namely that you |
| // should cancel form submission using `dojo.stopEvent()` on the |
| // passed event object instead of returning a boolean value from |
| // the handler itself. |
| // node: |
| // id or reference to the element to get or set the attribute on |
| // name: |
| // the name of the attribute to get or set. |
| // value: |
| // The value to set for the attribute |
| // returns: |
| // when used as a getter, the value of the requested attribute |
| // or null if that attribute does not have a specified or |
| // default value; |
| // |
| // when used as a setter, the DOM node |
| // |
| // example: |
| // | // get the current value of the "foo" attribute on a node |
| // | dojo.attr(dojo.byId("nodeId"), "foo"); |
| // | // or we can just pass the id: |
| // | dojo.attr("nodeId", "foo"); |
| // |
| // example: |
| // | // use attr() to set the tab index |
| // | dojo.attr("nodeId", "tabIndex", 3); |
| // | |
| // |
| // example: |
| // Set multiple values at once, including event handlers: |
| // | dojo.attr("formId", { |
| // | "foo": "bar", |
| // | "tabIndex": -1, |
| // | "method": "POST", |
| // | "onsubmit": function(e){ |
| // | // stop submitting the form. Note that the IE behavior |
| // | // of returning true or false will have no effect here |
| // | // since our handler is connect()ed to the built-in |
| // | // onsubmit behavior and so we need to use |
| // | // dojo.stopEvent() to ensure that the submission |
| // | // doesn't proceed. |
| // | dojo.stopEvent(e); |
| // | |
| // | // submit the form with Ajax |
| // | dojo.xhrPost({ form: "formId" }); |
| // | } |
| // | }); |
| // |
| // example: |
| // Style is s special case: Only set with an object hash of styles |
| // | dojo.attr("someNode",{ |
| // | id:"bar", |
| // | style:{ |
| // | width:"200px", height:"100px", color:"#000" |
| // | } |
| // | }); |
| // |
| // example: |
| // Again, only set style as an object hash of styles: |
| // | var obj = { color:"#fff", backgroundColor:"#000" }; |
| // | dojo.attr("someNode", "style", obj); |
| // | |
| // | // though shorter to use `dojo.style()` in this case: |
| // | dojo.style("someNode", obj); |
| |
| node = byId(node); |
| var args = arguments.length, prop; |
| if(args == 2 && typeof name != "string"){ // inline'd type check |
| // the object form of setter: the 2nd argument is a dictionary |
| for(var x in name){ |
| d.attr(node, x, name[x]); |
| } |
| return node; // DomNode |
| } |
| var lc = name.toLowerCase(), |
| propName = _propNames[lc] || name, |
| forceProp = _forcePropNames[propName], |
| attrName = _attrNames[lc] || name; |
| if(args == 3){ |
| // setter |
| do{ |
| if(propName == "style" && typeof value != "string"){ // inline'd type check |
| // special case: setting a style |
| d.style(node, value); |
| break; |
| } |
| if(propName == "innerHTML"){ |
| // special case: assigning HTML |
| if(d.isIE && node.tagName.toLowerCase() in _roInnerHtml){ |
| d.empty(node); |
| node.appendChild(d._toDom(value, node.ownerDocument)); |
| }else{ |
| node[propName] = value; |
| } |
| break; |
| } |
| if(d.isFunction(value)){ |
| // special case: assigning an event handler |
| // clobber if we can |
| var attrId = d.attr(node, _attrId); |
| if(!attrId){ |
| attrId = _ctr++; |
| d.attr(node, _attrId, attrId); |
| } |
| if(!_evtHdlrMap[attrId]){ |
| _evtHdlrMap[attrId] = {}; |
| } |
| var h = _evtHdlrMap[attrId][propName]; |
| if(h){ |
| d.disconnect(h); |
| }else{ |
| try{ |
| delete node[propName]; |
| }catch(e){} |
| } |
| // ensure that event objects are normalized, etc. |
| _evtHdlrMap[attrId][propName] = d.connect(node, propName, value); |
| break; |
| } |
| if(forceProp || typeof value == "boolean"){ |
| // special case: forcing assignment to the property |
| // special case: setting boolean to a property instead of attribute |
| node[propName] = value; |
| break; |
| } |
| // node's attribute |
| node.setAttribute(attrName, value); |
| }while(false); |
| return node; // DomNode |
| } |
| // getter |
| // should we access this attribute via a property or |
| // via getAttribute()? |
| value = node[propName]; |
| if(forceProp && typeof value != "undefined"){ |
| // node's property |
| return value; // Anything |
| } |
| if(propName != "href" && (typeof value == "boolean" || d.isFunction(value))){ |
| // node's property |
| return value; // Anything |
| } |
| // node's attribute |
| // we need _hasAttr() here to guard against IE returning a default value |
| return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything |
| }; |
| |
| dojo.removeAttr = function(/*DomNode|String*/ node, /*String*/ name){ |
| // summary: |
| // Removes an attribute from an HTML element. |
| // node: |
| // id or reference to the element to remove the attribute from |
| // name: |
| // the name of the attribute to remove |
| byId(node).removeAttribute(_fixAttrName(name)); |
| }; |
| |
| dojo.getNodeProp = function(/*DomNode|String*/ node, /*String*/ name){ |
| // summary: |
| // Returns an effective value of a property or an attribute. |
| // node: |
| // id or reference to the element to remove the attribute from |
| // name: |
| // the name of the attribute |
| node = byId(node); |
| var lc = name.toLowerCase(), |
| propName = _propNames[lc] || name; |
| if((propName in node) && propName != "href"){ |
| // node's property |
| return node[propName]; // Anything |
| } |
| // node's attribute |
| var attrName = _attrNames[lc] || name; |
| return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything |
| }; |
| |
| dojo.create = function(tag, attrs, refNode, pos){ |
| // summary: |
| // Create an element, allowing for optional attribute decoration |
| // and placement. |
| // |
| // description: |
| // A DOM Element creation function. A shorthand method for creating a node or |
| // a fragment, and allowing for a convenient optional attribute setting step, |
| // as well as an optional DOM placement reference. |
| //| |
| // Attributes are set by passing the optional object through `dojo.attr`. |
| // See `dojo.attr` for noted caveats and nuances, and API if applicable. |
| //| |
| // Placement is done via `dojo.place`, assuming the new node to be the action |
| // node, passing along the optional reference node and position. |
| // |
| // tag: String|DomNode |
| // A string of the element to create (eg: "div", "a", "p", "li", "script", "br"), |
| // or an existing DOM node to process. |
| // |
| // attrs: Object |
| // An object-hash of attributes to set on the newly created node. |
| // Can be null, if you don't want to set any attributes/styles. |
| // See: `dojo.attr` for a description of available attributes. |
| // |
| // refNode: String?|DomNode? |
| // Optional reference node. Used by `dojo.place` to place the newly created |
| // node somewhere in the dom relative to refNode. Can be a DomNode reference |
| // or String ID of a node. |
| // |
| // pos: String? |
| // Optional positional reference. Defaults to "last" by way of `dojo.place`, |
| // though can be set to "first","after","before","last", "replace" or "only" |
| // to further control the placement of the new node relative to the refNode. |
| // 'refNode' is required if a 'pos' is specified. |
| // |
| // returns: DomNode |
| // |
| // example: |
| // Create a DIV: |
| // | var n = dojo.create("div"); |
| // |
| // example: |
| // Create a DIV with content: |
| // | var n = dojo.create("div", { innerHTML:"<p>hi</p>" }); |
| // |
| // example: |
| // Place a new DIV in the BODY, with no attributes set |
| // | var n = dojo.create("div", null, dojo.body()); |
| // |
| // example: |
| // Create an UL, and populate it with LI's. Place the list as the first-child of a |
| // node with id="someId": |
| // | var ul = dojo.create("ul", null, "someId", "first"); |
| // | var items = ["one", "two", "three", "four"]; |
| // | dojo.forEach(items, function(data){ |
| // | dojo.create("li", { innerHTML: data }, ul); |
| // | }); |
| // |
| // example: |
| // Create an anchor, with an href. Place in BODY: |
| // | dojo.create("a", { href:"foo.html", title:"Goto FOO!" }, dojo.body()); |
| // |
| // example: |
| // Create a `dojo.NodeList()` from a new element (for syntatic sugar): |
| // | dojo.query(dojo.create('div')) |
| // | .addClass("newDiv") |
| // | .onclick(function(e){ console.log('clicked', e.target) }) |
| // | .place("#someNode"); // redundant, but cleaner. |
| |
| var doc = d.doc; |
| if(refNode){ |
| refNode = byId(refNode); |
| doc = refNode.ownerDocument; |
| } |
| if(typeof tag == "string"){ // inline'd type check |
| tag = doc.createElement(tag); |
| } |
| if(attrs){ d.attr(tag, attrs); } |
| if(refNode){ d.place(tag, refNode, pos); } |
| return tag; // DomNode |
| }; |
| |
| /*===== |
| dojo.empty = function(node){ |
| // summary: |
| // safely removes all children of the node. |
| // node: DOMNode|String |
| // a reference to a DOM node or an id. |
| // example: |
| // Destroy node's children byId: |
| // | dojo.empty("someId"); |
| // |
| // example: |
| // Destroy all nodes' children in a list by reference: |
| // | dojo.query(".someNode").forEach(dojo.empty); |
| } |
| =====*/ |
| |
| d.empty = |
| d.isIE ? function(node){ |
| node = byId(node); |
| for(var c; c = node.lastChild;){ // intentional assignment |
| d.destroy(c); |
| } |
| } : |
| function(node){ |
| byId(node).innerHTML = ""; |
| }; |
| |
| /*===== |
| dojo._toDom = function(frag, doc){ |
| // summary: |
| // instantiates an HTML fragment returning the corresponding DOM. |
| // frag: String |
| // the HTML fragment |
| // doc: DocumentNode? |
| // optional document to use when creating DOM nodes, defaults to |
| // dojo.doc if not specified. |
| // returns: DocumentFragment |
| // |
| // example: |
| // Create a table row: |
| // | var tr = dojo._toDom("<tr><td>First!</td></tr>"); |
| } |
| =====*/ |
| |
| // support stuff for dojo._toDom |
| var tagWrap = { |
| option: ["select"], |
| tbody: ["table"], |
| thead: ["table"], |
| tfoot: ["table"], |
| tr: ["table", "tbody"], |
| td: ["table", "tbody", "tr"], |
| th: ["table", "thead", "tr"], |
| legend: ["fieldset"], |
| caption: ["table"], |
| colgroup: ["table"], |
| col: ["table", "colgroup"], |
| li: ["ul"] |
| }, |
| reTag = /<\s*([\w\:]+)/, |
| masterNode = {}, masterNum = 0, |
| masterName = "__" + d._scopeName + "ToDomId"; |
| |
| // generate start/end tag strings to use |
| // for the injection for each special tag wrap case. |
| for(var param in tagWrap){ |
| if(tagWrap.hasOwnProperty(param)){ |
| var tw = tagWrap[param]; |
| tw.pre = param == "option" ? '<select multiple="multiple">' : "<" + tw.join("><") + ">"; |
| tw.post = "</" + tw.reverse().join("></") + ">"; |
| // the last line is destructive: it reverses the array, |
| // but we don't care at this point |
| } |
| } |
| |
| d._toDom = function(frag, doc){ |
| // summary: |
| // converts HTML string into DOM nodes. |
| |
| doc = doc || d.doc; |
| var masterId = doc[masterName]; |
| if(!masterId){ |
| doc[masterName] = masterId = ++masterNum + ""; |
| masterNode[masterId] = doc.createElement("div"); |
| } |
| |
| // make sure the frag is a string. |
| frag += ""; |
| |
| // find the starting tag, and get node wrapper |
| var match = frag.match(reTag), |
| tag = match ? match[1].toLowerCase() : "", |
| master = masterNode[masterId], |
| wrap, i, fc, df; |
| if(match && tagWrap[tag]){ |
| wrap = tagWrap[tag]; |
| master.innerHTML = wrap.pre + frag + wrap.post; |
| for(i = wrap.length; i; --i){ |
| master = master.firstChild; |
| } |
| }else{ |
| master.innerHTML = frag; |
| } |
| |
| // one node shortcut => return the node itself |
| if(master.childNodes.length == 1){ |
| return master.removeChild(master.firstChild); // DOMNode |
| } |
| |
| // return multiple nodes as a document fragment |
| df = doc.createDocumentFragment(); |
| while(fc = master.firstChild){ // intentional assignment |
| df.appendChild(fc); |
| } |
| return df; // DOMNode |
| }; |
| |
| // ============================= |
| // (CSS) Class Functions |
| // ============================= |
| var _className = "className"; |
| |
| dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr){ |
| // summary: |
| // Returns whether or not the specified classes are a portion of the |
| // class list currently applied to the node. |
| // |
| // node: |
| // String ID or DomNode reference to check the class for. |
| // |
| // classStr: |
| // A string class name to look for. |
| // |
| // example: |
| // Do something if a node with id="someNode" has class="aSillyClassName" present |
| // | if(dojo.hasClass("someNode","aSillyClassName")){ ... } |
| |
| return ((" "+ byId(node)[_className] +" ").indexOf(" " + classStr + " ") >= 0); // Boolean |
| }; |
| |
| var spaces = /\s+/, a1 = [""], |
| fakeNode = {}, |
| str2array = function(s){ |
| if(typeof s == "string" || s instanceof String){ |
| if(s.indexOf(" ") < 0){ |
| a1[0] = s; |
| return a1; |
| }else{ |
| return s.split(spaces); |
| } |
| } |
| // assumed to be an array |
| return s || ""; |
| }; |
| |
| dojo.addClass = function(/*DomNode|String*/node, /*String|Array*/classStr){ |
| // summary: |
| // Adds the specified classes to the end of the class list on the |
| // passed node. Will not re-apply duplicate classes. |
| // |
| // node: |
| // String ID or DomNode reference to add a class string too |
| // |
| // classStr: |
| // A String class name to add, or several space-separated class names, |
| // or an array of class names. |
| // |
| // example: |
| // Add a class to some node: |
| // | dojo.addClass("someNode", "anewClass"); |
| // |
| // example: |
| // Add two classes at once: |
| // | dojo.addClass("someNode", "firstClass secondClass"); |
| // |
| // example: |
| // Add two classes at once (using array): |
| // | dojo.addClass("someNode", ["firstClass", "secondClass"]); |
| // |
| // example: |
| // Available in `dojo.NodeList` for multiple additions |
| // | dojo.query("ul > li").addClass("firstLevel"); |
| |
| node = byId(node); |
| classStr = str2array(classStr); |
| var cls = node[_className], oldLen; |
| cls = cls ? " " + cls + " " : " "; |
| oldLen = cls.length; |
| for(var i = 0, len = classStr.length, c; i < len; ++i){ |
| c = classStr[i]; |
| if(c && cls.indexOf(" " + c + " ") < 0){ |
| cls += c + " "; |
| } |
| } |
| if(oldLen < cls.length){ |
| node[_className] = cls.substr(1, cls.length - 2); |
| } |
| }; |
| |
| dojo.removeClass = function(/*DomNode|String*/node, /*String|Array?*/classStr){ |
| // summary: |
| // Removes the specified classes from node. No `dojo.hasClass` |
| // check is required. |
| // |
| // node: |
| // String ID or DomNode reference to remove the class from. |
| // |
| // classStr: |
| // An optional String class name to remove, or several space-separated |
| // class names, or an array of class names. If omitted, all class names |
| // will be deleted. |
| // |
| // example: |
| // Remove a class from some node: |
| // | dojo.removeClass("someNode", "firstClass"); |
| // |
| // example: |
| // Remove two classes from some node: |
| // | dojo.removeClass("someNode", "firstClass secondClass"); |
| // |
| // example: |
| // Remove two classes from some node (using array): |
| // | dojo.removeClass("someNode", ["firstClass", "secondClass"]); |
| // |
| // example: |
| // Remove all classes from some node: |
| // | dojo.removeClass("someNode"); |
| // |
| // example: |
| // Available in `dojo.NodeList()` for multiple removal |
| // | dojo.query(".foo").removeClass("foo"); |
| |
| node = byId(node); |
| var cls; |
| if(classStr !== undefined){ |
| classStr = str2array(classStr); |
| cls = " " + node[_className] + " "; |
| for(var i = 0, len = classStr.length; i < len; ++i){ |
| cls = cls.replace(" " + classStr[i] + " ", " "); |
| } |
| cls = d.trim(cls); |
| }else{ |
| cls = ""; |
| } |
| if(node[_className] != cls){ node[_className] = cls; } |
| }; |
| |
| dojo.replaceClass = function(/*DomNode|String*/node, /*String|Array*/addClassStr, /*String|Array?*/removeClassStr){ |
| // summary: |
| // Replaces one or more classes on a node if not present. |
| // Operates more quickly than calling dojo.removeClass and dojo.addClass |
| // node: |
| // String ID or DomNode reference to remove the class from. |
| // addClassStr: |
| // A String class name to add, or several space-separated class names, |
| // or an array of class names. |
| // removeClassStr: |
| // A String class name to remove, or several space-separated class names, |
| // or an array of class names. |
| // |
| // example: |
| // | dojo.replaceClass("someNode", "add1 add2", "remove1 remove2"); |
| // |
| // example: |
| // Replace all classes with addMe |
| // | dojo.replaceClass("someNode", "addMe"); |
| // |
| // example: |
| // Available in `dojo.NodeList()` for multiple toggles |
| // | dojo.query(".findMe").replaceClass("addMe", "removeMe"); |
| |
| node = byId(node); |
| fakeNode.className = node.className; |
| dojo.removeClass(fakeNode, removeClassStr); |
| dojo.addClass(fakeNode, addClassStr); |
| if(node.className !== fakeNode.className){ |
| node.className = fakeNode.className; |
| } |
| }; |
| |
| dojo.toggleClass = function(/*DomNode|String*/node, /*String|Array*/classStr, /*Boolean?*/condition){ |
| // summary: |
| // Adds a class to node if not present, or removes if present. |
| // Pass a boolean condition if you want to explicitly add or remove. |
| // condition: |
| // If passed, true means to add the class, false means to remove. |
| // |
| // example: |
| // | dojo.toggleClass("someNode", "hovered"); |
| // |
| // example: |
| // Forcefully add a class |
| // | dojo.toggleClass("someNode", "hovered", true); |
| // |
| // example: |
| // Available in `dojo.NodeList()` for multiple toggles |
| // | dojo.query(".toggleMe").toggleClass("toggleMe"); |
| |
| if(condition === undefined){ |
| condition = !d.hasClass(node, classStr); |
| } |
| d[condition ? "addClass" : "removeClass"](node, classStr); |
| }; |
| |
| })(); |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.NodeList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.NodeList"] = true; |
| dojo.provide("dojo._base.NodeList"); |
| |
| |
| |
| |
| |
| |
| (function(){ |
| |
| var d = dojo; |
| |
| var ap = Array.prototype, aps = ap.slice, apc = ap.concat; |
| |
| var tnl = function(/*Array*/ a, /*dojo.NodeList?*/ parent, /*Function?*/ NodeListCtor){ |
| // summary: |
| // decorate an array to make it look like a `dojo.NodeList`. |
| // a: |
| // Array of nodes to decorate. |
| // parent: |
| // An optional parent NodeList that generated the current |
| // list of nodes. Used to call _stash() so the parent NodeList |
| // can be accessed via end() later. |
| // NodeListCtor: |
| // An optional constructor function to use for any |
| // new NodeList calls. This allows a certain chain of |
| // NodeList calls to use a different object than dojo.NodeList. |
| if(!a.sort){ |
| // make sure it's a real array before we pass it on to be wrapped |
| a = aps.call(a, 0); |
| } |
| var ctor = NodeListCtor || this._NodeListCtor || d._NodeListCtor; |
| a.constructor = ctor; |
| dojo._mixin(a, ctor.prototype); |
| a._NodeListCtor = ctor; |
| return parent ? a._stash(parent) : a; |
| }; |
| |
| var loopBody = function(f, a, o){ |
| a = [0].concat(aps.call(a, 0)); |
| o = o || d.global; |
| return function(node){ |
| a[0] = node; |
| return f.apply(o, a); |
| }; |
| }; |
| |
| // adapters |
| |
| var adaptAsForEach = function(f, o){ |
| // summary: |
| // adapts a single node function to be used in the forEach-type |
| // actions. The initial object is returned from the specialized |
| // function. |
| // f: Function |
| // a function to adapt |
| // o: Object? |
| // an optional context for f |
| return function(){ |
| this.forEach(loopBody(f, arguments, o)); |
| return this; // Object |
| }; |
| }; |
| |
| var adaptAsMap = function(f, o){ |
| // summary: |
| // adapts a single node function to be used in the map-type |
| // actions. The return is a new array of values, as via `dojo.map` |
| // f: Function |
| // a function to adapt |
| // o: Object? |
| // an optional context for f |
| return function(){ |
| return this.map(loopBody(f, arguments, o)); |
| }; |
| }; |
| |
| var adaptAsFilter = function(f, o){ |
| // summary: |
| // adapts a single node function to be used in the filter-type actions |
| // f: Function |
| // a function to adapt |
| // o: Object? |
| // an optional context for f |
| return function(){ |
| return this.filter(loopBody(f, arguments, o)); |
| }; |
| }; |
| |
| var adaptWithCondition = function(f, g, o){ |
| // summary: |
| // adapts a single node function to be used in the map-type |
| // actions, behaves like forEach() or map() depending on arguments |
| // f: Function |
| // a function to adapt |
| // g: Function |
| // a condition function, if true runs as map(), otherwise runs as forEach() |
| // o: Object? |
| // an optional context for f and g |
| return function(){ |
| var a = arguments, body = loopBody(f, a, o); |
| if(g.call(o || d.global, a)){ |
| return this.map(body); // self |
| } |
| this.forEach(body); |
| return this; // self |
| }; |
| }; |
| |
| var magicGuard = function(a){ |
| // summary: |
| // the guard function for dojo.attr() and dojo.style() |
| return a.length == 1 && (typeof a[0] == "string"); // inline'd type check |
| }; |
| |
| var orphan = function(node){ |
| // summary: |
| // function to orphan nodes |
| var p = node.parentNode; |
| if(p){ |
| p.removeChild(node); |
| } |
| }; |
| // FIXME: should we move orphan() to dojo.html? |
| |
| dojo.NodeList = function(){ |
| // summary: |
| // dojo.NodeList is an of Array subclass which adds syntactic |
| // sugar for chaining, common iteration operations, animation, and |
| // node manipulation. NodeLists are most often returned as the |
| // result of dojo.query() calls. |
| // description: |
| // dojo.NodeList instances provide many utilities that reflect |
| // core Dojo APIs for Array iteration and manipulation, DOM |
| // manipulation, and event handling. Instead of needing to dig up |
| // functions in the dojo.* namespace, NodeLists generally make the |
| // full power of Dojo available for DOM manipulation tasks in a |
| // simple, chainable way. |
| // example: |
| // create a node list from a node |
| // | new dojo.NodeList(dojo.byId("foo")); |
| // example: |
| // get a NodeList from a CSS query and iterate on it |
| // | var l = dojo.query(".thinger"); |
| // | l.forEach(function(node, index, nodeList){ |
| // | console.log(index, node.innerHTML); |
| // | }); |
| // example: |
| // use native and Dojo-provided array methods to manipulate a |
| // NodeList without needing to use dojo.* functions explicitly: |
| // | var l = dojo.query(".thinger"); |
| // | // since NodeLists are real arrays, they have a length |
| // | // property that is both readable and writable and |
| // | // push/pop/shift/unshift methods |
| // | console.log(l.length); |
| // | l.push(dojo.create("span")); |
| // | |
| // | // dojo's normalized array methods work too: |
| // | console.log( l.indexOf(dojo.byId("foo")) ); |
| // | // ...including the special "function as string" shorthand |
| // | console.log( l.every("item.nodeType == 1") ); |
| // | |
| // | // NodeLists can be [..] indexed, or you can use the at() |
| // | // function to get specific items wrapped in a new NodeList: |
| // | var node = l[3]; // the 4th element |
| // | var newList = l.at(1, 3); // the 2nd and 4th elements |
| // example: |
| // the style functions you expect are all there too: |
| // | // style() as a getter... |
| // | var borders = dojo.query(".thinger").style("border"); |
| // | // ...and as a setter: |
| // | dojo.query(".thinger").style("border", "1px solid black"); |
| // | // class manipulation |
| // | dojo.query("li:nth-child(even)").addClass("even"); |
| // | // even getting the coordinates of all the items |
| // | var coords = dojo.query(".thinger").coords(); |
| // example: |
| // DOM manipulation functions from the dojo.* namespace area also |
| // available: |
| // | // remove all of the elements in the list from their |
| // | // parents (akin to "deleting" them from the document) |
| // | dojo.query(".thinger").orphan(); |
| // | // place all elements in the list at the front of #foo |
| // | dojo.query(".thinger").place("foo", "first"); |
| // example: |
| // Event handling couldn't be easier. `dojo.connect` is mapped in, |
| // and shortcut handlers are provided for most DOM events: |
| // | // like dojo.connect(), but with implicit scope |
| // | dojo.query("li").connect("onclick", console, "log"); |
| // | |
| // | // many common event handlers are already available directly: |
| // | dojo.query("li").onclick(console, "log"); |
| // | var toggleHovered = dojo.hitch(dojo, "toggleClass", "hovered"); |
| // | dojo.query("p") |
| // | .onmouseenter(toggleHovered) |
| // | .onmouseleave(toggleHovered); |
| // example: |
| // chainability is a key advantage of NodeLists: |
| // | dojo.query(".thinger") |
| // | .onclick(function(e){ /* ... */ }) |
| // | .at(1, 3, 8) // get a subset |
| // | .style("padding", "5px") |
| // | .forEach(console.log); |
| |
| return tnl(Array.apply(null, arguments)); |
| }; |
| |
| //Allow things that new up a NodeList to use a delegated or alternate NodeList implementation. |
| d._NodeListCtor = d.NodeList; |
| |
| var nl = d.NodeList, nlp = nl.prototype; |
| |
| // expose adapters and the wrapper as private functions |
| |
| nl._wrap = nlp._wrap = tnl; |
| nl._adaptAsMap = adaptAsMap; |
| nl._adaptAsForEach = adaptAsForEach; |
| nl._adaptAsFilter = adaptAsFilter; |
| nl._adaptWithCondition = adaptWithCondition; |
| |
| // mass assignment |
| |
| // add array redirectors |
| d.forEach(["slice", "splice"], function(name){ |
| var f = ap[name]; |
| //Use a copy of the this array via this.slice() to allow .end() to work right in the splice case. |
| // CANNOT apply ._stash()/end() to splice since it currently modifies |
| // the existing this array -- it would break backward compatibility if we copy the array before |
| // the splice so that we can use .end(). So only doing the stash option to this._wrap for slice. |
| nlp[name] = function(){ return this._wrap(f.apply(this, arguments), name == "slice" ? this : null); }; |
| }); |
| // concat should be here but some browsers with native NodeList have problems with it |
| |
| // add array.js redirectors |
| d.forEach(["indexOf", "lastIndexOf", "every", "some"], function(name){ |
| var f = d[name]; |
| nlp[name] = function(){ return f.apply(d, [this].concat(aps.call(arguments, 0))); }; |
| }); |
| |
| // add conditional methods |
| d.forEach(["attr", "style"], function(name){ |
| nlp[name] = adaptWithCondition(d[name], magicGuard); |
| }); |
| |
| // add forEach actions |
| d.forEach(["connect", "addClass", "removeClass", "replaceClass", "toggleClass", "empty", "removeAttr"], function(name){ |
| nlp[name] = adaptAsForEach(d[name]); |
| }); |
| |
| dojo.extend(dojo.NodeList, { |
| _normalize: function(/*String||Element||Object||NodeList*/content, /*DOMNode?*/refNode){ |
| // summary: |
| // normalizes data to an array of items to insert. |
| // description: |
| // If content is an object, it can have special properties "template" and |
| // "parse". If "template" is defined, then the template value is run through |
| // dojo.string.substitute (if dojo.string.substitute has been dojo.required elsewhere), |
| // or if templateFunc is a function on the content, that function will be used to |
| // transform the template into a final string to be used for for passing to dojo._toDom. |
| // If content.parse is true, then it is remembered for later, for when the content |
| // nodes are inserted into the DOM. At that point, the nodes will be parsed for widgets |
| // (if dojo.parser has been dojo.required elsewhere). |
| |
| //Wanted to just use a DocumentFragment, but for the array/NodeList |
| //case that meant using cloneNode, but we may not want that. |
| //Cloning should only happen if the node operations span |
| //multiple refNodes. Also, need a real array, not a NodeList from the |
| //DOM since the node movements could change those NodeLists. |
| |
| var parse = content.parse === true ? true : false; |
| |
| //Do we have an object that needs to be run through a template? |
| if(typeof content.template == "string"){ |
| var templateFunc = content.templateFunc || (dojo.string && dojo.string.substitute); |
| content = templateFunc ? templateFunc(content.template, content) : content; |
| } |
| |
| var type = (typeof content); |
| if(type == "string" || type == "number"){ |
| content = dojo._toDom(content, (refNode && refNode.ownerDocument)); |
| if(content.nodeType == 11){ |
| //DocumentFragment. It cannot handle cloneNode calls, so pull out the children. |
| content = dojo._toArray(content.childNodes); |
| }else{ |
| content = [content]; |
| } |
| }else if(!dojo.isArrayLike(content)){ |
| content = [content]; |
| }else if(!dojo.isArray(content)){ |
| //To get to this point, content is array-like, but |
| //not an array, which likely means a DOM NodeList. Convert it now. |
| content = dojo._toArray(content); |
| } |
| |
| //Pass around the parse info |
| if(parse){ |
| content._runParse = true; |
| } |
| return content; //Array |
| }, |
| |
| _cloneNode: function(/*DOMNode*/ node){ |
| // summary: |
| // private utility to clone a node. Not very interesting in the vanilla |
| // dojo.NodeList case, but delegates could do interesting things like |
| // clone event handlers if that is derivable from the node. |
| return node.cloneNode(true); |
| }, |
| |
| _place: function(/*Array*/ary, /*DOMNode*/refNode, /*String*/position, /*Boolean*/useClone){ |
| // summary: |
| // private utility to handle placing an array of nodes relative to another node. |
| // description: |
| // Allows for cloning the nodes in the array, and for |
| // optionally parsing widgets, if ary._runParse is true. |
| |
| //Avoid a disallowed operation if trying to do an innerHTML on a non-element node. |
| if(refNode.nodeType != 1 && position == "only"){ |
| return; |
| } |
| var rNode = refNode, tempNode; |
| |
| //Always cycle backwards in case the array is really a |
| //DOM NodeList and the DOM operations take it out of the live collection. |
| var length = ary.length; |
| for(var i = length - 1; i >= 0; i--){ |
| var node = (useClone ? this._cloneNode(ary[i]) : ary[i]); |
| |
| //If need widget parsing, use a temp node, instead of waiting after inserting into |
| //real DOM because we need to start widget parsing at one node up from current node, |
| //which could cause some already parsed widgets to be parsed again. |
| if(ary._runParse && dojo.parser && dojo.parser.parse){ |
| if(!tempNode){ |
| tempNode = rNode.ownerDocument.createElement("div"); |
| } |
| tempNode.appendChild(node); |
| dojo.parser.parse(tempNode); |
| node = tempNode.firstChild; |
| while(tempNode.firstChild){ |
| tempNode.removeChild(tempNode.firstChild); |
| } |
| } |
| |
| if(i == length - 1){ |
| dojo.place(node, rNode, position); |
| }else{ |
| rNode.parentNode.insertBefore(node, rNode); |
| } |
| rNode = node; |
| } |
| }, |
| |
| _stash: function(parent){ |
| // summary: |
| // private function to hold to a parent NodeList. end() to return the parent NodeList. |
| // |
| // example: |
| // How to make a `dojo.NodeList` method that only returns the third node in |
| // the dojo.NodeList but allows access to the original NodeList by using this._stash: |
| // | dojo.extend(dojo.NodeList, { |
| // | third: function(){ |
| // | var newNodeList = dojo.NodeList(this[2]); |
| // | return newNodeList._stash(this); |
| // | } |
| // | }); |
| // | // then see how _stash applies a sub-list, to be .end()'ed out of |
| // | dojo.query(".foo") |
| // | .third() |
| // | .addClass("thirdFoo") |
| // | .end() |
| // | // access to the orig .foo list |
| // | .removeClass("foo") |
| // | |
| // |
| this._parent = parent; |
| return this; //dojo.NodeList |
| }, |
| |
| end: function(){ |
| // summary: |
| // Ends use of the current `dojo.NodeList` by returning the previous dojo.NodeList |
| // that generated the current dojo.NodeList. |
| // description: |
| // Returns the `dojo.NodeList` that generated the current `dojo.NodeList`. If there |
| // is no parent dojo.NodeList, an empty dojo.NodeList is returned. |
| // example: |
| // | dojo.query("a") |
| // | .filter(".disabled") |
| // | // operate on the anchors that only have a disabled class |
| // | .style("color", "grey") |
| // | .end() |
| // | // jump back to the list of anchors |
| // | .style(...) |
| // |
| if(this._parent){ |
| return this._parent; |
| }else{ |
| //Just return empty list. |
| return new this._NodeListCtor(); |
| } |
| }, |
| |
| // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array#Methods |
| |
| // FIXME: handle return values for #3244 |
| // http://trac.dojotoolkit.org/ticket/3244 |
| |
| // FIXME: |
| // need to wrap or implement: |
| // join (perhaps w/ innerHTML/outerHTML overload for toString() of items?) |
| // reduce |
| // reduceRight |
| |
| /*===== |
| slice: function(begin, end){ |
| // summary: |
| // Returns a new NodeList, maintaining this one in place |
| // description: |
| // This method behaves exactly like the Array.slice method |
| // with the caveat that it returns a dojo.NodeList and not a |
| // raw Array. For more details, see Mozilla's (slice |
| // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:slice] |
| // begin: Integer |
| // Can be a positive or negative integer, with positive |
| // integers noting the offset to begin at, and negative |
| // integers denoting an offset from the end (i.e., to the left |
| // of the end) |
| // end: Integer? |
| // Optional parameter to describe what position relative to |
| // the NodeList's zero index to end the slice at. Like begin, |
| // can be positive or negative. |
| return this._wrap(a.slice.apply(this, arguments)); |
| }, |
| |
| splice: function(index, howmany, item){ |
| // summary: |
| // Returns a new NodeList, manipulating this NodeList based on |
| // the arguments passed, potentially splicing in new elements |
| // at an offset, optionally deleting elements |
| // description: |
| // This method behaves exactly like the Array.splice method |
| // with the caveat that it returns a dojo.NodeList and not a |
| // raw Array. For more details, see Mozilla's (splice |
| // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:splice] |
| // For backwards compatibility, calling .end() on the spliced NodeList |
| // does not return the original NodeList -- splice alters the NodeList in place. |
| // index: Integer |
| // begin can be a positive or negative integer, with positive |
| // integers noting the offset to begin at, and negative |
| // integers denoting an offset from the end (i.e., to the left |
| // of the end) |
| // howmany: Integer? |
| // Optional parameter to describe what position relative to |
| // the NodeList's zero index to end the slice at. Like begin, |
| // can be positive or negative. |
| // item: Object...? |
| // Any number of optional parameters may be passed in to be |
| // spliced into the NodeList |
| // returns: |
| // dojo.NodeList |
| return this._wrap(a.splice.apply(this, arguments)); |
| }, |
| |
| indexOf: function(value, fromIndex){ |
| // summary: |
| // see dojo.indexOf(). The primary difference is that the acted-on |
| // array is implicitly this NodeList |
| // value: Object: |
| // The value to search for. |
| // fromIndex: Integer?: |
| // The location to start searching from. Optional. Defaults to 0. |
| // description: |
| // For more details on the behavior of indexOf, see Mozilla's |
| // (indexOf |
| // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:indexOf] |
| // returns: |
| // Positive Integer or 0 for a match, -1 of not found. |
| return d.indexOf(this, value, fromIndex); // Integer |
| }, |
| |
| lastIndexOf: function(value, fromIndex){ |
| // summary: |
| // see dojo.lastIndexOf(). The primary difference is that the |
| // acted-on array is implicitly this NodeList |
| // description: |
| // For more details on the behavior of lastIndexOf, see |
| // Mozilla's (lastIndexOf |
| // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:lastIndexOf] |
| // value: Object |
| // The value to search for. |
| // fromIndex: Integer? |
| // The location to start searching from. Optional. Defaults to 0. |
| // returns: |
| // Positive Integer or 0 for a match, -1 of not found. |
| return d.lastIndexOf(this, value, fromIndex); // Integer |
| }, |
| |
| every: function(callback, thisObject){ |
| // summary: |
| // see `dojo.every()` and the (Array.every |
| // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:every]. |
| // Takes the same structure of arguments and returns as |
| // dojo.every() with the caveat that the passed array is |
| // implicitly this NodeList |
| // callback: Function: the callback |
| // thisObject: Object?: the context |
| return d.every(this, callback, thisObject); // Boolean |
| }, |
| |
| some: function(callback, thisObject){ |
| // summary: |
| // Takes the same structure of arguments and returns as |
| // `dojo.some()` with the caveat that the passed array is |
| // implicitly this NodeList. See `dojo.some()` and Mozilla's |
| // (Array.some |
| // documentation)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some]. |
| // callback: Function: the callback |
| // thisObject: Object?: the context |
| return d.some(this, callback, thisObject); // Boolean |
| }, |
| =====*/ |
| |
| concat: function(item){ |
| // summary: |
| // Returns a new NodeList comprised of items in this NodeList |
| // as well as items passed in as parameters |
| // description: |
| // This method behaves exactly like the Array.concat method |
| // with the caveat that it returns a `dojo.NodeList` and not a |
| // raw Array. For more details, see the (Array.concat |
| // docs)[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:concat] |
| // item: Object? |
| // Any number of optional parameters may be passed in to be |
| // spliced into the NodeList |
| // returns: |
| // dojo.NodeList |
| |
| //return this._wrap(apc.apply(this, arguments)); |
| // the line above won't work for the native NodeList :-( |
| |
| // implementation notes: |
| // 1) Native NodeList is not an array, and cannot be used directly |
| // in concat() --- the latter doesn't recognize it as an array, and |
| // does not inline it, but append as a single entity. |
| // 2) On some browsers (e.g., Safari) the "constructor" property is |
| // read-only and cannot be changed. So we have to test for both |
| // native NodeList and dojo.NodeList in this property to recognize |
| // the node list. |
| |
| var t = d.isArray(this) ? this : aps.call(this, 0), |
| m = d.map(arguments, function(a){ |
| return a && !d.isArray(a) && |
| (typeof NodeList != "undefined" && a.constructor === NodeList || a.constructor === this._NodeListCtor) ? |
| aps.call(a, 0) : a; |
| }); |
| return this._wrap(apc.apply(t, m), this); // dojo.NodeList |
| }, |
| |
| map: function(/*Function*/ func, /*Function?*/ obj){ |
| // summary: |
| // see dojo.map(). The primary difference is that the acted-on |
| // array is implicitly this NodeList and the return is a |
| // dojo.NodeList (a subclass of Array) |
| ///return d.map(this, func, obj, d.NodeList); // dojo.NodeList |
| return this._wrap(d.map(this, func, obj), this); // dojo.NodeList |
| }, |
| |
| forEach: function(callback, thisObj){ |
| // summary: |
| // see `dojo.forEach()`. The primary difference is that the acted-on |
| // array is implicitly this NodeList. If you want the option to break out |
| // of the forEach loop, use every() or some() instead. |
| d.forEach(this, callback, thisObj); |
| // non-standard return to allow easier chaining |
| return this; // dojo.NodeList |
| }, |
| |
| /*===== |
| coords: function(){ |
| // summary: |
| // Returns the box objects of all elements in a node list as |
| // an Array (*not* a NodeList). Acts like `dojo.coords`, though assumes |
| // the node passed is each node in this list. |
| |
| return d.map(this, d.coords); // Array |
| }, |
| |
| position: function(){ |
| // summary: |
| // Returns border-box objects (x/y/w/h) of all elements in a node list |
| // as an Array (*not* a NodeList). Acts like `dojo.position`, though |
| // assumes the node passed is each node in this list. |
| |
| return d.map(this, d.position); // Array |
| }, |
| |
| attr: function(property, value){ |
| // summary: |
| // gets or sets the DOM attribute for every element in the |
| // NodeList. See also `dojo.attr` |
| // property: String |
| // the attribute to get/set |
| // value: String? |
| // optional. The value to set the property to |
| // returns: |
| // if no value is passed, the result is an array of attribute values |
| // If a value is passed, the return is this NodeList |
| // example: |
| // Make all nodes with a particular class focusable: |
| // | dojo.query(".focusable").attr("tabIndex", -1); |
| // example: |
| // Disable a group of buttons: |
| // | dojo.query("button.group").attr("disabled", true); |
| // example: |
| // innerHTML can be assigned or retrieved as well: |
| // | // get the innerHTML (as an array) for each list item |
| // | var ih = dojo.query("li.replaceable").attr("innerHTML"); |
| return; // dojo.NodeList |
| return; // Array |
| }, |
| |
| style: function(property, value){ |
| // summary: |
| // gets or sets the CSS property for every element in the NodeList |
| // property: String |
| // the CSS property to get/set, in JavaScript notation |
| // ("lineHieght" instead of "line-height") |
| // value: String? |
| // optional. The value to set the property to |
| // returns: |
| // if no value is passed, the result is an array of strings. |
| // If a value is passed, the return is this NodeList |
| return; // dojo.NodeList |
| return; // Array |
| }, |
| |
| addClass: function(className){ |
| // summary: |
| // adds the specified class to every node in the list |
| // className: String|Array |
| // A String class name to add, or several space-separated class names, |
| // or an array of class names. |
| return; // dojo.NodeList |
| }, |
| |
| removeClass: function(className){ |
| // summary: |
| // removes the specified class from every node in the list |
| // className: String|Array? |
| // An optional String class name to remove, or several space-separated |
| // class names, or an array of class names. If omitted, all class names |
| // will be deleted. |
| // returns: |
| // dojo.NodeList, this list |
| return; // dojo.NodeList |
| }, |
| |
| toggleClass: function(className, condition){ |
| // summary: |
| // Adds a class to node if not present, or removes if present. |
| // Pass a boolean condition if you want to explicitly add or remove. |
| // condition: Boolean? |
| // If passed, true means to add the class, false means to remove. |
| // className: String |
| // the CSS class to add |
| return; // dojo.NodeList |
| }, |
| |
| connect: function(methodName, objOrFunc, funcName){ |
| // summary: |
| // attach event handlers to every item of the NodeList. Uses dojo.connect() |
| // so event properties are normalized |
| // methodName: String |
| // the name of the method to attach to. For DOM events, this should be |
| // the lower-case name of the event |
| // objOrFunc: Object|Function|String |
| // if 2 arguments are passed (methodName, objOrFunc), objOrFunc should |
| // reference a function or be the name of the function in the global |
| // namespace to attach. If 3 arguments are provided |
| // (methodName, objOrFunc, funcName), objOrFunc must be the scope to |
| // locate the bound function in |
| // funcName: String? |
| // optional. A string naming the function in objOrFunc to bind to the |
| // event. May also be a function reference. |
| // example: |
| // add an onclick handler to every button on the page |
| // | dojo.query("div:nth-child(odd)").connect("onclick", function(e){ |
| // | console.log("clicked!"); |
| // | }); |
| // example: |
| // attach foo.bar() to every odd div's onmouseover |
| // | dojo.query("div:nth-child(odd)").connect("onmouseover", foo, "bar"); |
| }, |
| |
| empty: function(){ |
| // summary: |
| // clears all content from each node in the list. Effectively |
| // equivalent to removing all child nodes from every item in |
| // the list. |
| return this.forEach("item.innerHTML='';"); // dojo.NodeList |
| // FIXME: should we be checking for and/or disposing of widgets below these nodes? |
| }, |
| =====*/ |
| |
| // useful html methods |
| coords: adaptAsMap(d.coords), |
| position: adaptAsMap(d.position), |
| |
| // FIXME: connectPublisher()? connectRunOnce()? |
| |
| /* |
| destroy: function(){ |
| // summary: |
| // destroys every item in the list. |
| this.forEach(d.destroy); |
| // FIXME: should we be checking for and/or disposing of widgets below these nodes? |
| }, |
| */ |
| |
| place: function(/*String||Node*/ queryOrNode, /*String*/ position){ |
| // summary: |
| // places elements of this node list relative to the first element matched |
| // by queryOrNode. Returns the original NodeList. See: `dojo.place` |
| // queryOrNode: |
| // may be a string representing any valid CSS3 selector or a DOM node. |
| // In the selector case, only the first matching element will be used |
| // for relative positioning. |
| // position: |
| // can be one of: |
| // | "last" (default) |
| // | "first" |
| // | "before" |
| // | "after" |
| // | "only" |
| // | "replace" |
| // or an offset in the childNodes property |
| var item = d.query(queryOrNode)[0]; |
| return this.forEach(function(node){ d.place(node, item, position); }); // dojo.NodeList |
| }, |
| |
| orphan: function(/*String?*/ filter){ |
| // summary: |
| // removes elements in this list that match the filter |
| // from their parents and returns them as a new NodeList. |
| // filter: |
| // CSS selector like ".foo" or "div > span" |
| // returns: |
| // `dojo.NodeList` containing the orphaned elements |
| return (filter ? d._filterQueryResult(this, filter) : this).forEach(orphan); // dojo.NodeList |
| }, |
| |
| adopt: function(/*String||Array||DomNode*/ queryOrListOrNode, /*String?*/ position){ |
| // summary: |
| // places any/all elements in queryOrListOrNode at a |
| // position relative to the first element in this list. |
| // Returns a dojo.NodeList of the adopted elements. |
| // queryOrListOrNode: |
| // a DOM node or a query string or a query result. |
| // Represents the nodes to be adopted relative to the |
| // first element of this NodeList. |
| // position: |
| // can be one of: |
| // | "last" (default) |
| // | "first" |
| // | "before" |
| // | "after" |
| // | "only" |
| // | "replace" |
| // or an offset in the childNodes property |
| return d.query(queryOrListOrNode).place(this[0], position)._stash(this); // dojo.NodeList |
| }, |
| |
| // FIXME: do we need this? |
| query: function(/*String*/ queryStr){ |
| // summary: |
| // Returns a new list whose members match the passed query, |
| // assuming elements of the current NodeList as the root for |
| // each search. |
| // example: |
| // assume a DOM created by this markup: |
| // | <div id="foo"> |
| // | <p> |
| // | bacon is tasty, <span>dontcha think?</span> |
| // | </p> |
| // | </div> |
| // | <div id="bar"> |
| // | <p>great comedians may not be funny <span>in person</span></p> |
| // | </div> |
| // If we are presented with the following definition for a NodeList: |
| // | var l = new dojo.NodeList(dojo.byId("foo"), dojo.byId("bar")); |
| // it's possible to find all span elements under paragraphs |
| // contained by these elements with this sub-query: |
| // | var spans = l.query("p span"); |
| |
| // FIXME: probably slow |
| if(!queryStr){ return this; } |
| var ret = this.map(function(node){ |
| // FIXME: why would we ever get undefined here? |
| return d.query(queryStr, node).filter(function(subNode){ return subNode !== undefined; }); |
| }); |
| return this._wrap(apc.apply([], ret), this); // dojo.NodeList |
| }, |
| |
| filter: function(/*String|Function*/ filter){ |
| // summary: |
| // "masks" the built-in javascript filter() method (supported |
| // in Dojo via `dojo.filter`) to support passing a simple |
| // string filter in addition to supporting filtering function |
| // objects. |
| // filter: |
| // If a string, a CSS rule like ".thinger" or "div > span". |
| // example: |
| // "regular" JS filter syntax as exposed in dojo.filter: |
| // | dojo.query("*").filter(function(item){ |
| // | // highlight every paragraph |
| // | return (item.nodeName == "p"); |
| // | }).style("backgroundColor", "yellow"); |
| // example: |
| // the same filtering using a CSS selector |
| // | dojo.query("*").filter("p").styles("backgroundColor", "yellow"); |
| |
| var a = arguments, items = this, start = 0; |
| if(typeof filter == "string"){ // inline'd type check |
| items = d._filterQueryResult(this, a[0]); |
| if(a.length == 1){ |
| // if we only got a string query, pass back the filtered results |
| return items._stash(this); // dojo.NodeList |
| } |
| // if we got a callback, run it over the filtered items |
| start = 1; |
| } |
| return this._wrap(d.filter(items, a[start], a[start + 1]), this); // dojo.NodeList |
| }, |
| |
| /* |
| // FIXME: should this be "copyTo" and include parenting info? |
| clone: function(){ |
| // summary: |
| // creates node clones of each element of this list |
| // and returns a new list containing the clones |
| }, |
| */ |
| |
| addContent: function(/*String||DomNode||Object||dojo.NodeList*/ content, /*String||Integer?*/ position){ |
| // summary: |
| // add a node, NodeList or some HTML as a string to every item in the |
| // list. Returns the original list. |
| // description: |
| // a copy of the HTML content is added to each item in the |
| // list, with an optional position argument. If no position |
| // argument is provided, the content is appended to the end of |
| // each item. |
| // content: |
| // DOM node, HTML in string format, a NodeList or an Object. If a DOM node or |
| // NodeList, the content will be cloned if the current NodeList has more than one |
| // element. Only the DOM nodes are cloned, no event handlers. If it is an Object, |
| // it should be an object with at "template" String property that has the HTML string |
| // to insert. If dojo.string has already been dojo.required, then dojo.string.substitute |
| // will be used on the "template" to generate the final HTML string. Other allowed |
| // properties on the object are: "parse" if the HTML |
| // string should be parsed for widgets (dojo.require("dojo.parser") to get that |
| // option to work), and "templateFunc" if a template function besides dojo.string.substitute |
| // should be used to transform the "template". |
| // position: |
| // can be one of: |
| // | "last"||"end" (default) |
| // | "first||"start" |
| // | "before" |
| // | "after" |
| // | "replace" (replaces nodes in this NodeList with new content) |
| // | "only" (removes other children of the nodes so new content is the only child) |
| // or an offset in the childNodes property |
| // example: |
| // appends content to the end if the position is omitted |
| // | dojo.query("h3 > p").addContent("hey there!"); |
| // example: |
| // add something to the front of each element that has a |
| // "thinger" property: |
| // | dojo.query("[thinger]").addContent("...", "first"); |
| // example: |
| // adds a header before each element of the list |
| // | dojo.query(".note").addContent("<h4>NOTE:</h4>", "before"); |
| // example: |
| // add a clone of a DOM node to the end of every element in |
| // the list, removing it from its existing parent. |
| // | dojo.query(".note").addContent(dojo.byId("foo")); |
| // example: |
| // Append nodes from a templatized string. |
| // dojo.require("dojo.string"); |
| // dojo.query(".note").addContent({ |
| // template: '<b>${id}: </b><span>${name}</span>', |
| // id: "user332", |
| // name: "Mr. Anderson" |
| // }); |
| // example: |
| // Append nodes from a templatized string that also has widgets parsed. |
| // dojo.require("dojo.string"); |
| // dojo.require("dojo.parser"); |
| // var notes = dojo.query(".note").addContent({ |
| // template: '<button dojoType="dijit.form.Button">${text}</button>', |
| // parse: true, |
| // text: "Send" |
| // }); |
| content = this._normalize(content, this[0]); |
| for(var i = 0, node; (node = this[i]); i++){ |
| this._place(content, node, position, i > 0); |
| } |
| return this; //dojo.NodeList |
| }, |
| |
| instantiate: function(/*String|Object*/ declaredClass, /*Object?*/ properties){ |
| // summary: |
| // Create a new instance of a specified class, using the |
| // specified properties and each node in the nodeList as a |
| // srcNodeRef. |
| // example: |
| // Grabs all buttons in the page and converts them to diji.form.Buttons. |
| // | var buttons = dojo.query("button").instantiate("dijit.form.Button", {showLabel: true}); |
| var c = d.isFunction(declaredClass) ? declaredClass : d.getObject(declaredClass); |
| properties = properties || {}; |
| return this.forEach(function(node){ |
| new c(properties, node); |
| }); // dojo.NodeList |
| }, |
| |
| at: function(/*===== index =====*/){ |
| // summary: |
| // Returns a new NodeList comprised of items in this NodeList |
| // at the given index or indices. |
| // |
| // index: Integer... |
| // One or more 0-based indices of items in the current |
| // NodeList. A negative index will start at the end of the |
| // list and go backwards. |
| // |
| // example: |
| // Shorten the list to the first, second, and third elements |
| // | dojo.query("a").at(0, 1, 2).forEach(fn); |
| // |
| // example: |
| // Retrieve the first and last elements of a unordered list: |
| // | dojo.query("ul > li").at(0, -1).forEach(cb); |
| // |
| // example: |
| // Do something for the first element only, but end() out back to |
| // the original list and continue chaining: |
| // | dojo.query("a").at(0).onclick(fn).end().forEach(function(n){ |
| // | console.log(n); // all anchors on the page. |
| // | }) |
| // |
| // returns: |
| // dojo.NodeList |
| var t = new this._NodeListCtor(); |
| d.forEach(arguments, function(i){ |
| if(i < 0){ i = this.length + i } |
| if(this[i]){ t.push(this[i]); } |
| }, this); |
| return t._stash(this); // dojo.NodeList |
| } |
| |
| }); |
| |
| nl.events = [ |
| // summary: |
| // list of all DOM events used in NodeList |
| "blur", "focus", "change", "click", "error", "keydown", "keypress", |
| "keyup", "load", "mousedown", "mouseenter", "mouseleave", "mousemove", |
| "mouseout", "mouseover", "mouseup", "submit" |
| ]; |
| |
| // FIXME: pseudo-doc the above automatically generated on-event functions |
| |
| // syntactic sugar for DOM events |
| d.forEach(nl.events, function(evt){ |
| var _oe = "on" + evt; |
| nlp[_oe] = function(a, b){ |
| return this.connect(_oe, a, b); |
| }; |
| // FIXME: should these events trigger publishes? |
| /* |
| return (a ? this.connect(_oe, a, b) : |
| this.forEach(function(n){ |
| // FIXME: |
| // listeners get buried by |
| // addEventListener and can't be dug back |
| // out to be triggered externally. |
| // see: |
| // http://developer.mozilla.org/en/docs/DOM:element |
| |
| console.log(n, evt, _oe); |
| |
| // FIXME: need synthetic event support! |
| var _e = { target: n, faux: true, type: evt }; |
| // dojo._event_listener._synthesizeEvent({}, { target: n, faux: true, type: evt }); |
| try{ n[evt](_e); }catch(e){ console.log(e); } |
| try{ n[_oe](_e); }catch(e){ console.log(e); } |
| }) |
| ); |
| */ |
| } |
| ); |
| |
| })(); |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.query"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.query"] = true; |
| (function(){ |
| |
| /* |
| dojo.query() architectural overview: |
| |
| dojo.query is a relatively full-featured CSS3 query library. It is |
| designed to take any valid CSS3 selector and return the nodes matching |
| the selector. To do this quickly, it processes queries in several |
| steps, applying caching where profitable. |
| |
| The steps (roughly in reverse order of the way they appear in the code): |
| 1.) check to see if we already have a "query dispatcher" |
| - if so, use that with the given parameterization. Skip to step 4. |
| 2.) attempt to determine which branch to dispatch the query to: |
| - JS (optimized DOM iteration) |
| - native (FF3.1+, Safari 3.1+, IE 8+) |
| 3.) tokenize and convert to executable "query dispatcher" |
| - this is where the lion's share of the complexity in the |
| system lies. In the DOM version, the query dispatcher is |
| assembled as a chain of "yes/no" test functions pertaining to |
| a section of a simple query statement (".blah:nth-child(odd)" |
| but not "div div", which is 2 simple statements). Individual |
| statement dispatchers are cached (to prevent re-definition) |
| as are entire dispatch chains (to make re-execution of the |
| same query fast) |
| 4.) the resulting query dispatcher is called in the passed scope |
| (by default the top-level document) |
| - for DOM queries, this results in a recursive, top-down |
| evaluation of nodes based on each simple query section |
| - for native implementations, this may mean working around spec |
| bugs. So be it. |
| 5.) matched nodes are pruned to ensure they are unique (if necessary) |
| */ |
| |
| var defineQuery= function(d){ |
| // define everything in a closure for compressability reasons. "d" is an |
| // alias to "dojo" (or the toolkit alias object, e.g., "acme"). |
| |
| //////////////////////////////////////////////////////////////////////// |
| // Toolkit aliases |
| //////////////////////////////////////////////////////////////////////// |
| |
| // if you are extracting dojo.query for use in your own system, you will |
| // need to provide these methods and properties. No other porting should be |
| // necessary, save for configuring the system to use a class other than |
| // dojo.NodeList as the return instance instantiator |
| var trim = d.trim; |
| var each = d.forEach; |
| // d.isIE; // float |
| // d.isSafari; // float |
| // d.isOpera; // float |
| // d.isWebKit; // float |
| // d.doc ; // document element |
| var qlc = (d._NodeListCtor = d.NodeList); |
| |
| var getDoc = function(){ return d.doc; }; |
| // NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo |
| var cssCaseBug = ((d.isWebKit||d.isMozilla) && ((getDoc().compatMode) == "BackCompat")); |
| |
| //////////////////////////////////////////////////////////////////////// |
| // Global utilities |
| //////////////////////////////////////////////////////////////////////// |
| |
| |
| // on browsers that support the "children" collection we can avoid a lot of |
| // iteration on chaff (non-element) nodes. |
| // why. |
| var childNodesName = !!getDoc().firstChild["children"] ? "children" : "childNodes"; |
| |
| var specials = ">~+"; |
| |
| // global thunk to determine whether we should treat the current query as |
| // case sensitive or not. This switch is flipped by the query evaluator |
| // based on the document passed as the context to search. |
| var caseSensitive = false; |
| |
| // how high? |
| var yesman = function(){ return true; }; |
| |
| //////////////////////////////////////////////////////////////////////// |
| // Tokenizer |
| //////////////////////////////////////////////////////////////////////// |
| |
| var getQueryParts = function(query){ |
| // summary: |
| // state machine for query tokenization |
| // description: |
| // instead of using a brittle and slow regex-based CSS parser, |
| // dojo.query implements an AST-style query representation. This |
| // representation is only generated once per query. For example, |
| // the same query run multiple times or under different root nodes |
| // does not re-parse the selector expression but instead uses the |
| // cached data structure. The state machine implemented here |
| // terminates on the last " " (space) character and returns an |
| // ordered array of query component structures (or "parts"). Each |
| // part represents an operator or a simple CSS filtering |
| // expression. The structure for parts is documented in the code |
| // below. |
| |
| |
| // NOTE: |
| // this code is designed to run fast and compress well. Sacrifices |
| // to readability and maintainability have been made. Your best |
| // bet when hacking the tokenizer is to put The Donnas on *really* |
| // loud (may we recommend their "Spend The Night" release?) and |
| // just assume you're gonna make mistakes. Keep the unit tests |
| // open and run them frequently. Knowing is half the battle ;-) |
| if(specials.indexOf(query.slice(-1)) >= 0){ |
| // if we end with a ">", "+", or "~", that means we're implicitly |
| // searching all children, so make it explicit |
| query += " * " |
| }else{ |
| // if you have not provided a terminator, one will be provided for |
| // you... |
| query += " "; |
| } |
| |
| var ts = function(/*Integer*/ s, /*Integer*/ e){ |
| // trim and slice. |
| |
| // take an index to start a string slice from and an end position |
| // and return a trimmed copy of that sub-string |
| return trim(query.slice(s, e)); |
| } |
| |
| // the overall data graph of the full query, as represented by queryPart objects |
| var queryParts = []; |
| |
| |
| // state keeping vars |
| var inBrackets = -1, inParens = -1, inMatchFor = -1, |
| inPseudo = -1, inClass = -1, inId = -1, inTag = -1, |
| lc = "", cc = "", pStart; |
| |
| // iteration vars |
| var x = 0, // index in the query |
| ql = query.length, |
| currentPart = null, // data structure representing the entire clause |
| _cp = null; // the current pseudo or attr matcher |
| |
| // several temporary variables are assigned to this structure during a |
| // potential sub-expression match: |
| // attr: |
| // a string representing the current full attribute match in a |
| // bracket expression |
| // type: |
| // if there's an operator in a bracket expression, this is |
| // used to keep track of it |
| // value: |
| // the internals of parenthetical expression for a pseudo. for |
| // :nth-child(2n+1), value might be "2n+1" |
| |
| var endTag = function(){ |
| // called when the tokenizer hits the end of a particular tag name. |
| // Re-sets state variables for tag matching and sets up the matcher |
| // to handle the next type of token (tag or operator). |
| if(inTag >= 0){ |
| var tv = (inTag == x) ? null : ts(inTag, x); // .toLowerCase(); |
| currentPart[ (specials.indexOf(tv) < 0) ? "tag" : "oper" ] = tv; |
| inTag = -1; |
| } |
| } |
| |
| var endId = function(){ |
| // called when the tokenizer might be at the end of an ID portion of a match |
| if(inId >= 0){ |
| currentPart.id = ts(inId, x).replace(/\\/g, ""); |
| inId = -1; |
| } |
| } |
| |
| var endClass = function(){ |
| // called when the tokenizer might be at the end of a class name |
| // match. CSS allows for multiple classes, so we augment the |
| // current item with another class in its list |
| if(inClass >= 0){ |
| currentPart.classes.push(ts(inClass+1, x).replace(/\\/g, "")); |
| inClass = -1; |
| } |
| } |
| |
| var endAll = function(){ |
| // at the end of a simple fragment, so wall off the matches |
| endId(); endTag(); endClass(); |
| } |
| |
| var endPart = function(){ |
| endAll(); |
| if(inPseudo >= 0){ |
| currentPart.pseudos.push({ name: ts(inPseudo+1, x) }); |
| } |
| // hint to the selector engine to tell it whether or not it |
| // needs to do any iteration. Many simple selectors don't, and |
| // we can avoid significant construction-time work by advising |
| // the system to skip them |
| currentPart.loops = ( |
| currentPart.pseudos.length || |
| currentPart.attrs.length || |
| currentPart.classes.length ); |
| |
| currentPart.oquery = currentPart.query = ts(pStart, x); // save the full expression as a string |
| |
| |
| // otag/tag are hints to suggest to the system whether or not |
| // it's an operator or a tag. We save a copy of otag since the |
| // tag name is cast to upper-case in regular HTML matches. The |
| // system has a global switch to figure out if the current |
| // expression needs to be case sensitive or not and it will use |
| // otag or tag accordingly |
| currentPart.otag = currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*"); |
| |
| if(currentPart.tag){ |
| // if we're in a case-insensitive HTML doc, we likely want |
| // the toUpperCase when matching on element.tagName. If we |
| // do it here, we can skip the string op per node |
| // comparison |
| currentPart.tag = currentPart.tag.toUpperCase(); |
| } |
| |
| // add the part to the list |
| if(queryParts.length && (queryParts[queryParts.length-1].oper)){ |
| // operators are always infix, so we remove them from the |
| // list and attach them to the next match. The evaluator is |
| // responsible for sorting out how to handle them. |
| currentPart.infixOper = queryParts.pop(); |
| currentPart.query = currentPart.infixOper.query + " " + currentPart.query; |
| /* |
| console.debug( "swapping out the infix", |
| currentPart.infixOper, |
| "and attaching it to", |
| currentPart); |
| */ |
| } |
| queryParts.push(currentPart); |
| |
| currentPart = null; |
| } |
| |
| // iterate over the query, character by character, building up a |
| // list of query part objects |
| for(; lc=cc, cc=query.charAt(x), x < ql; x++){ |
| // cc: the current character in the match |
| // lc: the last character (if any) |
| |
| // someone is trying to escape something, so don't try to match any |
| // fragments. We assume we're inside a literal. |
| if(lc == "\\"){ continue; } |
| if(!currentPart){ // a part was just ended or none has yet been created |
| // NOTE: I hate all this alloc, but it's shorter than writing tons of if's |
| pStart = x; |
| // rules describe full CSS sub-expressions, like: |
| // #someId |
| // .className:first-child |
| // but not: |
| // thinger > div.howdy[type=thinger] |
| // the indidual components of the previous query would be |
| // split into 3 parts that would be represented a structure |
| // like: |
| // [ |
| // { |
| // query: "thinger", |
| // tag: "thinger", |
| // }, |
| // { |
| // query: "div.howdy[type=thinger]", |
| // classes: ["howdy"], |
| // infixOper: { |
| // query: ">", |
| // oper: ">", |
| // } |
| // }, |
| // ] |
| currentPart = { |
| query: null, // the full text of the part's rule |
| pseudos: [], // CSS supports multiple pseud-class matches in a single rule |
| attrs: [], // CSS supports multi-attribute match, so we need an array |
| classes: [], // class matches may be additive, e.g.: .thinger.blah.howdy |
| tag: null, // only one tag... |
| oper: null, // ...or operator per component. Note that these wind up being exclusive. |
| id: null, // the id component of a rule |
| getTag: function(){ |
| return (caseSensitive) ? this.otag : this.tag; |
| } |
| }; |
| |
| // if we don't have a part, we assume we're going to start at |
| // the beginning of a match, which should be a tag name. This |
| // might fault a little later on, but we detect that and this |
| // iteration will still be fine. |
| inTag = x; |
| } |
| |
| if(inBrackets >= 0){ |
| // look for a the close first |
| if(cc == "]"){ // if we're in a [...] clause and we end, do assignment |
| if(!_cp.attr){ |
| // no attribute match was previously begun, so we |
| // assume this is an attribute existence match in the |
| // form of [someAttributeName] |
| _cp.attr = ts(inBrackets+1, x); |
| }else{ |
| // we had an attribute already, so we know that we're |
| // matching some sort of value, as in [attrName=howdy] |
| _cp.matchFor = ts((inMatchFor||inBrackets+1), x); |
| } |
| var cmf = _cp.matchFor; |
| if(cmf){ |
| // try to strip quotes from the matchFor value. We want |
| // [attrName=howdy] to match the same |
| // as [attrName = 'howdy' ] |
| if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){ |
| _cp.matchFor = cmf.slice(1, -1); |
| } |
| } |
| // end the attribute by adding it to the list of attributes. |
| currentPart.attrs.push(_cp); |
| _cp = null; // necessary? |
| inBrackets = inMatchFor = -1; |
| }else if(cc == "="){ |
| // if the last char was an operator prefix, make sure we |
| // record it along with the "=" operator. |
| var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : ""; |
| _cp.type = addToCc+cc; |
| _cp.attr = ts(inBrackets+1, x-addToCc.length); |
| inMatchFor = x+1; |
| } |
| // now look for other clause parts |
| }else if(inParens >= 0){ |
| // if we're in a parenthetical expression, we need to figure |
| // out if it's attached to a pseudo-selector rule like |
| // :nth-child(1) |
| if(cc == ")"){ |
| if(inPseudo >= 0){ |
| _cp.value = ts(inParens+1, x); |
| } |
| inPseudo = inParens = -1; |
| } |
| }else if(cc == "#"){ |
| // start of an ID match |
| endAll(); |
| inId = x+1; |
| }else if(cc == "."){ |
| // start of a class match |
| endAll(); |
| inClass = x; |
| }else if(cc == ":"){ |
| // start of a pseudo-selector match |
| endAll(); |
| inPseudo = x; |
| }else if(cc == "["){ |
| // start of an attribute match. |
| endAll(); |
| inBrackets = x; |
| // provide a new structure for the attribute match to fill-in |
| _cp = { |
| /*===== |
| attr: null, type: null, matchFor: null |
| =====*/ |
| }; |
| }else if(cc == "("){ |
| // we really only care if we've entered a parenthetical |
| // expression if we're already inside a pseudo-selector match |
| if(inPseudo >= 0){ |
| // provide a new structure for the pseudo match to fill-in |
| _cp = { |
| name: ts(inPseudo+1, x), |
| value: null |
| } |
| currentPart.pseudos.push(_cp); |
| } |
| inParens = x; |
| }else if( |
| (cc == " ") && |
| // if it's a space char and the last char is too, consume the |
| // current one without doing more work |
| (lc != cc) |
| ){ |
| endPart(); |
| } |
| } |
| return queryParts; |
| }; |
| |
| |
| //////////////////////////////////////////////////////////////////////// |
| // DOM query infrastructure |
| //////////////////////////////////////////////////////////////////////// |
| |
| var agree = function(first, second){ |
| // the basic building block of the yes/no chaining system. agree(f1, |
| // f2) generates a new function which returns the boolean results of |
| // both of the passed functions to a single logical-anded result. If |
| // either are not passed, the other is used exclusively. |
| if(!first){ return second; } |
| if(!second){ return first; } |
| |
| return function(){ |
| return first.apply(window, arguments) && second.apply(window, arguments); |
| } |
| }; |
| |
| var getArr = function(i, arr){ |
| // helps us avoid array alloc when we don't need it |
| var r = arr||[]; // FIXME: should this be 'new d._NodeListCtor()' ? |
| if(i){ r.push(i); } |
| return r; |
| }; |
| |
| var _isElement = function(n){ return (1 == n.nodeType); }; |
| |
| // FIXME: need to coalesce _getAttr with defaultGetter |
| var blank = ""; |
| var _getAttr = function(elem, attr){ |
| if(!elem){ return blank; } |
| if(attr == "class"){ |
| return elem.className || blank; |
| } |
| if(attr == "for"){ |
| return elem.htmlFor || blank; |
| } |
| if(attr == "style"){ |
| return elem.style.cssText || blank; |
| } |
| return (caseSensitive ? elem.getAttribute(attr) : elem.getAttribute(attr, 2)) || blank; |
| }; |
| |
| var attrs = { |
| "*=": function(attr, value){ |
| return function(elem){ |
| // E[foo*="bar"] |
| // an E element whose "foo" attribute value contains |
| // the substring "bar" |
| return (_getAttr(elem, attr).indexOf(value)>=0); |
| } |
| }, |
| "^=": function(attr, value){ |
| // E[foo^="bar"] |
| // an E element whose "foo" attribute value begins exactly |
| // with the string "bar" |
| return function(elem){ |
| return (_getAttr(elem, attr).indexOf(value)==0); |
| } |
| }, |
| "$=": function(attr, value){ |
| // E[foo$="bar"] |
| // an E element whose "foo" attribute value ends exactly |
| // with the string "bar" |
| var tval = " "+value; |
| return function(elem){ |
| var ea = " "+_getAttr(elem, attr); |
| return (ea.lastIndexOf(value)==(ea.length-value.length)); |
| } |
| }, |
| "~=": function(attr, value){ |
| // E[foo~="bar"] |
| // an E element whose "foo" attribute value is a list of |
| // space-separated values, one of which is exactly equal |
| // to "bar" |
| |
| // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]"; |
| var tval = " "+value+" "; |
| return function(elem){ |
| var ea = " "+_getAttr(elem, attr)+" "; |
| return (ea.indexOf(tval)>=0); |
| } |
| }, |
| "|=": function(attr, value){ |
| // E[hreflang|="en"] |
| // an E element whose "hreflang" attribute has a |
| // hyphen-separated list of values beginning (from the |
| // left) with "en" |
| var valueDash = " "+value+"-"; |
| return function(elem){ |
| var ea = " "+_getAttr(elem, attr); |
| return ( |
| (ea == value) || |
| (ea.indexOf(valueDash)==0) |
| ); |
| } |
| }, |
| "=": function(attr, value){ |
| return function(elem){ |
| return (_getAttr(elem, attr) == value); |
| } |
| } |
| }; |
| |
| // avoid testing for node type if we can. Defining this in the negative |
| // here to avoid negation in the fast path. |
| var _noNES = (typeof getDoc().firstChild.nextElementSibling == "undefined"); |
| var _ns = !_noNES ? "nextElementSibling" : "nextSibling"; |
| var _ps = !_noNES ? "previousElementSibling" : "previousSibling"; |
| var _simpleNodeTest = (_noNES ? _isElement : yesman); |
| |
| var _lookLeft = function(node){ |
| // look left |
| while(node = node[_ps]){ |
| if(_simpleNodeTest(node)){ return false; } |
| } |
| return true; |
| }; |
| |
| var _lookRight = function(node){ |
| // look right |
| while(node = node[_ns]){ |
| if(_simpleNodeTest(node)){ return false; } |
| } |
| return true; |
| }; |
| |
| var getNodeIndex = function(node){ |
| var root = node.parentNode; |
| var i = 0, |
| tret = root[childNodesName], |
| ci = (node["_i"]||-1), |
| cl = (root["_l"]||-1); |
| |
| if(!tret){ return -1; } |
| var l = tret.length; |
| |
| // we calculate the parent length as a cheap way to invalidate the |
| // cache. It's not 100% accurate, but it's much more honest than what |
| // other libraries do |
| if( cl == l && ci >= 0 && cl >= 0 ){ |
| // if it's legit, tag and release |
| return ci; |
| } |
| |
| // else re-key things |
| root["_l"] = l; |
| ci = -1; |
| for(var te = root["firstElementChild"]||root["firstChild"]; te; te = te[_ns]){ |
| if(_simpleNodeTest(te)){ |
| te["_i"] = ++i; |
| if(node === te){ |
| // NOTE: |
| // shortcutting the return at this step in indexing works |
| // very well for benchmarking but we avoid it here since |
| // it leads to potential O(n^2) behavior in sequential |
| // getNodexIndex operations on a previously un-indexed |
| // parent. We may revisit this at a later time, but for |
| // now we just want to get the right answer more often |
| // than not. |
| ci = i; |
| } |
| } |
| } |
| return ci; |
| }; |
| |
| var isEven = function(elem){ |
| return !((getNodeIndex(elem)) % 2); |
| }; |
| |
| var isOdd = function(elem){ |
| return ((getNodeIndex(elem)) % 2); |
| }; |
| |
| var pseudos = { |
| "checked": function(name, condition){ |
| return function(elem){ |
| return !!("checked" in elem ? elem.checked : elem.selected); |
| } |
| }, |
| "first-child": function(){ return _lookLeft; }, |
| "last-child": function(){ return _lookRight; }, |
| "only-child": function(name, condition){ |
| return function(node){ |
| if(!_lookLeft(node)){ return false; } |
| if(!_lookRight(node)){ return false; } |
| return true; |
| }; |
| }, |
| "empty": function(name, condition){ |
| return function(elem){ |
| // DomQuery and jQuery get this wrong, oddly enough. |
| // The CSS 3 selectors spec is pretty explicit about it, too. |
| var cn = elem.childNodes; |
| var cnl = elem.childNodes.length; |
| // if(!cnl){ return true; } |
| for(var x=cnl-1; x >= 0; x--){ |
| var nt = cn[x].nodeType; |
| if((nt === 1)||(nt == 3)){ return false; } |
| } |
| return true; |
| } |
| }, |
| "contains": function(name, condition){ |
| var cz = condition.charAt(0); |
| if( cz == '"' || cz == "'" ){ //remove quote |
| condition = condition.slice(1, -1); |
| } |
| return function(elem){ |
| return (elem.innerHTML.indexOf(condition) >= 0); |
| } |
| }, |
| "not": function(name, condition){ |
| var p = getQueryParts(condition)[0]; |
| var ignores = { el: 1 }; |
| if(p.tag != "*"){ |
| ignores.tag = 1; |
| } |
| if(!p.classes.length){ |
| ignores.classes = 1; |
| } |
| var ntf = getSimpleFilterFunc(p, ignores); |
| return function(elem){ |
| return (!ntf(elem)); |
| } |
| }, |
| "nth-child": function(name, condition){ |
| var pi = parseInt; |
| // avoid re-defining function objects if we can |
| if(condition == "odd"){ |
| return isOdd; |
| }else if(condition == "even"){ |
| return isEven; |
| } |
| // FIXME: can we shorten this? |
| if(condition.indexOf("n") != -1){ |
| var tparts = condition.split("n", 2); |
| var pred = tparts[0] ? ((tparts[0] == '-') ? -1 : pi(tparts[0])) : 1; |
| var idx = tparts[1] ? pi(tparts[1]) : 0; |
| var lb = 0, ub = -1; |
| if(pred > 0){ |
| if(idx < 0){ |
| idx = (idx % pred) && (pred + (idx % pred)); |
| }else if(idx>0){ |
| if(idx >= pred){ |
| lb = idx - idx % pred; |
| } |
| idx = idx % pred; |
| } |
| }else if(pred<0){ |
| pred *= -1; |
| // idx has to be greater than 0 when pred is negative; |
| // shall we throw an error here? |
| if(idx > 0){ |
| ub = idx; |
| idx = idx % pred; |
| } |
| } |
| if(pred > 0){ |
| return function(elem){ |
| var i = getNodeIndex(elem); |
| return (i>=lb) && (ub<0 || i<=ub) && ((i % pred) == idx); |
| } |
| }else{ |
| condition = idx; |
| } |
| } |
| var ncount = pi(condition); |
| return function(elem){ |
| return (getNodeIndex(elem) == ncount); |
| } |
| } |
| }; |
| |
| var defaultGetter = (d.isIE < 9 || (dojo.isIE && dojo.isQuirks)) ? function(cond){ |
| var clc = cond.toLowerCase(); |
| if(clc == "class"){ cond = "className"; } |
| return function(elem){ |
| return (caseSensitive ? elem.getAttribute(cond) : elem[cond]||elem[clc]); |
| } |
| } : function(cond){ |
| return function(elem){ |
| return (elem && elem.getAttribute && elem.hasAttribute(cond)); |
| } |
| }; |
| |
| var getSimpleFilterFunc = function(query, ignores){ |
| // generates a node tester function based on the passed query part. The |
| // query part is one of the structures generated by the query parser |
| // when it creates the query AST. The "ignores" object specifies which |
| // (if any) tests to skip, allowing the system to avoid duplicating |
| // work where it may have already been taken into account by other |
| // factors such as how the nodes to test were fetched in the first |
| // place |
| if(!query){ return yesman; } |
| ignores = ignores||{}; |
| |
| var ff = null; |
| |
| if(!("el" in ignores)){ |
| ff = agree(ff, _isElement); |
| } |
| |
| if(!("tag" in ignores)){ |
| if(query.tag != "*"){ |
| ff = agree(ff, function(elem){ |
| return (elem && (elem.tagName == query.getTag())); |
| }); |
| } |
| } |
| |
| if(!("classes" in ignores)){ |
| each(query.classes, function(cname, idx, arr){ |
| // get the class name |
| /* |
| var isWildcard = cname.charAt(cname.length-1) == "*"; |
| if(isWildcard){ |
| cname = cname.substr(0, cname.length-1); |
| } |
| // I dislike the regex thing, even if memoized in a cache, but it's VERY short |
| var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)"); |
| */ |
| var re = new RegExp("(?:^|\\s)" + cname + "(?:\\s|$)"); |
| ff = agree(ff, function(elem){ |
| return re.test(elem.className); |
| }); |
| ff.count = idx; |
| }); |
| } |
| |
| if(!("pseudos" in ignores)){ |
| each(query.pseudos, function(pseudo){ |
| var pn = pseudo.name; |
| if(pseudos[pn]){ |
| ff = agree(ff, pseudos[pn](pn, pseudo.value)); |
| } |
| }); |
| } |
| |
| if(!("attrs" in ignores)){ |
| each(query.attrs, function(attr){ |
| var matcher; |
| var a = attr.attr; |
| // type, attr, matchFor |
| if(attr.type && attrs[attr.type]){ |
| matcher = attrs[attr.type](a, attr.matchFor); |
| }else if(a.length){ |
| matcher = defaultGetter(a); |
| } |
| if(matcher){ |
| ff = agree(ff, matcher); |
| } |
| }); |
| } |
| |
| if(!("id" in ignores)){ |
| if(query.id){ |
| ff = agree(ff, function(elem){ |
| return (!!elem && (elem.id == query.id)); |
| }); |
| } |
| } |
| |
| if(!ff){ |
| if(!("default" in ignores)){ |
| ff = yesman; |
| } |
| } |
| return ff; |
| }; |
| |
| var _nextSibling = function(filterFunc){ |
| return function(node, ret, bag){ |
| while(node = node[_ns]){ |
| if(_noNES && (!_isElement(node))){ continue; } |
| if( |
| (!bag || _isUnique(node, bag)) && |
| filterFunc(node) |
| ){ |
| ret.push(node); |
| } |
| break; |
| } |
| return ret; |
| } |
| }; |
| |
| var _nextSiblings = function(filterFunc){ |
| return function(root, ret, bag){ |
| var te = root[_ns]; |
| while(te){ |
| if(_simpleNodeTest(te)){ |
| if(bag && !_isUnique(te, bag)){ |
| break; |
| } |
| if(filterFunc(te)){ |
| ret.push(te); |
| } |
| } |
| te = te[_ns]; |
| } |
| return ret; |
| } |
| }; |
| |
| // get an array of child *elements*, skipping text and comment nodes |
| var _childElements = function(filterFunc){ |
| filterFunc = filterFunc||yesman; |
| return function(root, ret, bag){ |
| // get an array of child elements, skipping text and comment nodes |
| var te, x = 0, tret = root[childNodesName]; |
| while(te = tret[x++]){ |
| if( |
| _simpleNodeTest(te) && |
| (!bag || _isUnique(te, bag)) && |
| (filterFunc(te, x)) |
| ){ |
| ret.push(te); |
| } |
| } |
| return ret; |
| }; |
| }; |
| |
| /* |
| // thanks, Dean! |
| var itemIsAfterRoot = d.isIE ? function(item, root){ |
| return (item.sourceIndex > root.sourceIndex); |
| } : function(item, root){ |
| return (item.compareDocumentPosition(root) == 2); |
| }; |
| */ |
| |
| // test to see if node is below root |
| var _isDescendant = function(node, root){ |
| var pn = node.parentNode; |
| while(pn){ |
| if(pn == root){ |
| break; |
| } |
| pn = pn.parentNode; |
| } |
| return !!pn; |
| }; |
| |
| var _getElementsFuncCache = {}; |
| |
| var getElementsFunc = function(query){ |
| var retFunc = _getElementsFuncCache[query.query]; |
| // if we've got a cached dispatcher, just use that |
| if(retFunc){ return retFunc; } |
| // else, generate a new on |
| |
| // NOTE: |
| // this function returns a function that searches for nodes and |
| // filters them. The search may be specialized by infix operators |
| // (">", "~", or "+") else it will default to searching all |
| // descendants (the " " selector). Once a group of children is |
| // found, a test function is applied to weed out the ones we |
| // don't want. Many common cases can be fast-pathed. We spend a |
| // lot of cycles to create a dispatcher that doesn't do more work |
| // than necessary at any point since, unlike this function, the |
| // dispatchers will be called every time. The logic of generating |
| // efficient dispatchers looks like this in pseudo code: |
| // |
| // # if it's a purely descendant query (no ">", "+", or "~" modifiers) |
| // if infixOperator == " ": |
| // if only(id): |
| // return def(root): |
| // return d.byId(id, root); |
| // |
| // elif id: |
| // return def(root): |
| // return filter(d.byId(id, root)); |
| // |
| // elif cssClass && getElementsByClassName: |
| // return def(root): |
| // return filter(root.getElementsByClassName(cssClass)); |
| // |
| // elif only(tag): |
| // return def(root): |
| // return root.getElementsByTagName(tagName); |
| // |
| // else: |
| // # search by tag name, then filter |
| // return def(root): |
| // return filter(root.getElementsByTagName(tagName||"*")); |
| // |
| // elif infixOperator == ">": |
| // # search direct children |
| // return def(root): |
| // return filter(root.children); |
| // |
| // elif infixOperator == "+": |
| // # search next sibling |
| // return def(root): |
| // return filter(root.nextElementSibling); |
| // |
| // elif infixOperator == "~": |
| // # search rightward siblings |
| // return def(root): |
| // return filter(nextSiblings(root)); |
| |
| var io = query.infixOper; |
| var oper = (io ? io.oper : ""); |
| // the default filter func which tests for all conditions in the query |
| // part. This is potentially inefficient, so some optimized paths may |
| // re-define it to test fewer things. |
| var filterFunc = getSimpleFilterFunc(query, { el: 1 }); |
| var qt = query.tag; |
| var wildcardTag = ("*" == qt); |
| var ecs = getDoc()["getElementsByClassName"]; |
| |
| if(!oper){ |
| // if there's no infix operator, then it's a descendant query. ID |
| // and "elements by class name" variants can be accelerated so we |
| // call them out explicitly: |
| if(query.id){ |
| // testing shows that the overhead of yesman() is acceptable |
| // and can save us some bytes vs. re-defining the function |
| // everywhere. |
| filterFunc = (!query.loops && wildcardTag) ? |
| yesman : |
| getSimpleFilterFunc(query, { el: 1, id: 1 }); |
| |
| retFunc = function(root, arr){ |
| var te = d.byId(query.id, (root.ownerDocument||root)); |
| if(!te || !filterFunc(te)){ return; } |
| if(9 == root.nodeType){ // if root's a doc, we just return directly |
| return getArr(te, arr); |
| }else{ // otherwise check ancestry |
| if(_isDescendant(te, root)){ |
| return getArr(te, arr); |
| } |
| } |
| } |
| }else if( |
| ecs && |
| // isAlien check. Workaround for Prototype.js being totally evil/dumb. |
| /\{\s*\[native code\]\s*\}/.test(String(ecs)) && |
| query.classes.length && |
| !cssCaseBug |
| ){ |
| // it's a class-based query and we've got a fast way to run it. |
| |
| // ignore class and ID filters since we will have handled both |
| filterFunc = getSimpleFilterFunc(query, { el: 1, classes: 1, id: 1 }); |
| var classesString = query.classes.join(" "); |
| retFunc = function(root, arr, bag){ |
| var ret = getArr(0, arr), te, x=0; |
| var tret = root.getElementsByClassName(classesString); |
| while((te = tret[x++])){ |
| if(filterFunc(te, root) && _isUnique(te, bag)){ |
| ret.push(te); |
| } |
| } |
| return ret; |
| }; |
| |
| }else if(!wildcardTag && !query.loops){ |
| // it's tag only. Fast-path it. |
| retFunc = function(root, arr, bag){ |
| var ret = getArr(0, arr), te, x=0; |
| var tret = root.getElementsByTagName(query.getTag()); |
| while((te = tret[x++])){ |
| if(_isUnique(te, bag)){ |
| ret.push(te); |
| } |
| } |
| return ret; |
| }; |
| }else{ |
| // the common case: |
| // a descendant selector without a fast path. By now it's got |
| // to have a tag selector, even if it's just "*" so we query |
| // by that and filter |
| filterFunc = getSimpleFilterFunc(query, { el: 1, tag: 1, id: 1 }); |
| retFunc = function(root, arr, bag){ |
| var ret = getArr(0, arr), te, x=0; |
| // we use getTag() to avoid case sensitivity issues |
| var tret = root.getElementsByTagName(query.getTag()); |
| while((te = tret[x++])){ |
| if(filterFunc(te, root) && _isUnique(te, bag)){ |
| ret.push(te); |
| } |
| } |
| return ret; |
| }; |
| } |
| }else{ |
| // the query is scoped in some way. Instead of querying by tag we |
| // use some other collection to find candidate nodes |
| var skipFilters = { el: 1 }; |
| if(wildcardTag){ |
| skipFilters.tag = 1; |
| } |
| filterFunc = getSimpleFilterFunc(query, skipFilters); |
| if("+" == oper){ |
| retFunc = _nextSibling(filterFunc); |
| }else if("~" == oper){ |
| retFunc = _nextSiblings(filterFunc); |
| }else if(">" == oper){ |
| retFunc = _childElements(filterFunc); |
| } |
| } |
| // cache it and return |
| return _getElementsFuncCache[query.query] = retFunc; |
| }; |
| |
| var filterDown = function(root, queryParts){ |
| // NOTE: |
| // this is the guts of the DOM query system. It takes a list of |
| // parsed query parts and a root and finds children which match |
| // the selector represented by the parts |
| var candidates = getArr(root), qp, x, te, qpl = queryParts.length, bag, ret; |
| |
| for(var i = 0; i < qpl; i++){ |
| ret = []; |
| qp = queryParts[i]; |
| x = candidates.length - 1; |
| if(x > 0){ |
| // if we have more than one root at this level, provide a new |
| // hash to use for checking group membership but tell the |
| // system not to post-filter us since we will already have been |
| // gauranteed to be unique |
| bag = {}; |
| ret.nozip = true; |
| } |
| var gef = getElementsFunc(qp); |
| for(var j = 0; (te = candidates[j]); j++){ |
| // for every root, get the elements that match the descendant |
| // selector, adding them to the "ret" array and filtering them |
| // via membership in this level's bag. If there are more query |
| // parts, then this level's return will be used as the next |
| // level's candidates |
| gef(te, ret, bag); |
| } |
| if(!ret.length){ break; } |
| candidates = ret; |
| } |
| return ret; |
| }; |
| |
| //////////////////////////////////////////////////////////////////////// |
| // the query runner |
| //////////////////////////////////////////////////////////////////////// |
| |
| // these are the primary caches for full-query results. The query |
| // dispatcher functions are generated then stored here for hash lookup in |
| // the future |
| var _queryFuncCacheDOM = {}, |
| _queryFuncCacheQSA = {}; |
| |
| // this is the second level of spliting, from full-length queries (e.g., |
| // "div.foo .bar") into simple query expressions (e.g., ["div.foo", |
| // ".bar"]) |
| var getStepQueryFunc = function(query){ |
| var qparts = getQueryParts(trim(query)); |
| |
| // if it's trivial, avoid iteration and zipping costs |
| if(qparts.length == 1){ |
| // we optimize this case here to prevent dispatch further down the |
| // chain, potentially slowing things down. We could more elegantly |
| // handle this in filterDown(), but it's slower for simple things |
| // that need to be fast (e.g., "#someId"). |
| var tef = getElementsFunc(qparts[0]); |
| return function(root){ |
| var r = tef(root, new qlc()); |
| if(r){ r.nozip = true; } |
| return r; |
| } |
| } |
| |
| // otherwise, break it up and return a runner that iterates over the parts recursively |
| return function(root){ |
| return filterDown(root, qparts); |
| } |
| }; |
| |
| // NOTES: |
| // * we can't trust QSA for anything but document-rooted queries, so |
| // caching is split into DOM query evaluators and QSA query evaluators |
| // * caching query results is dirty and leak-prone (or, at a minimum, |
| // prone to unbounded growth). Other toolkits may go this route, but |
| // they totally destroy their own ability to manage their memory |
| // footprint. If we implement it, it should only ever be with a fixed |
| // total element reference # limit and an LRU-style algorithm since JS |
| // has no weakref support. Caching compiled query evaluators is also |
| // potentially problematic, but even on large documents the size of the |
| // query evaluators is often < 100 function objects per evaluator (and |
| // LRU can be applied if it's ever shown to be an issue). |
| // * since IE's QSA support is currently only for HTML documents and even |
| // then only in IE 8's "standards mode", we have to detect our dispatch |
| // route at query time and keep 2 separate caches. Ugg. |
| |
| // we need to determine if we think we can run a given query via |
| // querySelectorAll or if we'll need to fall back on DOM queries to get |
| // there. We need a lot of information about the environment and the query |
| // to make the determiniation (e.g. does it support QSA, does the query in |
| // question work in the native QSA impl, etc.). |
| var nua = navigator.userAgent; |
| // some versions of Safari provided QSA, but it was buggy and crash-prone. |
| // We need te detect the right "internal" webkit version to make this work. |
| var wk = "WebKit/"; |
| var is525 = ( |
| d.isWebKit && |
| (nua.indexOf(wk) > 0) && |
| (parseFloat(nua.split(wk)[1]) > 528) |
| ); |
| |
| // IE QSA queries may incorrectly include comment nodes, so we throw the |
| // zipping function into "remove" comments mode instead of the normal "skip |
| // it" which every other QSA-clued browser enjoys |
| var noZip = d.isIE ? "commentStrip" : "nozip"; |
| |
| var qsa = "querySelectorAll"; |
| var qsaAvail = ( |
| !!getDoc()[qsa] && |
| // see #5832 |
| (!d.isSafari || (d.isSafari > 3.1) || is525 ) |
| ); |
| |
| //Don't bother with n+3 type of matches, IE complains if we modify those. |
| var infixSpaceRe = /n\+\d|([^ ])?([>~+])([^ =])?/g; |
| var infixSpaceFunc = function(match, pre, ch, post) { |
| return ch ? (pre ? pre + " " : "") + ch + (post ? " " + post : "") : /*n+3*/ match; |
| }; |
| |
| var getQueryFunc = function(query, forceDOM){ |
| //Normalize query. The CSS3 selectors spec allows for omitting spaces around |
| //infix operators, >, ~ and + |
| //Do the work here since detection for spaces is used as a simple "not use QSA" |
| //test below. |
| query = query.replace(infixSpaceRe, infixSpaceFunc); |
| |
| if(qsaAvail){ |
| // if we've got a cached variant and we think we can do it, run it! |
| var qsaCached = _queryFuncCacheQSA[query]; |
| if(qsaCached && !forceDOM){ return qsaCached; } |
| } |
| |
| // else if we've got a DOM cached variant, assume that we already know |
| // all we need to and use it |
| var domCached = _queryFuncCacheDOM[query]; |
| if(domCached){ return domCached; } |
| |
| // TODO: |
| // today we're caching DOM and QSA branches separately so we |
| // recalc useQSA every time. If we had a way to tag root+query |
| // efficiently, we'd be in good shape to do a global cache. |
| |
| var qcz = query.charAt(0); |
| var nospace = (-1 == query.indexOf(" ")); |
| |
| // byId searches are wicked fast compared to QSA, even when filtering |
| // is required |
| if( (query.indexOf("#") >= 0) && (nospace) ){ |
| forceDOM = true; |
| } |
| |
| var useQSA = ( |
| qsaAvail && (!forceDOM) && |
| // as per CSS 3, we can't currently start w/ combinator: |
| // http://www.w3.org/TR/css3-selectors/#w3cselgrammar |
| (specials.indexOf(qcz) == -1) && |
| // IE's QSA impl sucks on pseudos |
| (!d.isIE || (query.indexOf(":") == -1)) && |
| |
| (!(cssCaseBug && (query.indexOf(".") >= 0))) && |
| |
| // FIXME: |
| // need to tighten up browser rules on ":contains" and "|=" to |
| // figure out which aren't good |
| // Latest webkit (around 531.21.8) does not seem to do well with :checked on option |
| // elements, even though according to spec, selected options should |
| // match :checked. So go nonQSA for it: |
| // http://bugs.dojotoolkit.org/ticket/5179 |
| (query.indexOf(":contains") == -1) && (query.indexOf(":checked") == -1) && |
| (query.indexOf("|=") == -1) // some browsers don't grok it |
| ); |
| |
| // TODO: |
| // if we've got a descendant query (e.g., "> .thinger" instead of |
| // just ".thinger") in a QSA-able doc, but are passed a child as a |
| // root, it should be possible to give the item a synthetic ID and |
| // trivially rewrite the query to the form "#synid > .thinger" to |
| // use the QSA branch |
| |
| |
| if(useQSA){ |
| var tq = (specials.indexOf(query.charAt(query.length-1)) >= 0) ? |
| (query + " *") : query; |
| return _queryFuncCacheQSA[query] = function(root){ |
| try{ |
| // the QSA system contains an egregious spec bug which |
| // limits us, effectively, to only running QSA queries over |
| // entire documents. See: |
| // http://ejohn.org/blog/thoughts-on-queryselectorall/ |
| // despite this, we can also handle QSA runs on simple |
| // selectors, but we don't want detection to be expensive |
| // so we're just checking for the presence of a space char |
| // right now. Not elegant, but it's cheaper than running |
| // the query parser when we might not need to |
| if(!((9 == root.nodeType) || nospace)){ throw ""; } |
| var r = root[qsa](tq); |
| // skip expensive duplication checks and just wrap in a NodeList |
| r[noZip] = true; |
| return r; |
| }catch(e){ |
| // else run the DOM branch on this query, ensuring that we |
| // default that way in the future |
| return getQueryFunc(query, true)(root); |
| } |
| } |
| }else{ |
| // DOM branch |
| var parts = query.split(/\s*,\s*/); |
| return _queryFuncCacheDOM[query] = ((parts.length < 2) ? |
| // if not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher |
| getStepQueryFunc(query) : |
| // if it *is* a complex query, break it up into its |
| // constituent parts and return a dispatcher that will |
| // merge the parts when run |
| function(root){ |
| var pindex = 0, // avoid array alloc for every invocation |
| ret = [], |
| tp; |
| while((tp = parts[pindex++])){ |
| ret = ret.concat(getStepQueryFunc(tp)(root)); |
| } |
| return ret; |
| } |
| ); |
| } |
| }; |
| |
| var _zipIdx = 0; |
| |
| // NOTE: |
| // this function is Moo inspired, but our own impl to deal correctly |
| // with XML in IE |
| var _nodeUID = d.isIE ? function(node){ |
| if(caseSensitive){ |
| // XML docs don't have uniqueID on their nodes |
| return (node.getAttribute("_uid") || node.setAttribute("_uid", ++_zipIdx) || _zipIdx); |
| |
| }else{ |
| return node.uniqueID; |
| } |
| } : |
| function(node){ |
| return (node._uid || (node._uid = ++_zipIdx)); |
| }; |
| |
| // determine if a node in is unique in a "bag". In this case we don't want |
| // to flatten a list of unique items, but rather just tell if the item in |
| // question is already in the bag. Normally we'd just use hash lookup to do |
| // this for us but IE's DOM is busted so we can't really count on that. On |
| // the upside, it gives us a built in unique ID function. |
| var _isUnique = function(node, bag){ |
| if(!bag){ return 1; } |
| var id = _nodeUID(node); |
| if(!bag[id]){ return bag[id] = 1; } |
| return 0; |
| }; |
| |
| // attempt to efficiently determine if an item in a list is a dupe, |
| // returning a list of "uniques", hopefully in doucment order |
| var _zipIdxName = "_zipIdx"; |
| var _zip = function(arr){ |
| if(arr && arr.nozip){ |
| return (qlc._wrap) ? qlc._wrap(arr) : arr; |
| } |
| // var ret = new d._NodeListCtor(); |
| var ret = new qlc(); |
| if(!arr || !arr.length){ return ret; } |
| if(arr[0]){ |
| ret.push(arr[0]); |
| } |
| if(arr.length < 2){ return ret; } |
| |
| _zipIdx++; |
| |
| // we have to fork here for IE and XML docs because we can't set |
| // expandos on their nodes (apparently). *sigh* |
| if(d.isIE && caseSensitive){ |
| var szidx = _zipIdx+""; |
| arr[0].setAttribute(_zipIdxName, szidx); |
| for(var x = 1, te; te = arr[x]; x++){ |
| if(arr[x].getAttribute(_zipIdxName) != szidx){ |
| ret.push(te); |
| } |
| te.setAttribute(_zipIdxName, szidx); |
| } |
| }else if(d.isIE && arr.commentStrip){ |
| try{ |
| for(var x = 1, te; te = arr[x]; x++){ |
| if(_isElement(te)){ |
| ret.push(te); |
| } |
| } |
| }catch(e){ /* squelch */ } |
| }else{ |
| if(arr[0]){ arr[0][_zipIdxName] = _zipIdx; } |
| for(var x = 1, te; te = arr[x]; x++){ |
| if(arr[x][_zipIdxName] != _zipIdx){ |
| ret.push(te); |
| } |
| te[_zipIdxName] = _zipIdx; |
| } |
| } |
| return ret; |
| }; |
| |
| // the main executor |
| d.query = function(/*String*/ query, /*String|DOMNode?*/ root){ |
| // summary: |
| // Returns nodes which match the given CSS3 selector, searching the |
| // entire document by default but optionally taking a node to scope |
| // the search by. Returns an instance of dojo.NodeList. |
| // description: |
| // dojo.query() is the swiss army knife of DOM node manipulation in |
| // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's |
| // "$" function, dojo.query provides robust, high-performance |
| // CSS-based node selector support with the option of scoping searches |
| // to a particular sub-tree of a document. |
| // |
| // Supported Selectors: |
| // -------------------- |
| // |
| // dojo.query() supports a rich set of CSS3 selectors, including: |
| // |
| // * class selectors (e.g., `.foo`) |
| // * node type selectors like `span` |
| // * ` ` descendant selectors |
| // * `>` child element selectors |
| // * `#foo` style ID selectors |
| // * `*` universal selector |
| // * `~`, the preceded-by sibling selector |
| // * `+`, the immediately preceded-by sibling selector |
| // * attribute queries: |
| // | * `[foo]` attribute presence selector |
| // | * `[foo='bar']` attribute value exact match |
| // | * `[foo~='bar']` attribute value list item match |
| // | * `[foo^='bar']` attribute start match |
| // | * `[foo$='bar']` attribute end match |
| // | * `[foo*='bar']` attribute substring match |
| // * `:first-child`, `:last-child`, and `:only-child` positional selectors |
| // * `:empty` content emtpy selector |
| // * `:checked` pseudo selector |
| // * `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations |
| // * `:nth-child(even)`, `:nth-child(odd)` positional selectors |
| // * `:not(...)` negation pseudo selectors |
| // |
| // Any legal combination of these selectors will work with |
| // `dojo.query()`, including compound selectors ("," delimited). |
| // Very complex and useful searches can be constructed with this |
| // palette of selectors and when combined with functions for |
| // manipulation presented by dojo.NodeList, many types of DOM |
| // manipulation operations become very straightforward. |
| // |
| // Unsupported Selectors: |
| // ---------------------- |
| // |
| // While dojo.query handles many CSS3 selectors, some fall outside of |
| // what's reasonable for a programmatic node querying engine to |
| // handle. Currently unsupported selectors include: |
| // |
| // * namespace-differentiated selectors of any form |
| // * all `::` pseduo-element selectors |
| // * certain pseduo-selectors which don't get a lot of day-to-day use: |
| // | * `:root`, `:lang()`, `:target`, `:focus` |
| // * all visual and state selectors: |
| // | * `:root`, `:active`, `:hover`, `:visisted`, `:link`, |
| // `:enabled`, `:disabled` |
| // * `:*-of-type` pseudo selectors |
| // |
| // dojo.query and XML Documents: |
| // ----------------------------- |
| // |
| // `dojo.query` (as of dojo 1.2) supports searching XML documents |
| // in a case-sensitive manner. If an HTML document is served with |
| // a doctype that forces case-sensitivity (e.g., XHTML 1.1 |
| // Strict), dojo.query() will detect this and "do the right |
| // thing". Case sensitivity is dependent upon the document being |
| // searched and not the query used. It is therefore possible to |
| // use case-sensitive queries on strict sub-documents (iframes, |
| // etc.) or XML documents while still assuming case-insensitivity |
| // for a host/root document. |
| // |
| // Non-selector Queries: |
| // --------------------- |
| // |
| // If something other than a String is passed for the query, |
| // `dojo.query` will return a new `dojo.NodeList` instance |
| // constructed from that parameter alone and all further |
| // processing will stop. This means that if you have a reference |
| // to a node or NodeList, you can quickly construct a new NodeList |
| // from the original by calling `dojo.query(node)` or |
| // `dojo.query(list)`. |
| // |
| // query: |
| // The CSS3 expression to match against. For details on the syntax of |
| // CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors> |
| // root: |
| // A DOMNode (or node id) to scope the search from. Optional. |
| // returns: dojo.NodeList |
| // An instance of `dojo.NodeList`. Many methods are available on |
| // NodeLists for searching, iterating, manipulating, and handling |
| // events on the matched nodes in the returned list. |
| // example: |
| // search the entire document for elements with the class "foo": |
| // | dojo.query(".foo"); |
| // these elements will match: |
| // | <span class="foo"></span> |
| // | <span class="foo bar"></span> |
| // | <p class="thud foo"></p> |
| // example: |
| // search the entire document for elements with the classes "foo" *and* "bar": |
| // | dojo.query(".foo.bar"); |
| // these elements will match: |
| // | <span class="foo bar"></span> |
| // while these will not: |
| // | <span class="foo"></span> |
| // | <p class="thud foo"></p> |
| // example: |
| // find `<span>` elements which are descendants of paragraphs and |
| // which have a "highlighted" class: |
| // | dojo.query("p span.highlighted"); |
| // the innermost span in this fragment matches: |
| // | <p class="foo"> |
| // | <span>... |
| // | <span class="highlighted foo bar">...</span> |
| // | </span> |
| // | </p> |
| // example: |
| // set an "odd" class on all odd table rows inside of the table |
| // `#tabular_data`, using the `>` (direct child) selector to avoid |
| // affecting any nested tables: |
| // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd"); |
| // example: |
| // remove all elements with the class "error" from the document |
| // and store them in a list: |
| // | var errors = dojo.query(".error").orphan(); |
| // example: |
| // add an onclick handler to every submit button in the document |
| // which causes the form to be sent via Ajax instead: |
| // | dojo.query("input[type='submit']").onclick(function(e){ |
| // | dojo.stopEvent(e); // prevent sending the form |
| // | var btn = e.target; |
| // | dojo.xhrPost({ |
| // | form: btn.form, |
| // | load: function(data){ |
| // | // replace the form with the response |
| // | var div = dojo.doc.createElement("div"); |
| // | dojo.place(div, btn.form, "after"); |
| // | div.innerHTML = data; |
| // | dojo.style(btn.form, "display", "none"); |
| // | } |
| // | }); |
| // | }); |
| |
| //Set list constructor to desired value. This can change |
| //between calls, so always re-assign here. |
| qlc = d._NodeListCtor; |
| |
| if(!query){ |
| return new qlc(); |
| } |
| |
| if(query.constructor == qlc){ |
| return query; |
| } |
| if(typeof query != "string"){ // inline'd type check |
| return new qlc(query); // dojo.NodeList |
| } |
| if(typeof root == "string"){ // inline'd type check |
| root = d.byId(root); |
| if(!root){ return new qlc(); } |
| } |
| |
| root = root||getDoc(); |
| var od = root.ownerDocument||root.documentElement; |
| |
| // throw the big case sensitivity switch |
| |
| // NOTE: |
| // Opera in XHTML mode doesn't detect case-sensitivity correctly |
| // and it's not clear that there's any way to test for it |
| caseSensitive = (root.contentType && root.contentType=="application/xml") || |
| (d.isOpera && (root.doctype || od.toString() == "[object XMLDocument]")) || |
| (!!od) && |
| (d.isIE ? od.xml : (root.xmlVersion||od.xmlVersion)); |
| |
| // NOTE: |
| // adding "true" as the 2nd argument to getQueryFunc is useful for |
| // testing the DOM branch without worrying about the |
| // behavior/performance of the QSA branch. |
| var r = getQueryFunc(query)(root); |
| |
| // FIXME: |
| // need to investigate this branch WRT #8074 and #8075 |
| if(r && r.nozip && !qlc._wrap){ |
| return r; |
| } |
| return _zip(r); // dojo.NodeList |
| } |
| |
| // FIXME: need to add infrastructure for post-filtering pseudos, ala :last |
| d.query.pseudos = pseudos; |
| |
| // function for filtering a NodeList based on a selector, optimized for simple selectors |
| d._filterQueryResult = function(/*NodeList*/ nodeList, /*String*/ filter, /*String|DOMNode?*/ root){ |
| var tmpNodeList = new d._NodeListCtor(), |
| parts = getQueryParts(filter), |
| filterFunc = |
| (parts.length == 1 && !/[^\w#\.]/.test(filter)) ? |
| getSimpleFilterFunc(parts[0]) : |
| function(node) { |
| return dojo.query(filter, root).indexOf(node) != -1; |
| }; |
| for(var x = 0, te; te = nodeList[x]; x++){ |
| if(filterFunc(te)){ tmpNodeList.push(te); } |
| } |
| return tmpNodeList; |
| } |
| };//end defineQuery |
| |
| var defineAcme= function(){ |
| // a self-sufficient query impl |
| acme = { |
| trim: function(/*String*/ str){ |
| // summary: |
| // trims whitespaces from both sides of the string |
| str = str.replace(/^\s+/, ''); |
| for(var i = str.length - 1; i >= 0; i--){ |
| if(/\S/.test(str.charAt(i))){ |
| str = str.substring(0, i + 1); |
| break; |
| } |
| } |
| return str; // String |
| }, |
| forEach: function(/*String*/ arr, /*Function*/ callback, /*Object?*/ thisObject){ |
| // summary: |
| // an iterator function that passes items, indexes, |
| // and the array to a callback |
| if(!arr || !arr.length){ return; } |
| for(var i=0,l=arr.length; i<l; ++i){ |
| callback.call(thisObject||window, arr[i], i, arr); |
| } |
| }, |
| byId: function(id, doc){ |
| // summary: |
| // a function that return an element by ID, but also |
| // accepts nodes safely |
| if(typeof id == "string"){ |
| return (doc||document).getElementById(id); // DomNode |
| }else{ |
| return id; // DomNode |
| } |
| }, |
| // the default document to search |
| doc: document, |
| // the constructor for node list objects returned from query() |
| NodeList: Array |
| }; |
| |
| // define acme.isIE, acme.isSafari, acme.isOpera, etc. |
| var n = navigator; |
| var dua = n.userAgent; |
| var dav = n.appVersion; |
| var tv = parseFloat(dav); |
| acme.isOpera = (dua.indexOf("Opera") >= 0) ? tv: undefined; |
| acme.isKhtml = (dav.indexOf("Konqueror") >= 0) ? tv : undefined; |
| acme.isWebKit = parseFloat(dua.split("WebKit/")[1]) || undefined; |
| acme.isChrome = parseFloat(dua.split("Chrome/")[1]) || undefined; |
| var index = Math.max(dav.indexOf("WebKit"), dav.indexOf("Safari"), 0); |
| if(index && !acme.isChrome){ |
| acme.isSafari = parseFloat(dav.split("Version/")[1]); |
| if(!acme.isSafari || parseFloat(dav.substr(index + 7)) <= 419.3){ |
| acme.isSafari = 2; |
| } |
| } |
| if(document.all && !acme.isOpera){ |
| acme.isIE = parseFloat(dav.split("MSIE ")[1]) || undefined; |
| } |
| |
| Array._wrap = function(arr){ return arr; }; |
| return acme; |
| }; |
| |
| //prefers queryPortability, then acme, then dojo |
| if(this["dojo"]){ |
| dojo.provide("dojo._base.query"); |
| |
| |
| defineQuery(this["queryPortability"]||this["acme"]||dojo); |
| }else{ |
| defineQuery(this["queryPortability"]||this["acme"]||defineAcme()); |
| } |
| |
| })(); |
| |
| /* |
| */ |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.xhr"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.xhr"] = true; |
| dojo.provide("dojo._base.xhr"); |
| |
| |
| |
| |
| |
| |
| (function(){ |
| var _d = dojo, cfg = _d.config; |
| |
| function setValue(/*Object*/obj, /*String*/name, /*String*/value){ |
| //summary: |
| // For the named property in object, set the value. If a value |
| // already exists and it is a string, convert the value to be an |
| // array of values. |
| |
| //Skip it if there is no value |
| if(value === null){ |
| return; |
| } |
| |
| var val = obj[name]; |
| if(typeof val == "string"){ // inline'd type check |
| obj[name] = [val, value]; |
| }else if(_d.isArray(val)){ |
| val.push(value); |
| }else{ |
| obj[name] = value; |
| } |
| } |
| |
| dojo.fieldToObject = function(/*DOMNode||String*/ inputNode){ |
| // summary: |
| // Serialize a form field to a JavaScript object. |
| // |
| // description: |
| // Returns the value encoded in a form field as |
| // as a string or an array of strings. Disabled form elements |
| // and unchecked radio and checkboxes are skipped. Multi-select |
| // elements are returned as an array of string values. |
| var ret = null; |
| var item = _d.byId(inputNode); |
| if(item){ |
| var _in = item.name; |
| var type = (item.type||"").toLowerCase(); |
| if(_in && type && !item.disabled){ |
| if(type == "radio" || type == "checkbox"){ |
| if(item.checked){ ret = item.value; } |
| }else if(item.multiple){ |
| ret = []; |
| _d.query("option", item).forEach(function(opt){ |
| if(opt.selected){ |
| ret.push(opt.value); |
| } |
| }); |
| }else{ |
| ret = item.value; |
| } |
| } |
| } |
| return ret; // Object |
| }; |
| |
| dojo.formToObject = function(/*DOMNode||String*/ formNode){ |
| // summary: |
| // Serialize a form node to a JavaScript object. |
| // description: |
| // Returns the values encoded in an HTML form as |
| // string properties in an object which it then returns. Disabled form |
| // elements, buttons, and other non-value form elements are skipped. |
| // Multi-select elements are returned as an array of string values. |
| // |
| // example: |
| // This form: |
| // | <form id="test_form"> |
| // | <input type="text" name="blah" value="blah"> |
| // | <input type="text" name="no_value" value="blah" disabled> |
| // | <input type="button" name="no_value2" value="blah"> |
| // | <select type="select" multiple name="multi" size="5"> |
| // | <option value="blah">blah</option> |
| // | <option value="thud" selected>thud</option> |
| // | <option value="thonk" selected>thonk</option> |
| // | </select> |
| // | </form> |
| // |
| // yields this object structure as the result of a call to |
| // formToObject(): |
| // |
| // | { |
| // | blah: "blah", |
| // | multi: [ |
| // | "thud", |
| // | "thonk" |
| // | ] |
| // | }; |
| |
| var ret = {}; |
| var exclude = "file|submit|image|reset|button|"; |
| _d.forEach(dojo.byId(formNode).elements, function(item){ |
| var _in = item.name; |
| var type = (item.type||"").toLowerCase(); |
| if(_in && type && exclude.indexOf(type) == -1 && !item.disabled){ |
| setValue(ret, _in, _d.fieldToObject(item)); |
| if(type == "image"){ |
| ret[_in+".x"] = ret[_in+".y"] = ret[_in].x = ret[_in].y = 0; |
| } |
| } |
| }); |
| return ret; // Object |
| }; |
| |
| dojo.objectToQuery = function(/*Object*/ map){ |
| // summary: |
| // takes a name/value mapping object and returns a string representing |
| // a URL-encoded version of that object. |
| // example: |
| // this object: |
| // |
| // | { |
| // | blah: "blah", |
| // | multi: [ |
| // | "thud", |
| // | "thonk" |
| // | ] |
| // | }; |
| // |
| // yields the following query string: |
| // |
| // | "blah=blah&multi=thud&multi=thonk" |
| |
| // FIXME: need to implement encodeAscii!! |
| var enc = encodeURIComponent; |
| var pairs = []; |
| var backstop = {}; |
| for(var name in map){ |
| var value = map[name]; |
| if(value != backstop[name]){ |
| var assign = enc(name) + "="; |
| if(_d.isArray(value)){ |
| for(var i=0; i < value.length; i++){ |
| pairs.push(assign + enc(value[i])); |
| } |
| }else{ |
| pairs.push(assign + enc(value)); |
| } |
| } |
| } |
| return pairs.join("&"); // String |
| }; |
| |
| dojo.formToQuery = function(/*DOMNode||String*/ formNode){ |
| // summary: |
| // Returns a URL-encoded string representing the form passed as either a |
| // node or string ID identifying the form to serialize |
| return _d.objectToQuery(_d.formToObject(formNode)); // String |
| }; |
| |
| dojo.formToJson = function(/*DOMNode||String*/ formNode, /*Boolean?*/prettyPrint){ |
| // summary: |
| // Create a serialized JSON string from a form node or string |
| // ID identifying the form to serialize |
| return _d.toJson(_d.formToObject(formNode), prettyPrint); // String |
| }; |
| |
| dojo.queryToObject = function(/*String*/ str){ |
| // summary: |
| // Create an object representing a de-serialized query section of a |
| // URL. Query keys with multiple values are returned in an array. |
| // |
| // example: |
| // This string: |
| // |
| // | "foo=bar&foo=baz&thinger=%20spaces%20=blah&zonk=blarg&" |
| // |
| // results in this object structure: |
| // |
| // | { |
| // | foo: [ "bar", "baz" ], |
| // | thinger: " spaces =blah", |
| // | zonk: "blarg" |
| // | } |
| // |
| // Note that spaces and other urlencoded entities are correctly |
| // handled. |
| |
| // FIXME: should we grab the URL string if we're not passed one? |
| var ret = {}; |
| var qp = str.split("&"); |
| var dec = decodeURIComponent; |
| _d.forEach(qp, function(item){ |
| if(item.length){ |
| var parts = item.split("="); |
| var name = dec(parts.shift()); |
| var val = dec(parts.join("=")); |
| if(typeof ret[name] == "string"){ // inline'd type check |
| ret[name] = [ret[name]]; |
| } |
| |
| if(_d.isArray(ret[name])){ |
| ret[name].push(val); |
| }else{ |
| ret[name] = val; |
| } |
| } |
| }); |
| return ret; // Object |
| }; |
| |
| // need to block async callbacks from snatching this thread as the result |
| // of an async callback might call another sync XHR, this hangs khtml forever |
| // must checked by watchInFlight() |
| |
| dojo._blockAsync = false; |
| |
| // MOW: remove dojo._contentHandlers alias in 2.0 |
| var handlers = _d._contentHandlers = dojo.contentHandlers = { |
| // summary: |
| // A map of availble XHR transport handle types. Name matches the |
| // `handleAs` attribute passed to XHR calls. |
| // |
| // description: |
| // A map of availble XHR transport handle types. Name matches the |
| // `handleAs` attribute passed to XHR calls. Each contentHandler is |
| // called, passing the xhr object for manipulation. The return value |
| // from the contentHandler will be passed to the `load` or `handle` |
| // functions defined in the original xhr call. |
| // |
| // example: |
| // Creating a custom content-handler: |
| // | dojo.contentHandlers.makeCaps = function(xhr){ |
| // | return xhr.responseText.toUpperCase(); |
| // | } |
| // | // and later: |
| // | dojo.xhrGet({ |
| // | url:"foo.txt", |
| // | handleAs:"makeCaps", |
| // | load: function(data){ /* data is a toUpper version of foo.txt */ } |
| // | }); |
| |
| text: function(xhr){ |
| // summary: A contentHandler which simply returns the plaintext response data |
| return xhr.responseText; |
| }, |
| json: function(xhr){ |
| // summary: A contentHandler which returns a JavaScript object created from the response data |
| return _d.fromJson(xhr.responseText || null); |
| }, |
| "json-comment-filtered": function(xhr){ |
| // summary: A contentHandler which expects comment-filtered JSON. |
| // description: |
| // A contentHandler which expects comment-filtered JSON. |
| // the json-comment-filtered option was implemented to prevent |
| // "JavaScript Hijacking", but it is less secure than standard JSON. Use |
| // standard JSON instead. JSON prefixing can be used to subvert hijacking. |
| // |
| // Will throw a notice suggesting to use application/json mimetype, as |
| // json-commenting can introduce security issues. To decrease the chances of hijacking, |
| // use the standard `json` contentHandler, and prefix your "JSON" with: {}&& |
| // |
| // use djConfig.useCommentedJson = true to turn off the notice |
| if(!dojo.config.useCommentedJson){ |
| console.warn("Consider using the standard mimetype:application/json." |
| + " json-commenting can introduce security issues. To" |
| + " decrease the chances of hijacking, use the standard the 'json' handler and" |
| + " prefix your json with: {}&&\n" |
| + "Use djConfig.useCommentedJson=true to turn off this message."); |
| } |
| |
| var value = xhr.responseText; |
| var cStartIdx = value.indexOf("\/*"); |
| var cEndIdx = value.lastIndexOf("*\/"); |
| if(cStartIdx == -1 || cEndIdx == -1){ |
| throw new Error("JSON was not comment filtered"); |
| } |
| return _d.fromJson(value.substring(cStartIdx+2, cEndIdx)); |
| }, |
| javascript: function(xhr){ |
| // summary: A contentHandler which evaluates the response data, expecting it to be valid JavaScript |
| |
| // FIXME: try Moz and IE specific eval variants? |
| return _d.eval(xhr.responseText); |
| }, |
| xml: function(xhr){ |
| // summary: A contentHandler returning an XML Document parsed from the response data |
| var result = xhr.responseXML; |
| if(_d.isIE && (!result || !result.documentElement)){ |
| //WARNING: this branch used by the xml handling in dojo.io.iframe, |
| //so be sure to test dojo.io.iframe if making changes below. |
| var ms = function(n){ return "MSXML" + n + ".DOMDocument"; }; |
| var dp = ["Microsoft.XMLDOM", ms(6), ms(4), ms(3), ms(2)]; |
| _d.some(dp, function(p){ |
| try{ |
| var dom = new ActiveXObject(p); |
| dom.async = false; |
| dom.loadXML(xhr.responseText); |
| result = dom; |
| }catch(e){ return false; } |
| return true; |
| }); |
| } |
| return result; // DOMDocument |
| }, |
| "json-comment-optional": function(xhr){ |
| // summary: A contentHandler which checks the presence of comment-filtered JSON and |
| // alternates between the `json` and `json-comment-filtered` contentHandlers. |
| if(xhr.responseText && /^[^{\[]*\/\*/.test(xhr.responseText)){ |
| return handlers["json-comment-filtered"](xhr); |
| }else{ |
| return handlers["json"](xhr); |
| } |
| } |
| }; |
| |
| /*===== |
| dojo.__IoArgs = function(){ |
| // url: String |
| // URL to server endpoint. |
| // content: Object? |
| // Contains properties with string values. These |
| // properties will be serialized as name1=value2 and |
| // passed in the request. |
| // timeout: Integer? |
| // Milliseconds to wait for the response. If this time |
| // passes, the then error callbacks are called. |
| // form: DOMNode? |
| // DOM node for a form. Used to extract the form values |
| // and send to the server. |
| // preventCache: Boolean? |
| // Default is false. If true, then a |
| // "dojo.preventCache" parameter is sent in the request |
| // with a value that changes with each request |
| // (timestamp). Useful only with GET-type requests. |
| // handleAs: String? |
| // Acceptable values depend on the type of IO |
| // transport (see specific IO calls for more information). |
| // rawBody: String? |
| // Sets the raw body for an HTTP request. If this is used, then the content |
| // property is ignored. This is mostly useful for HTTP methods that have |
| // a body to their requests, like PUT or POST. This property can be used instead |
| // of postData and putData for dojo.rawXhrPost and dojo.rawXhrPut respectively. |
| // ioPublish: Boolean? |
| // Set this explicitly to false to prevent publishing of topics related to |
| // IO operations. Otherwise, if djConfig.ioPublish is set to true, topics |
| // will be published via dojo.publish for different phases of an IO operation. |
| // See dojo.__IoPublish for a list of topics that are published. |
| // load: Function? |
| // This function will be |
| // called on a successful HTTP response code. |
| // error: Function? |
| // This function will |
| // be called when the request fails due to a network or server error, the url |
| // is invalid, etc. It will also be called if the load or handle callback throws an |
| // exception, unless djConfig.debugAtAllCosts is true. This allows deployed applications |
| // to continue to run even when a logic error happens in the callback, while making |
| // it easier to troubleshoot while in debug mode. |
| // handle: Function? |
| // This function will |
| // be called at the end of every request, whether or not an error occurs. |
| this.url = url; |
| this.content = content; |
| this.timeout = timeout; |
| this.form = form; |
| this.preventCache = preventCache; |
| this.handleAs = handleAs; |
| this.ioPublish = ioPublish; |
| this.load = function(response, ioArgs){ |
| // ioArgs: dojo.__IoCallbackArgs |
| // Provides additional information about the request. |
| // response: Object |
| // The response in the format as defined with handleAs. |
| } |
| this.error = function(response, ioArgs){ |
| // ioArgs: dojo.__IoCallbackArgs |
| // Provides additional information about the request. |
| // response: Object |
| // The response in the format as defined with handleAs. |
| } |
| this.handle = function(loadOrError, response, ioArgs){ |
| // loadOrError: String |
| // Provides a string that tells you whether this function |
| // was called because of success (load) or failure (error). |
| // response: Object |
| // The response in the format as defined with handleAs. |
| // ioArgs: dojo.__IoCallbackArgs |
| // Provides additional information about the request. |
| } |
| } |
| =====*/ |
| |
| /*===== |
| dojo.__IoCallbackArgs = function(args, xhr, url, query, handleAs, id, canDelete, json){ |
| // args: Object |
| // the original object argument to the IO call. |
| // xhr: XMLHttpRequest |
| // For XMLHttpRequest calls only, the |
| // XMLHttpRequest object that was used for the |
| // request. |
| // url: String |
| // The final URL used for the call. Many times it |
| // will be different than the original args.url |
| // value. |
| // query: String |
| // For non-GET requests, the |
| // name1=value1&name2=value2 parameters sent up in |
| // the request. |
| // handleAs: String |
| // The final indicator on how the response will be |
| // handled. |
| // id: String |
| // For dojo.io.script calls only, the internal |
| // script ID used for the request. |
| // canDelete: Boolean |
| // For dojo.io.script calls only, indicates |
| // whether the script tag that represents the |
| // request can be deleted after callbacks have |
| // been called. Used internally to know when |
| // cleanup can happen on JSONP-type requests. |
| // json: Object |
| // For dojo.io.script calls only: holds the JSON |
| // response for JSONP-type requests. Used |
| // internally to hold on to the JSON responses. |
| // You should not need to access it directly -- |
| // the same object should be passed to the success |
| // callbacks directly. |
| this.args = args; |
| this.xhr = xhr; |
| this.url = url; |
| this.query = query; |
| this.handleAs = handleAs; |
| this.id = id; |
| this.canDelete = canDelete; |
| this.json = json; |
| } |
| =====*/ |
| |
| |
| /*===== |
| dojo.__IoPublish = function(){ |
| // summary: |
| // This is a list of IO topics that can be published |
| // if djConfig.ioPublish is set to true. IO topics can be |
| // published for any Input/Output, network operation. So, |
| // dojo.xhr, dojo.io.script and dojo.io.iframe can all |
| // trigger these topics to be published. |
| // start: String |
| // "/dojo/io/start" is sent when there are no outstanding IO |
| // requests, and a new IO request is started. No arguments |
| // are passed with this topic. |
| // send: String |
| // "/dojo/io/send" is sent whenever a new IO request is started. |
| // It passes the dojo.Deferred for the request with the topic. |
| // load: String |
| // "/dojo/io/load" is sent whenever an IO request has loaded |
| // successfully. It passes the response and the dojo.Deferred |
| // for the request with the topic. |
| // error: String |
| // "/dojo/io/error" is sent whenever an IO request has errored. |
| // It passes the error and the dojo.Deferred |
| // for the request with the topic. |
| // done: String |
| // "/dojo/io/done" is sent whenever an IO request has completed, |
| // either by loading or by erroring. It passes the error and |
| // the dojo.Deferred for the request with the topic. |
| // stop: String |
| // "/dojo/io/stop" is sent when all outstanding IO requests have |
| // finished. No arguments are passed with this topic. |
| this.start = "/dojo/io/start"; |
| this.send = "/dojo/io/send"; |
| this.load = "/dojo/io/load"; |
| this.error = "/dojo/io/error"; |
| this.done = "/dojo/io/done"; |
| this.stop = "/dojo/io/stop"; |
| } |
| =====*/ |
| |
| |
| dojo._ioSetArgs = function(/*dojo.__IoArgs*/args, |
| /*Function*/canceller, |
| /*Function*/okHandler, |
| /*Function*/errHandler){ |
| // summary: |
| // sets up the Deferred and ioArgs property on the Deferred so it |
| // can be used in an io call. |
| // args: |
| // The args object passed into the public io call. Recognized properties on |
| // the args object are: |
| // canceller: |
| // The canceller function used for the Deferred object. The function |
| // will receive one argument, the Deferred object that is related to the |
| // canceller. |
| // okHandler: |
| // The first OK callback to be registered with Deferred. It has the opportunity |
| // to transform the OK response. It will receive one argument -- the Deferred |
| // object returned from this function. |
| // errHandler: |
| // The first error callback to be registered with Deferred. It has the opportunity |
| // to do cleanup on an error. It will receive two arguments: error (the |
| // Error object) and dfd, the Deferred object returned from this function. |
| |
| var ioArgs = {args: args, url: args.url}; |
| |
| //Get values from form if requestd. |
| var formObject = null; |
| if(args.form){ |
| var form = _d.byId(args.form); |
| //IE requires going through getAttributeNode instead of just getAttribute in some form cases, |
| //so use it for all. See #2844 |
| var actnNode = form.getAttributeNode("action"); |
| ioArgs.url = ioArgs.url || (actnNode ? actnNode.value : null); |
| formObject = _d.formToObject(form); |
| } |
| |
| // set up the query params |
| var miArgs = [{}]; |
| |
| if(formObject){ |
| // potentially over-ride url-provided params w/ form values |
| miArgs.push(formObject); |
| } |
| if(args.content){ |
| // stuff in content over-rides what's set by form |
| miArgs.push(args.content); |
| } |
| if(args.preventCache){ |
| miArgs.push({"dojo.preventCache": new Date().valueOf()}); |
| } |
| ioArgs.query = _d.objectToQuery(_d.mixin.apply(null, miArgs)); |
| |
| // .. and the real work of getting the deferred in order, etc. |
| ioArgs.handleAs = args.handleAs || "text"; |
| var d = new _d.Deferred(canceller); |
| d.addCallbacks(okHandler, function(error){ |
| return errHandler(error, d); |
| }); |
| |
| //Support specifying load, error and handle callback functions from the args. |
| //For those callbacks, the "this" object will be the args object. |
| //The callbacks will get the deferred result value as the |
| //first argument and the ioArgs object as the second argument. |
| var ld = args.load; |
| if(ld && _d.isFunction(ld)){ |
| d.addCallback(function(value){ |
| return ld.call(args, value, ioArgs); |
| }); |
| } |
| var err = args.error; |
| if(err && _d.isFunction(err)){ |
| d.addErrback(function(value){ |
| return err.call(args, value, ioArgs); |
| }); |
| } |
| var handle = args.handle; |
| if(handle && _d.isFunction(handle)){ |
| d.addBoth(function(value){ |
| return handle.call(args, value, ioArgs); |
| }); |
| } |
| |
| //Plug in topic publishing, if dojo.publish is loaded. |
| if(cfg.ioPublish && _d.publish && ioArgs.args.ioPublish !== false){ |
| d.addCallbacks( |
| function(res){ |
| _d.publish("/dojo/io/load", [d, res]); |
| return res; |
| }, |
| function(res){ |
| _d.publish("/dojo/io/error", [d, res]); |
| return res; |
| } |
| ); |
| d.addBoth(function(res){ |
| _d.publish("/dojo/io/done", [d, res]); |
| return res; |
| }); |
| } |
| |
| d.ioArgs = ioArgs; |
| |
| // FIXME: need to wire up the xhr object's abort method to something |
| // analagous in the Deferred |
| return d; |
| }; |
| |
| var _deferredCancel = function(/*Deferred*/dfd){ |
| // summary: canceller function for dojo._ioSetArgs call. |
| |
| dfd.canceled = true; |
| var xhr = dfd.ioArgs.xhr; |
| var _at = typeof xhr.abort; |
| if(_at == "function" || _at == "object" || _at == "unknown"){ |
| xhr.abort(); |
| } |
| var err = dfd.ioArgs.error; |
| if(!err){ |
| err = new Error("xhr cancelled"); |
| err.dojoType="cancel"; |
| } |
| return err; |
| }; |
| var _deferredOk = function(/*Deferred*/dfd){ |
| // summary: okHandler function for dojo._ioSetArgs call. |
| |
| var ret = handlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr); |
| return ret === undefined ? null : ret; |
| }; |
| var _deferError = function(/*Error*/error, /*Deferred*/dfd){ |
| // summary: errHandler function for dojo._ioSetArgs call. |
| |
| if(!dfd.ioArgs.args.failOk){ |
| console.error(error); |
| } |
| return error; |
| }; |
| |
| // avoid setting a timer per request. It degrades performance on IE |
| // something fierece if we don't use unified loops. |
| var _inFlightIntvl = null; |
| var _inFlight = []; |
| |
| |
| //Use a separate count for knowing if we are starting/stopping io calls. |
| //Cannot use _inFlight.length since it can change at a different time than |
| //when we want to do this kind of test. We only want to decrement the count |
| //after a callback/errback has finished, since the callback/errback should be |
| //considered as part of finishing a request. |
| var _pubCount = 0; |
| var _checkPubCount = function(dfd){ |
| if(_pubCount <= 0){ |
| _pubCount = 0; |
| if(cfg.ioPublish && _d.publish && (!dfd || dfd && dfd.ioArgs.args.ioPublish !== false)){ |
| _d.publish("/dojo/io/stop"); |
| } |
| } |
| }; |
| |
| var _watchInFlight = function(){ |
| //summary: |
| // internal method that checks each inflight XMLHttpRequest to see |
| // if it has completed or if the timeout situation applies. |
| |
| var now = (new Date()).getTime(); |
| // make sure sync calls stay thread safe, if this callback is called |
| // during a sync call and this results in another sync call before the |
| // first sync call ends the browser hangs |
| if(!_d._blockAsync){ |
| // we need manual loop because we often modify _inFlight (and therefore 'i') while iterating |
| // note: the second clause is an assigment on purpose, lint may complain |
| for(var i = 0, tif; i < _inFlight.length && (tif = _inFlight[i]); i++){ |
| var dfd = tif.dfd; |
| var func = function(){ |
| if(!dfd || dfd.canceled || !tif.validCheck(dfd)){ |
| _inFlight.splice(i--, 1); |
| _pubCount -= 1; |
| }else if(tif.ioCheck(dfd)){ |
| _inFlight.splice(i--, 1); |
| tif.resHandle(dfd); |
| _pubCount -= 1; |
| }else if(dfd.startTime){ |
| //did we timeout? |
| if(dfd.startTime + (dfd.ioArgs.args.timeout || 0) < now){ |
| _inFlight.splice(i--, 1); |
| var err = new Error("timeout exceeded"); |
| err.dojoType = "timeout"; |
| dfd.errback(err); |
| //Cancel the request so the io module can do appropriate cleanup. |
| dfd.cancel(); |
| _pubCount -= 1; |
| } |
| } |
| }; |
| if(dojo.config.debugAtAllCosts){ |
| func.call(this); |
| }else{ |
| try{ |
| func.call(this); |
| }catch(e){ |
| dfd.errback(e); |
| } |
| } |
| } |
| } |
| |
| _checkPubCount(dfd); |
| |
| if(!_inFlight.length){ |
| clearInterval(_inFlightIntvl); |
| _inFlightIntvl = null; |
| return; |
| } |
| }; |
| |
| dojo._ioCancelAll = function(){ |
| //summary: Cancels all pending IO requests, regardless of IO type |
| //(xhr, script, iframe). |
| try{ |
| _d.forEach(_inFlight, function(i){ |
| try{ |
| i.dfd.cancel(); |
| }catch(e){/*squelch*/} |
| }); |
| }catch(e){/*squelch*/} |
| }; |
| |
| //Automatically call cancel all io calls on unload |
| //in IE for trac issue #2357. |
| if(_d.isIE){ |
| _d.addOnWindowUnload(_d._ioCancelAll); |
| } |
| |
| _d._ioNotifyStart = function(/*Deferred*/dfd){ |
| // summary: |
| // If dojo.publish is available, publish topics |
| // about the start of a request queue and/or the |
| // the beginning of request. |
| // description: |
| // Used by IO transports. An IO transport should |
| // call this method before making the network connection. |
| if(cfg.ioPublish && _d.publish && dfd.ioArgs.args.ioPublish !== false){ |
| if(!_pubCount){ |
| _d.publish("/dojo/io/start"); |
| } |
| _pubCount += 1; |
| _d.publish("/dojo/io/send", [dfd]); |
| } |
| }; |
| |
| _d._ioWatch = function(dfd, validCheck, ioCheck, resHandle){ |
| // summary: |
| // Watches the io request represented by dfd to see if it completes. |
| // dfd: Deferred |
| // The Deferred object to watch. |
| // validCheck: Function |
| // Function used to check if the IO request is still valid. Gets the dfd |
| // object as its only argument. |
| // ioCheck: Function |
| // Function used to check if basic IO call worked. Gets the dfd |
| // object as its only argument. |
| // resHandle: Function |
| // Function used to process response. Gets the dfd |
| // object as its only argument. |
| var args = dfd.ioArgs.args; |
| if(args.timeout){ |
| dfd.startTime = (new Date()).getTime(); |
| } |
| |
| _inFlight.push({dfd: dfd, validCheck: validCheck, ioCheck: ioCheck, resHandle: resHandle}); |
| if(!_inFlightIntvl){ |
| _inFlightIntvl = setInterval(_watchInFlight, 50); |
| } |
| // handle sync requests |
| //A weakness: async calls in flight |
| //could have their handlers called as part of the |
| //_watchInFlight call, before the sync's callbacks |
| // are called. |
| if(args.sync){ |
| _watchInFlight(); |
| } |
| }; |
| |
| var _defaultContentType = "application/x-www-form-urlencoded"; |
| |
| var _validCheck = function(/*Deferred*/dfd){ |
| return dfd.ioArgs.xhr.readyState; //boolean |
| }; |
| var _ioCheck = function(/*Deferred*/dfd){ |
| return 4 == dfd.ioArgs.xhr.readyState; //boolean |
| }; |
| var _resHandle = function(/*Deferred*/dfd){ |
| var xhr = dfd.ioArgs.xhr; |
| if(_d._isDocumentOk(xhr)){ |
| dfd.callback(dfd); |
| }else{ |
| var err = new Error("Unable to load " + dfd.ioArgs.url + " status:" + xhr.status); |
| err.status = xhr.status; |
| err.responseText = xhr.responseText; |
| dfd.errback(err); |
| } |
| }; |
| |
| dojo._ioAddQueryToUrl = function(/*dojo.__IoCallbackArgs*/ioArgs){ |
| //summary: Adds query params discovered by the io deferred construction to the URL. |
| //Only use this for operations which are fundamentally GET-type operations. |
| if(ioArgs.query.length){ |
| ioArgs.url += (ioArgs.url.indexOf("?") == -1 ? "?" : "&") + ioArgs.query; |
| ioArgs.query = null; |
| } |
| }; |
| |
| /*===== |
| dojo.declare("dojo.__XhrArgs", dojo.__IoArgs, { |
| constructor: function(){ |
| // summary: |
| // In addition to the properties listed for the dojo._IoArgs type, |
| // the following properties are allowed for dojo.xhr* methods. |
| // handleAs: String? |
| // Acceptable values are: text (default), json, json-comment-optional, |
| // json-comment-filtered, javascript, xml. See `dojo.contentHandlers` |
| // sync: Boolean? |
| // false is default. Indicates whether the request should |
| // be a synchronous (blocking) request. |
| // headers: Object? |
| // Additional HTTP headers to send in the request. |
| // failOk: Boolean? |
| // false is default. Indicates whether a request should be |
| // allowed to fail (and therefore no console error message in |
| // the event of a failure) |
| this.handleAs = handleAs; |
| this.sync = sync; |
| this.headers = headers; |
| this.failOk = failOk; |
| } |
| }); |
| =====*/ |
| |
| dojo.xhr = function(/*String*/ method, /*dojo.__XhrArgs*/ args, /*Boolean?*/ hasBody){ |
| // summary: |
| // Sends an HTTP request with the given method. |
| // description: |
| // Sends an HTTP request with the given method. |
| // See also dojo.xhrGet(), xhrPost(), xhrPut() and dojo.xhrDelete() for shortcuts |
| // for those HTTP methods. There are also methods for "raw" PUT and POST methods |
| // via dojo.rawXhrPut() and dojo.rawXhrPost() respectively. |
| // method: |
| // HTTP method to be used, such as GET, POST, PUT, DELETE. Should be uppercase. |
| // hasBody: |
| // If the request has an HTTP body, then pass true for hasBody. |
| |
| //Make the Deferred object for this xhr request. |
| var dfd = _d._ioSetArgs(args, _deferredCancel, _deferredOk, _deferError); |
| var ioArgs = dfd.ioArgs; |
| |
| //Pass the args to _xhrObj, to allow alternate XHR calls based specific calls, like |
| //the one used for iframe proxies. |
| var xhr = ioArgs.xhr = _d._xhrObj(ioArgs.args); |
| //If XHR factory fails, cancel the deferred. |
| if(!xhr){ |
| dfd.cancel(); |
| return dfd; |
| } |
| |
| //Allow for specifying the HTTP body completely. |
| if("postData" in args){ |
| ioArgs.query = args.postData; |
| }else if("putData" in args){ |
| ioArgs.query = args.putData; |
| }else if("rawBody" in args){ |
| ioArgs.query = args.rawBody; |
| }else if((arguments.length > 2 && !hasBody) || "POST|PUT".indexOf(method.toUpperCase()) == -1){ |
| //Check for hasBody being passed. If no hasBody, |
| //then only append query string if not a POST or PUT request. |
| _d._ioAddQueryToUrl(ioArgs); |
| } |
| |
| // IE 6 is a steaming pile. It won't let you call apply() on the native function (xhr.open). |
| // workaround for IE6's apply() "issues" |
| xhr.open(method, ioArgs.url, args.sync !== true, args.user || undefined, args.password || undefined); |
| if(args.headers){ |
| for(var hdr in args.headers){ |
| if(hdr.toLowerCase() === "content-type" && !args.contentType){ |
| args.contentType = args.headers[hdr]; |
| }else if(args.headers[hdr]){ |
| //Only add header if it has a value. This allows for instnace, skipping |
| //insertion of X-Requested-With by specifying empty value. |
| xhr.setRequestHeader(hdr, args.headers[hdr]); |
| } |
| } |
| } |
| // FIXME: is this appropriate for all content types? |
| xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType); |
| if(!args.headers || !("X-Requested-With" in args.headers)){ |
| xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); |
| } |
| // FIXME: set other headers here! |
| _d._ioNotifyStart(dfd); |
| if(dojo.config.debugAtAllCosts){ |
| xhr.send(ioArgs.query); |
| }else{ |
| try{ |
| xhr.send(ioArgs.query); |
| }catch(e){ |
| ioArgs.error = e; |
| dfd.cancel(); |
| } |
| } |
| _d._ioWatch(dfd, _validCheck, _ioCheck, _resHandle); |
| xhr = null; |
| return dfd; // dojo.Deferred |
| }; |
| |
| dojo.xhrGet = function(/*dojo.__XhrArgs*/ args){ |
| // summary: |
| // Sends an HTTP GET request to the server. |
| return _d.xhr("GET", args); // dojo.Deferred |
| }; |
| |
| dojo.rawXhrPost = dojo.xhrPost = function(/*dojo.__XhrArgs*/ args){ |
| // summary: |
| // Sends an HTTP POST request to the server. In addtion to the properties |
| // listed for the dojo.__XhrArgs type, the following property is allowed: |
| // postData: |
| // String. Send raw data in the body of the POST request. |
| return _d.xhr("POST", args, true); // dojo.Deferred |
| }; |
| |
| dojo.rawXhrPut = dojo.xhrPut = function(/*dojo.__XhrArgs*/ args){ |
| // summary: |
| // Sends an HTTP PUT request to the server. In addtion to the properties |
| // listed for the dojo.__XhrArgs type, the following property is allowed: |
| // putData: |
| // String. Send raw data in the body of the PUT request. |
| return _d.xhr("PUT", args, true); // dojo.Deferred |
| }; |
| |
| dojo.xhrDelete = function(/*dojo.__XhrArgs*/ args){ |
| // summary: |
| // Sends an HTTP DELETE request to the server. |
| return _d.xhr("DELETE", args); //dojo.Deferred |
| }; |
| |
| /* |
| dojo.wrapForm = function(formNode){ |
| //summary: |
| // A replacement for FormBind, but not implemented yet. |
| |
| // FIXME: need to think harder about what extensions to this we might |
| // want. What should we allow folks to do w/ this? What events to |
| // set/send? |
| throw new Error("dojo.wrapForm not yet implemented"); |
| } |
| */ |
| })(); |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.fx"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.fx"] = true; |
| dojo.provide("dojo._base.fx"); |
| |
| |
| |
| |
| |
| |
| /* |
| Animation loosely package based on Dan Pupius' work, contributed under CLA: |
| http://pupius.co.uk/js/Toolkit.Drawing.js |
| */ |
| (function(){ |
| var d = dojo; |
| var _mixin = d._mixin; |
| |
| dojo._Line = function(/*int*/ start, /*int*/ end){ |
| // summary: |
| // dojo._Line is the object used to generate values from a start value |
| // to an end value |
| // start: int |
| // Beginning value for range |
| // end: int |
| // Ending value for range |
| this.start = start; |
| this.end = end; |
| }; |
| |
| dojo._Line.prototype.getValue = function(/*float*/ n){ |
| // summary: Returns the point on the line |
| // n: a floating point number greater than 0 and less than 1 |
| return ((this.end - this.start) * n) + this.start; // Decimal |
| }; |
| |
| dojo.Animation = function(args){ |
| // summary: |
| // A generic animation class that fires callbacks into its handlers |
| // object at various states. |
| // description: |
| // A generic animation class that fires callbacks into its handlers |
| // object at various states. Nearly all dojo animation functions |
| // return an instance of this method, usually without calling the |
| // .play() method beforehand. Therefore, you will likely need to |
| // call .play() on instances of `dojo.Animation` when one is |
| // returned. |
| // args: Object |
| // The 'magic argument', mixing all the properties into this |
| // animation instance. |
| |
| _mixin(this, args); |
| if(d.isArray(this.curve)){ |
| this.curve = new d._Line(this.curve[0], this.curve[1]); |
| } |
| |
| }; |
| |
| // Alias to drop come 2.0: |
| d._Animation = d.Animation; |
| |
| d.extend(dojo.Animation, { |
| // duration: Integer |
| // The time in milliseonds the animation will take to run |
| duration: 350, |
| |
| /*===== |
| // curve: dojo._Line|Array |
| // A two element array of start and end values, or a `dojo._Line` instance to be |
| // used in the Animation. |
| curve: null, |
| |
| // easing: Function? |
| // A Function to adjust the acceleration (or deceleration) of the progress |
| // across a dojo._Line |
| easing: null, |
| =====*/ |
| |
| // repeat: Integer? |
| // The number of times to loop the animation |
| repeat: 0, |
| |
| // rate: Integer? |
| // the time in milliseconds to wait before advancing to next frame |
| // (used as a fps timer: 1000/rate = fps) |
| rate: 20 /* 50 fps */, |
| |
| /*===== |
| // delay: Integer? |
| // The time in milliseconds to wait before starting animation after it |
| // has been .play()'ed |
| delay: null, |
| |
| // beforeBegin: Event? |
| // Synthetic event fired before a dojo.Animation begins playing (synchronous) |
| beforeBegin: null, |
| |
| // onBegin: Event? |
| // Synthetic event fired as a dojo.Animation begins playing (useful?) |
| onBegin: null, |
| |
| // onAnimate: Event? |
| // Synthetic event fired at each interval of a `dojo.Animation` |
| onAnimate: null, |
| |
| // onEnd: Event? |
| // Synthetic event fired after the final frame of a `dojo.Animation` |
| onEnd: null, |
| |
| // onPlay: Event? |
| // Synthetic event fired any time a `dojo.Animation` is play()'ed |
| onPlay: null, |
| |
| // onPause: Event? |
| // Synthetic event fired when a `dojo.Animation` is paused |
| onPause: null, |
| |
| // onStop: Event |
| // Synthetic event fires when a `dojo.Animation` is stopped |
| onStop: null, |
| |
| =====*/ |
| |
| _percent: 0, |
| _startRepeatCount: 0, |
| |
| _getStep: function(){ |
| var _p = this._percent, |
| _e = this.easing |
| ; |
| return _e ? _e(_p) : _p; |
| }, |
| _fire: function(/*Event*/ evt, /*Array?*/ args){ |
| // summary: |
| // Convenience function. Fire event "evt" and pass it the |
| // arguments specified in "args". |
| // description: |
| // Convenience function. Fire event "evt" and pass it the |
| // arguments specified in "args". |
| // Fires the callback in the scope of the `dojo.Animation` |
| // instance. |
| // evt: |
| // The event to fire. |
| // args: |
| // The arguments to pass to the event. |
| var a = args||[]; |
| if(this[evt]){ |
| if(d.config.debugAtAllCosts){ |
| this[evt].apply(this, a); |
| }else{ |
| try{ |
| this[evt].apply(this, a); |
| }catch(e){ |
| // squelch and log because we shouldn't allow exceptions in |
| // synthetic event handlers to cause the internal timer to run |
| // amuck, potentially pegging the CPU. I'm not a fan of this |
| // squelch, but hopefully logging will make it clear what's |
| // going on |
| console.error("exception in animation handler for:", evt); |
| console.error(e); |
| } |
| } |
| } |
| return this; // dojo.Animation |
| }, |
| |
| play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){ |
| // summary: |
| // Start the animation. |
| // delay: |
| // How many milliseconds to delay before starting. |
| // gotoStart: |
| // If true, starts the animation from the beginning; otherwise, |
| // starts it from its current position. |
| // returns: dojo.Animation |
| // The instance to allow chaining. |
| |
| var _t = this; |
| if(_t._delayTimer){ _t._clearTimer(); } |
| if(gotoStart){ |
| _t._stopTimer(); |
| _t._active = _t._paused = false; |
| _t._percent = 0; |
| }else if(_t._active && !_t._paused){ |
| return _t; |
| } |
| |
| _t._fire("beforeBegin", [_t.node]); |
| |
| var de = delay || _t.delay, |
| _p = dojo.hitch(_t, "_play", gotoStart); |
| |
| if(de > 0){ |
| _t._delayTimer = setTimeout(_p, de); |
| return _t; |
| } |
| _p(); |
| return _t; |
| }, |
| |
| _play: function(gotoStart){ |
| var _t = this; |
| if(_t._delayTimer){ _t._clearTimer(); } |
| _t._startTime = new Date().valueOf(); |
| if(_t._paused){ |
| _t._startTime -= _t.duration * _t._percent; |
| } |
| |
| _t._active = true; |
| _t._paused = false; |
| var value = _t.curve.getValue(_t._getStep()); |
| if(!_t._percent){ |
| if(!_t._startRepeatCount){ |
| _t._startRepeatCount = _t.repeat; |
| } |
| _t._fire("onBegin", [value]); |
| } |
| |
| _t._fire("onPlay", [value]); |
| |
| _t._cycle(); |
| return _t; // dojo.Animation |
| }, |
| |
| pause: function(){ |
| // summary: Pauses a running animation. |
| var _t = this; |
| if(_t._delayTimer){ _t._clearTimer(); } |
| _t._stopTimer(); |
| if(!_t._active){ return _t; /*dojo.Animation*/ } |
| _t._paused = true; |
| _t._fire("onPause", [_t.curve.getValue(_t._getStep())]); |
| return _t; // dojo.Animation |
| }, |
| |
| gotoPercent: function(/*Decimal*/ percent, /*Boolean?*/ andPlay){ |
| // summary: |
| // Sets the progress of the animation. |
| // percent: |
| // A percentage in decimal notation (between and including 0.0 and 1.0). |
| // andPlay: |
| // If true, play the animation after setting the progress. |
| var _t = this; |
| _t._stopTimer(); |
| _t._active = _t._paused = true; |
| _t._percent = percent; |
| if(andPlay){ _t.play(); } |
| return _t; // dojo.Animation |
| }, |
| |
| stop: function(/*boolean?*/ gotoEnd){ |
| // summary: Stops a running animation. |
| // gotoEnd: If true, the animation will end. |
| var _t = this; |
| if(_t._delayTimer){ _t._clearTimer(); } |
| if(!_t._timer){ return _t; /* dojo.Animation */ } |
| _t._stopTimer(); |
| if(gotoEnd){ |
| _t._percent = 1; |
| } |
| _t._fire("onStop", [_t.curve.getValue(_t._getStep())]); |
| _t._active = _t._paused = false; |
| return _t; // dojo.Animation |
| }, |
| |
| status: function(){ |
| // summary: |
| // Returns a string token representation of the status of |
| // the animation, one of: "paused", "playing", "stopped" |
| if(this._active){ |
| return this._paused ? "paused" : "playing"; // String |
| } |
| return "stopped"; // String |
| }, |
| |
| _cycle: function(){ |
| var _t = this; |
| if(_t._active){ |
| var curr = new Date().valueOf(); |
| var step = (curr - _t._startTime) / (_t.duration); |
| |
| if(step >= 1){ |
| step = 1; |
| } |
| _t._percent = step; |
| |
| // Perform easing |
| if(_t.easing){ |
| step = _t.easing(step); |
| } |
| |
| _t._fire("onAnimate", [_t.curve.getValue(step)]); |
| |
| if(_t._percent < 1){ |
| _t._startTimer(); |
| }else{ |
| _t._active = false; |
| |
| if(_t.repeat > 0){ |
| _t.repeat--; |
| _t.play(null, true); |
| }else if(_t.repeat == -1){ |
| _t.play(null, true); |
| }else{ |
| if(_t._startRepeatCount){ |
| _t.repeat = _t._startRepeatCount; |
| _t._startRepeatCount = 0; |
| } |
| } |
| _t._percent = 0; |
| _t._fire("onEnd", [_t.node]); |
| !_t.repeat && _t._stopTimer(); |
| } |
| } |
| return _t; // dojo.Animation |
| }, |
| |
| _clearTimer: function(){ |
| // summary: Clear the play delay timer |
| clearTimeout(this._delayTimer); |
| delete this._delayTimer; |
| } |
| |
| }); |
| |
| // the local timer, stubbed into all Animation instances |
| var ctr = 0, |
| timer = null, |
| runner = { |
| run: function(){} |
| }; |
| |
| d.extend(d.Animation, { |
| |
| _startTimer: function(){ |
| if(!this._timer){ |
| this._timer = d.connect(runner, "run", this, "_cycle"); |
| ctr++; |
| } |
| if(!timer){ |
| timer = setInterval(d.hitch(runner, "run"), this.rate); |
| } |
| }, |
| |
| _stopTimer: function(){ |
| if(this._timer){ |
| d.disconnect(this._timer); |
| this._timer = null; |
| ctr--; |
| } |
| if(ctr <= 0){ |
| clearInterval(timer); |
| timer = null; |
| ctr = 0; |
| } |
| } |
| |
| }); |
| |
| var _makeFadeable = |
| d.isIE ? function(node){ |
| // only set the zoom if the "tickle" value would be the same as the |
| // default |
| var ns = node.style; |
| // don't set the width to auto if it didn't already cascade that way. |
| // We don't want to f anyones designs |
| if(!ns.width.length && d.style(node, "width") == "auto"){ |
| ns.width = "auto"; |
| } |
| } : |
| function(){}; |
| |
| dojo._fade = function(/*Object*/ args){ |
| // summary: |
| // Returns an animation that will fade the node defined by |
| // args.node from the start to end values passed (args.start |
| // args.end) (end is mandatory, start is optional) |
| |
| args.node = d.byId(args.node); |
| var fArgs = _mixin({ properties: {} }, args), |
| props = (fArgs.properties.opacity = {}); |
| |
| props.start = !("start" in fArgs) ? |
| function(){ |
| return +d.style(fArgs.node, "opacity")||0; |
| } : fArgs.start; |
| props.end = fArgs.end; |
| |
| var anim = d.animateProperty(fArgs); |
| d.connect(anim, "beforeBegin", d.partial(_makeFadeable, fArgs.node)); |
| |
| return anim; // dojo.Animation |
| }; |
| |
| /*===== |
| dojo.__FadeArgs = function(node, duration, easing){ |
| // node: DOMNode|String |
| // The node referenced in the animation |
| // duration: Integer? |
| // Duration of the animation in milliseconds. |
| // easing: Function? |
| // An easing function. |
| this.node = node; |
| this.duration = duration; |
| this.easing = easing; |
| } |
| =====*/ |
| |
| dojo.fadeIn = function(/*dojo.__FadeArgs*/ args){ |
| // summary: |
| // Returns an animation that will fade node defined in 'args' from |
| // its current opacity to fully opaque. |
| return d._fade(_mixin({ end: 1 }, args)); // dojo.Animation |
| }; |
| |
| dojo.fadeOut = function(/*dojo.__FadeArgs*/ args){ |
| // summary: |
| // Returns an animation that will fade node defined in 'args' |
| // from its current opacity to fully transparent. |
| return d._fade(_mixin({ end: 0 }, args)); // dojo.Animation |
| }; |
| |
| dojo._defaultEasing = function(/*Decimal?*/ n){ |
| // summary: The default easing function for dojo.Animation(s) |
| return 0.5 + ((Math.sin((n + 1.5) * Math.PI)) / 2); |
| }; |
| |
| var PropLine = function(properties){ |
| // PropLine is an internal class which is used to model the values of |
| // an a group of CSS properties across an animation lifecycle. In |
| // particular, the "getValue" function handles getting interpolated |
| // values between start and end for a particular CSS value. |
| this._properties = properties; |
| for(var p in properties){ |
| var prop = properties[p]; |
| if(prop.start instanceof d.Color){ |
| // create a reusable temp color object to keep intermediate results |
| prop.tempColor = new d.Color(); |
| } |
| } |
| }; |
| |
| PropLine.prototype.getValue = function(r){ |
| var ret = {}; |
| for(var p in this._properties){ |
| var prop = this._properties[p], |
| start = prop.start; |
| if(start instanceof d.Color){ |
| ret[p] = d.blendColors(start, prop.end, r, prop.tempColor).toCss(); |
| }else if(!d.isArray(start)){ |
| ret[p] = ((prop.end - start) * r) + start + (p != "opacity" ? prop.units || "px" : 0); |
| } |
| } |
| return ret; |
| }; |
| |
| /*===== |
| dojo.declare("dojo.__AnimArgs", [dojo.__FadeArgs], { |
| // Properties: Object? |
| // A hash map of style properties to Objects describing the transition, |
| // such as the properties of dojo._Line with an additional 'units' property |
| properties: {} |
| |
| //TODOC: add event callbacks |
| }); |
| =====*/ |
| |
| dojo.animateProperty = function(/*dojo.__AnimArgs*/ args){ |
| // summary: |
| // Returns an animation that will transition the properties of |
| // node defined in `args` depending how they are defined in |
| // `args.properties` |
| // |
| // description: |
| // `dojo.animateProperty` is the foundation of most `dojo.fx` |
| // animations. It takes an object of "properties" corresponding to |
| // style properties, and animates them in parallel over a set |
| // duration. |
| // |
| // example: |
| // A simple animation that changes the width of the specified node. |
| // | dojo.animateProperty({ |
| // | node: "nodeId", |
| // | properties: { width: 400 }, |
| // | }).play(); |
| // Dojo figures out the start value for the width and converts the |
| // integer specified for the width to the more expressive but |
| // verbose form `{ width: { end: '400', units: 'px' } }` which you |
| // can also specify directly. Defaults to 'px' if ommitted. |
| // |
| // example: |
| // Animate width, height, and padding over 2 seconds... the |
| // pedantic way: |
| // | dojo.animateProperty({ node: node, duration:2000, |
| // | properties: { |
| // | width: { start: '200', end: '400', units:"px" }, |
| // | height: { start:'200', end: '400', units:"px" }, |
| // | paddingTop: { start:'5', end:'50', units:"px" } |
| // | } |
| // | }).play(); |
| // Note 'paddingTop' is used over 'padding-top'. Multi-name CSS properties |
| // are written using "mixed case", as the hyphen is illegal as an object key. |
| // |
| // example: |
| // Plug in a different easing function and register a callback for |
| // when the animation ends. Easing functions accept values between |
| // zero and one and return a value on that basis. In this case, an |
| // exponential-in curve. |
| // | dojo.animateProperty({ |
| // | node: "nodeId", |
| // | // dojo figures out the start value |
| // | properties: { width: { end: 400 } }, |
| // | easing: function(n){ |
| // | return (n==0) ? 0 : Math.pow(2, 10 * (n - 1)); |
| // | }, |
| // | onEnd: function(node){ |
| // | // called when the animation finishes. The animation |
| // | // target is passed to this function |
| // | } |
| // | }).play(500); // delay playing half a second |
| // |
| // example: |
| // Like all `dojo.Animation`s, animateProperty returns a handle to the |
| // Animation instance, which fires the events common to Dojo FX. Use `dojo.connect` |
| // to access these events outside of the Animation definiton: |
| // | var anim = dojo.animateProperty({ |
| // | node:"someId", |
| // | properties:{ |
| // | width:400, height:500 |
| // | } |
| // | }); |
| // | dojo.connect(anim,"onEnd", function(){ |
| // | console.log("animation ended"); |
| // | }); |
| // | // play the animation now: |
| // | anim.play(); |
| // |
| // example: |
| // Each property can be a function whose return value is substituted along. |
| // Additionally, each measurement (eg: start, end) can be a function. The node |
| // reference is passed direcly to callbacks. |
| // | dojo.animateProperty({ |
| // | node:"mine", |
| // | properties:{ |
| // | height:function(node){ |
| // | // shrink this node by 50% |
| // | return dojo.position(node).h / 2 |
| // | }, |
| // | width:{ |
| // | start:function(node){ return 100; }, |
| // | end:function(node){ return 200; } |
| // | } |
| // | } |
| // | }).play(); |
| // |
| |
| var n = args.node = d.byId(args.node); |
| if(!args.easing){ args.easing = d._defaultEasing; } |
| |
| var anim = new d.Animation(args); |
| d.connect(anim, "beforeBegin", anim, function(){ |
| var pm = {}; |
| for(var p in this.properties){ |
| // Make shallow copy of properties into pm because we overwrite |
| // some values below. In particular if start/end are functions |
| // we don't want to overwrite them or the functions won't be |
| // called if the animation is reused. |
| if(p == "width" || p == "height"){ |
| this.node.display = "block"; |
| } |
| var prop = this.properties[p]; |
| if(d.isFunction(prop)){ |
| prop = prop(n); |
| } |
| prop = pm[p] = _mixin({}, (d.isObject(prop) ? prop: { end: prop })); |
| |
| if(d.isFunction(prop.start)){ |
| prop.start = prop.start(n); |
| } |
| if(d.isFunction(prop.end)){ |
| prop.end = prop.end(n); |
| } |
| var isColor = (p.toLowerCase().indexOf("color") >= 0); |
| function getStyle(node, p){ |
| // dojo.style(node, "height") can return "auto" or "" on IE; this is more reliable: |
| var v = { height: node.offsetHeight, width: node.offsetWidth }[p]; |
| if(v !== undefined){ return v; } |
| v = d.style(node, p); |
| return (p == "opacity") ? +v : (isColor ? v : parseFloat(v)); |
| } |
| if(!("end" in prop)){ |
| prop.end = getStyle(n, p); |
| }else if(!("start" in prop)){ |
| prop.start = getStyle(n, p); |
| } |
| |
| if(isColor){ |
| prop.start = new d.Color(prop.start); |
| prop.end = new d.Color(prop.end); |
| }else{ |
| prop.start = (p == "opacity") ? +prop.start : parseFloat(prop.start); |
| } |
| } |
| this.curve = new PropLine(pm); |
| }); |
| d.connect(anim, "onAnimate", d.hitch(d, "style", anim.node)); |
| return anim; // dojo.Animation |
| }; |
| |
| dojo.anim = function( /*DOMNode|String*/ node, |
| /*Object*/ properties, |
| /*Integer?*/ duration, |
| /*Function?*/ easing, |
| /*Function?*/ onEnd, |
| /*Integer?*/ delay){ |
| // summary: |
| // A simpler interface to `dojo.animateProperty()`, also returns |
| // an instance of `dojo.Animation` but begins the animation |
| // immediately, unlike nearly every other Dojo animation API. |
| // description: |
| // `dojo.anim` is a simpler (but somewhat less powerful) version |
| // of `dojo.animateProperty`. It uses defaults for many basic properties |
| // and allows for positional parameters to be used in place of the |
| // packed "property bag" which is used for other Dojo animation |
| // methods. |
| // |
| // The `dojo.Animation` object returned from `dojo.anim` will be |
| // already playing when it is returned from this function, so |
| // calling play() on it again is (usually) a no-op. |
| // node: |
| // a DOM node or the id of a node to animate CSS properties on |
| // duration: |
| // The number of milliseconds over which the animation |
| // should run. Defaults to the global animation default duration |
| // (350ms). |
| // easing: |
| // An easing function over which to calculate acceleration |
| // and deceleration of the animation through its duration. |
| // A default easing algorithm is provided, but you may |
| // plug in any you wish. A large selection of easing algorithms |
| // are available in `dojo.fx.easing`. |
| // onEnd: |
| // A function to be called when the animation finishes |
| // running. |
| // delay: |
| // The number of milliseconds to delay beginning the |
| // animation by. The default is 0. |
| // example: |
| // Fade out a node |
| // | dojo.anim("id", { opacity: 0 }); |
| // example: |
| // Fade out a node over a full second |
| // | dojo.anim("id", { opacity: 0 }, 1000); |
| return d.animateProperty({ // dojo.Animation |
| node: node, |
| duration: duration || d.Animation.prototype.duration, |
| properties: properties, |
| easing: easing, |
| onEnd: onEnd |
| }).play(delay || 0); |
| }; |
| })(); |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base.browser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base.browser"] = true; |
| dojo.provide("dojo._base.browser"); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| //Need this to be the last code segment in base, so do not place any |
| //dojo/requireIf calls in this file/ Otherwise, due to how the build system |
| //puts all requireIf dependencies after the current file, the require calls |
| //could be called before all of base is defined/ |
| dojo.forEach(dojo.config.require, function(i){ |
| dojo["require"](i); |
| }); |
| |
| } |
| |
| if(!dojo._hasResource["dojo._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
| dojo._hasResource["dojo._base"] = true; |
| dojo.provide("dojo._base"); |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| } |
| |
| //INSERT dojo.i18n._preloadLocalizations HERE |
| |
| //Check if document already complete, and if so, just trigger page load |
| //listeners. NOTE: does not work with Firefox before 3.6. To support |
| //those browsers, set djConfig.afterOnLoad = true when you know Dojo is added |
| //after page load. Using a timeout so the rest of this |
| //script gets evaluated properly. This work needs to happen after the |
| //dojo.config.require work done in dojo._base. |
| if(dojo.isBrowser && (document.readyState === "complete" || dojo.config.afterOnLoad)){ |
| window.setTimeout(dojo._loadInit, 100); |
| } |
| })(); |
| |