You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
11252 lines
368 KiB
JavaScript
11252 lines
368 KiB
JavaScript
/*
|
|
Copyright (c) 2004-2010, 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
|
|
};
|
|
|
|
if(typeof djConfig != "undefined"){
|
|
for(var opt in djConfig){
|
|
d.config[opt] = djConfig[opt];
|
|
}
|
|
}
|
|
|
|
/*=====
|
|
// Override locale setting, if specified
|
|
dojo.locale = {
|
|
// summary: the locale as defined by Dojo (read-only)
|
|
};
|
|
=====*/
|
|
dojo.locale = d.config.locale;
|
|
|
|
var rev = "$Rev: 22487 $".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: 5, patch: 0, 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 only for object and method detection.
|
|
// Not useful for testing generic properties on an object.
|
|
// In particular, dojo.exists("foo.bar") when foo.bar = ""
|
|
// will return false. Use ("bar" in foo) to test for those cases.
|
|
// 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); // 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;
|
|
|
|
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{
|
|
return !module ? d._loadUri(uri, cb) : d._loadUriAndCheck(uri, module, cb); // Boolean
|
|
}catch(e){
|
|
console.error(e);
|
|
return false; // Boolean
|
|
}
|
|
}
|
|
|
|
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){
|
|
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:
|
|
// 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:
|
|
// 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. Example:
|
|
//
|
|
// | <script type="text/javascript">
|
|
// | dojo.require("foo");
|
|
// | dojo.require("bar");
|
|
// | dojo.addOnLoad(function(){
|
|
// | //you can now safely do something with foo and bar
|
|
// | });
|
|
// | </script>
|
|
//
|
|
// 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 excpetion 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. 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");
|
|
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 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){
|
|
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
|
|
}
|
|
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){ throw new Error("declare: " + msg); }
|
|
|
|
// C3 Method Resolution Order (see http://www.python.org/download/releases/2.3/mro/)
|
|
function c3mro(bases){
|
|
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?");
|
|
}else if(opts.call(base) != "[object Function]"){
|
|
err("mixin #" + i + " is not a callable constructor.");
|
|
}
|
|
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");
|
|
}
|
|
|
|
// 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()");
|
|
}
|
|
|
|
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);
|
|
}
|
|
// 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");
|
|
}
|
|
// 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);
|
|
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.");
|
|
}
|
|
}else if(superclass !== null){
|
|
err("unknown base class. Did you use dojo.require to pull it in?")
|
|
}
|
|
}
|
|
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
|
|
var r = t && t.apply(this, arguments);
|
|
// make local copy of listener array so it is immutable during processing
|
|
var i, lls;
|
|
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 promisesthemselves, 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 syncrhonous 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;
|
|
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"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
|
|
dojo._hasResource["dojo._base"] = true;
|
|
dojo.provide("dojo._base");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
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);
|
|
var oname = name;
|
|
if(
|
|
!dojo.isIE &&
|
|
(name == "mouseenter" || name == "mouseleave")
|
|
){
|
|
var ofp = fp;
|
|
//oname = name;
|
|
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 ? 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){
|
|
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;
|
|
}
|
|
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 && 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);
|
|
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 = function(evt){
|
|
evt = evt || window.event;
|
|
del._stopPropagation.call(evt);
|
|
del._preventDefault.call(evt);
|
|
}
|
|
}
|
|
|
|
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.isOpera){
|
|
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
|
|
return (typeof id == "string") ? (doc || dojo.doc).getElementById(id) : id; // 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 = node.charAt(0) == "<" ? 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 ? 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 ? 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._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.doc.documentElement, n.clientHeight? { x:d._fixIeBiDiScrollLeft(n.scrollLeft), y:n.scrollTop } :
|
|
(n=d.body(), { x: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 < 8
|
|
// 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 dd = d.doc;
|
|
if(d.isIE < 8 && !d._isBodyLtr()){
|
|
var de = d.isQuirks ? dd.body : dd.documentElement;
|
|
return scrollLeft + de.clientWidth - de.scrollWidth; // 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.
|
|
|
|
var db = d.body(), dh = db.parentNode, ret;
|
|
node = byId(node);
|
|
if(node["getBoundingClientRect"]){
|
|
// IE6+, FF3+, super-modern WebKit, and Opera 9.6+ all take this branch
|
|
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);
|
|
}
|
|
}else{
|
|
// FF2 and older WebKit
|
|
ret = {
|
|
x: 0,
|
|
y: 0,
|
|
w: node.offsetWidth,
|
|
h: node.offsetHeight
|
|
};
|
|
if(node["offsetParent"]){
|
|
ret.x -= _sumAncestorProperties(node, "scrollLeft");
|
|
ret.y -= _sumAncestorProperties(node, "scrollTop");
|
|
|
|
var curnode = node;
|
|
do{
|
|
var n = curnode.offsetLeft,
|
|
t = curnode.offsetTop;
|
|
ret.x += isNaN(n) ? 0 : n;
|
|
ret.y += isNaN(t) ? 0 : t;
|
|
|
|
cs = gcs(curnode);
|
|
if(curnode != node){
|
|
if(d.isMoz){
|
|
// tried left+right with differently sized left/right borders
|
|
// it really is 2xleft border in FF, not left+right, even in RTL!
|
|
ret.x += 2 * px(curnode,cs.borderLeftWidth);
|
|
ret.y += 2 * px(curnode,cs.borderTopWidth);
|
|
}else{
|
|
ret.x += px(curnode, cs.borderLeftWidth);
|
|
ret.y += px(curnode, cs.borderTopWidth);
|
|
}
|
|
}
|
|
// static children in a static div in FF2 are affected by the div's border as well
|
|
// but offsetParent will skip this div!
|
|
if(d.isMoz && cs.position=="static"){
|
|
var parent=curnode.parentNode;
|
|
while(parent!=curnode.offsetParent){
|
|
var pcs=gcs(parent);
|
|
if(pcs.position=="static"){
|
|
ret.x += px(curnode,pcs.borderLeftWidth);
|
|
ret.y += px(curnode,pcs.borderTopWidth);
|
|
}
|
|
parent=parent.parentNode;
|
|
}
|
|
}
|
|
curnode = curnode.offsetParent;
|
|
}while((curnode != dh) && curnode);
|
|
}else if(node.x && node.y){
|
|
ret.x += isNaN(node.x) ? 0 : node.x;
|
|
ret.y += isNaN(node.y) ? 0 : node.y;
|
|
}
|
|
}
|
|
// account for document scrolling
|
|
// if offsetParent is used, ret value already includes scroll position
|
|
// so we may have to actually remove that value if !includeScroll
|
|
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){
|
|
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 = [""],
|
|
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.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", "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 utiltity 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 empy 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 loction 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 loction 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 retreived 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?*/ simpleFilter){
|
|
// summary:
|
|
// removes elements in this list that match the simple filter
|
|
// from their parents and returns them as a new NodeList.
|
|
// simpleFilter:
|
|
// single-expression CSS rule. For example, ".thinger" or
|
|
// "#someId[attrName='value']" but not "div > span". In short,
|
|
// anything which does not invoke a descent to evaluate but
|
|
// can instead be used to test a single node is acceptable.
|
|
// returns:
|
|
// `dojo.NodeList` containing the orpahned elements
|
|
return (simpleFilter ? d._filterQueryResult(this, simpleFilter) : 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 memebers 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 commedians may not be funny <span>in person</span></p>
|
|
// | </div>
|
|
// If we are presented with the following defintion 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*/ simpleFilter){
|
|
// 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.
|
|
// simpleFilter:
|
|
// If a string, a single-expression CSS rule. For example,
|
|
// ".thinger" or "#someId[attrName='value']" but not "div >
|
|
// span". In short, anything which does not invoke a descent
|
|
// to evaluate but can instead be used to test a single node
|
|
// is acceptable.
|
|
// 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 simpleFilter == "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 hte only child)
|
|
// or an offset in the childNodes property
|
|
// example:
|
|
// appends content to the end if the position is ommitted
|
|
// | 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;
|
|
if(typeof dojo != "undefined"){
|
|
dojo.provide("dojo._base.query");
|
|
|
|
|
|
|
|
}
|
|
|
|
/*
|
|
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)
|
|
*/
|
|
|
|
;(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 extracing 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) charachter 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 readibility 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 durring 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, charachter by charachter, 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 charachter (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 existance 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 pseduo-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 possed, 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 calcuate 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:
|
|
// shortcuting 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) ? 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 generatd 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 memozied 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
|
|
// founde, 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 immediately preceeded-by sibling selector
|
|
// * `+`, the preceeded-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 resaonable 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;
|
|
|
|
// one-off function for filtering a NodeList based on a simple selector
|
|
d._filterQueryResult = function(nodeList, simpleFilter){
|
|
var tmpNodeList = new d._NodeListCtor();
|
|
var filterFunc = getSimpleFilterFunc(getQueryParts(simpleFilter)[0]);
|
|
for(var x = 0, te; te = nodeList[x]; x++){
|
|
if(filterFunc(te)){ tmpNodeList.push(te); }
|
|
}
|
|
return tmpNodeList;
|
|
}
|
|
})(this["queryPortability"]||this["acme"]||dojo);
|
|
|
|
/*
|
|
*/
|
|
|
|
}
|
|
|
|
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);
|
|
});
|
|
|
|
}
|
|
|
|
//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);
|
|
}
|
|
})();
|
|
|