/* Copyright (c) 2004-2009, 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 a compiled version of Dojo, built for deployment and not for development. To get an editable version, please visit: http://dojotoolkit.org for documentation and information on getting the source. */ ;(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 // ` // | 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 // // 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("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"); var 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; var _a = arguments; var 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]+""); var 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"); // | // 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); // String } })(); /*===== 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 } =====*/ 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; // 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] || dua.split("Shiretoko/")[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, for a previous //version. Make sure isIE reflects the desired version. //document.documentMode of 5 means quirks mode. if(d.isIE >= 8 && document.documentMode != 5){ d.isIE = document.documentMode; } } //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; } var cm = document.compatMode; d.isQuirks = cm == "BackCompat" || cm == "QuirksMode" || d.isIE < 6; // 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; return (stat >= 200 && stat < 300) || // Boolean stat == 304 || // allow any 2XX response code stat == 1223 || // get it out of the cache (!stat && (location.protocol=="file:" || location.protocol=="chrome:") ); // Internet Explorer mangled the status code } //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 = this._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 oldHandler = _w[evtName] || function(){}; _w[evtName] = function(){ fp.apply(_w, arguments); oldHandler.apply(_w, arguments); }; }; 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())(); } }; 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); } }; })(); dojo._initFired = false; // BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/) dojo._loadInit = function(e){ dojo._initFired = true; // allow multiple calls, only first one will take effect // A bug in khtml calls events callbacks for document for event which isnt supported // for example a created contextmenu event calls DOMContentLoaded, workaround var type = e && e.type ? e.type.toLowerCase() : "load"; if(arguments.callee.initialized || (type != "domcontentloaded" && type != "load")){ return; } arguments.callee.initialized = true; if("_khtmlTimer" in dojo){ clearInterval(dojo._khtmlTimer); delete dojo._khtmlTimer; } if(dojo._inFlightCount == 0){ dojo._modulesLoaded(); } } if(!dojo.config.afterOnLoad){ // START DOMContentLoaded // Mozilla and Opera 9 expose the event we could use if(document.addEventListener){ // NOTE: // due to a threading issue in Firefox 2.0, we can't enable // DOMContentLoaded on that platform. For more information, see: // http://trac.dojotoolkit.org/ticket/1704 if(dojo.isWebKit > 525 || dojo.isOpera || dojo.isFF >= 3 || (dojo.isMoz && dojo.config.enableMozDomContentLoaded === true)){ document.addEventListener("DOMContentLoaded", dojo._loadInit, null); } // mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already. // also used for Mozilla because of trac #1640 window.addEventListener("load", dojo._loadInit, null); } if(dojo.isAIR){ window.addEventListener("load", dojo._loadInit, null); }else if((dojo.isWebKit < 525) || dojo.isKhtml){ dojo._khtmlTimer = setInterval(function(){ if(/loaded|complete/.test(document.readyState)){ dojo._loadInit(); // call the onload handler } }, 10); } // END DOMContentLoaded } if(dojo.isIE){ // for Internet Explorer. readyState will not be achieved on init // call, but dojo doesn't need it however, we'll include it // because we don't know if there are other functions added that // might. Note that this has changed because the build process // strips all comments -- including conditional ones. if(!dojo.config.afterOnLoad){ document.write('' + '' ); } try{ document.namespaces.add("v","urn:schemas-microsoft-com:vml"); document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML); display:inline-block"); }catch(e){} } /* 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"); // Crockford (ish) functions dojo.isString = function(/*anything*/ it){ // summary: // Return true if it is a String return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean } dojo.isArray = function(/*anything*/ it){ // summary: // Return true if it is an Array return it && (it instanceof Array || typeof it == "array"); // Boolean } /*===== dojo.isFunction = function(it){ // summary: Return true if it is a Function // it: anything return; // Boolean } =====*/ dojo.isFunction = (function(){ var _isFunction = function(/*anything*/ it){ var t = typeof it; // must evaluate separately due to bizarre Opera bug. See #8937 return it && (t == "function" || it instanceof Function); // Boolean }; return dojo.isSafari ? // only slow this down w/ gratuitious casting in Safari (not WebKit) function(/*anything*/ it){ if(typeof it == "function" && it == "[object NodeList]"){ return false; } return _isFunction(it); // Boolean } : _isFunction; })(); 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" || dojo.isArray(it) || dojo.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` var d = dojo; 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 && !dojo.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 2){ return dojo._hitchArgs.apply(dojo, arguments); // Function } if(!method){ method = scope; scope = null; } if(dojo.isString(method)){ scope = scope || dojo.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(); if(props){ dojo._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. } =====*/ (function(){ 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 = dojo.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 dojo.hitch.apply(dojo, arr.concat(dojo._toArray(arguments))); // Function } dojo.clone = function(/*anything*/ o){ // summary: // Clones objects (including DOM nodes) and all children. // Warning: do not clone cyclic structures. if(!o){ return o; } if(dojo.isArray(o)){ var r = []; for(var i = 0; i < o.length; ++i){ r.push(dojo.clone(o[i])); } return r; // Array } if(!dojo.isObject(o)){ return o; /*anything*/ } if(o.nodeType && o.cloneNode){ // isNode return o.cloneNode(true); // Node } if(o instanceof Date){ return new Date(o.getTime()); // Date } // Generic objects r = new o.constructor(); // specific to dojo.declare()'d classes! for(i in o){ if(!(i in r) || r[i] != o[i]){ r[i] = dojo.clone(o[i]); } } 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*$/, ''); }; } 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"); // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA dojo.declare = function(/*String*/ className, /*Function|Function[]*/ superclass, /*Object*/ props){ // summary: // Create a feature-rich constructor from compact notation // className: // The name of the constructor (loosely, a "class") // stored in the "declaredClass" property in the created prototype // superclass: // May be null, a Function, or an Array of Functions. If an array, // the first element is used as the prototypical ancestor and // any following Functions become mixin ancestors. // props: // An object whose properties are copied to the // created prototype. // Add an instance-initialization function by making it a property // named "constructor". // description: // Create a constructor using a compact notation for inheritance and // prototype extension. // // All superclasses (including mixins) must be Functions (not simple Objects). // // 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. // // "className" is cached in "declaredClass" property of the new class. // // 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(); // | } // | ); // process superclass argument var dd = arguments.callee, mixins; if(dojo.isArray(superclass)){ mixins = superclass; superclass = mixins.shift(); } // construct intermediate classes for mixins if(mixins){ dojo.forEach(mixins, function(m, i){ if(!m){ throw(className + ": mixin #" + i + " is null"); } // It's likely a required module is not loaded superclass = dd._delegate(superclass, m); }); } // create constructor var ctor = dd._delegate(superclass); // extend with "props" props = props || {}; ctor.extend(props); // more prototype decoration dojo.extend(ctor, {declaredClass: className, _constructor: props.constructor/*, preamble: null*/}); // special help for IE ctor.prototype.constructor = ctor; // create named reference return dojo.setObject(className, ctor); // Function }; dojo.mixin(dojo.declare, { _delegate: function(base, mixin){ var bp = (base||0).prototype, mp = (mixin||0).prototype, dd=dojo.declare; // fresh constructor, fresh prototype var ctor = dd._makeCtor(); // cache ancestry dojo.mixin(ctor, {superclass: bp, mixin: mp, extend: dd._extend}); // chain prototypes if(base){ctor.prototype = dojo._delegate(bp);} // add mixin and core dojo.extend(ctor, dd._core, mp||0, {_constructor: null, preamble: null}); // special help for IE ctor.prototype.constructor = ctor; // name this class for debugging ctor.prototype.declaredClass = (bp||0).declaredClass + '_' + (mp||0).declaredClass; return ctor; }, _extend: function(props){ var i, fn; for(i in props){ if(dojo.isFunction(fn=props[i]) && !0[i]){fn.nom=i;fn.ctor=this;} } dojo.extend(this, props); }, _makeCtor: function(){ // we have to make a function, but don't want to close over anything return function(){ this._construct(arguments); }; }, _core: { _construct: function(args){ var c=args.callee, s=c.superclass, ct=s&&s.constructor, m=c.mixin, mct=m&&m.constructor, a=args, ii, fn; // side-effect of = used on purpose here, lint may complain, don't try this at home if(a[0]){ // FIXME: preambles for each mixin should be allowed // FIXME: // should we allow the preamble here NOT to modify the // default args, but instead to act on each mixin // independently of the class instance being constructed // (for impedence matching)? // allow any first argument w/ a "preamble" property to act as a // class preamble (not exclusive of the prototype preamble) if(/*dojo.isFunction*/((fn = a[0].preamble))){ a = fn.apply(this, a) || a; } } // prototype preamble if((fn = c.prototype.preamble)){a = fn.apply(this, a) || a;} // FIXME: // need to provide an optional prototype-settable // "_explicitSuper" property which disables this // initialize superclass if(ct&&ct.apply){ct.apply(this, a);} // initialize mixin if(mct&&mct.apply){mct.apply(this, a);} // initialize self if((ii=c.prototype._constructor)){ii.apply(this, args);} // post construction if(this.constructor.prototype==c.prototype && (ct=this.postscript)){ ct.apply(this, args); } }, _findMixin: function(mixin){ var c = this.constructor, p, m; while(c){ p = c.superclass; m = c.mixin; if(m==mixin || (m instanceof mixin.constructor)){return p;} if(m && m._findMixin && (m=m._findMixin(mixin))){return m;} c = p && p.constructor; } }, _findMethod: function(name, method, ptype, has){ // consciously trading readability for bytes and speed in this low-level method var p=ptype, c, m, f; do{ c = p.constructor; m = c.mixin; // find method by name in our mixin ancestor if(m && (m=this._findMethod(name, method, m, has))){return m;} // if we found a named method that either exactly-is or exactly-is-not 'method' if((f=p[name])&&(has==(f==method))){return p;} // ascend chain p = c.superclass; }while(p); // if we couldn't find an ancestor in our primary chain, try a mixin chain return !has && (p=this._findMixin(ptype)) && this._findMethod(name, method, p, has); }, inherited: function(name, args, newArgs){ // optionalize name argument var a = arguments; if(!dojo.isString(a[0])){newArgs=args; args=name; name=args.callee.nom;} a = newArgs||args; var c = args.callee, p = this.constructor.prototype, fn, mp; // if not an instance override if(this[name] != c || p[name] == c){ // start from memoized prototype, or // find a prototype that has property 'name' == 'c' mp = (c.ctor||0).superclass || this._findMethod(name, c, p, true); if(!mp){throw(this.declaredClass + ': inherited method "' + name + '" mismatch');} // find a prototype that has property 'name' != 'c' p = this._findMethod(name, c, mp, false); } // we expect 'name' to be in prototype 'p' fn = p && p[name]; if(!fn){throw(mp.declaredClass + ': inherited method "' + name + '" not found');} // if the function exists, invoke it in our scope return fn.apply(this, a); } } }); } 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 lls; lls = [].concat(ls); // invoke listeners after target function for(var 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: // Create a link that calls one function when another executes. // // description: // Connects method to event, so that after event fires, method // does too. All connected functions are passed the same arguments as // the event function was initially called with. You may connect as // many methods to event as needed. // // event must be a string. If obj is null, dojo.global is used. // // null arguments may simply be omitted. // // obj[event] can resolve to a function or undefined (null). // If obj[event] is null, it is assigned a function. // // 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 ommited 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. // description: // JavaScript has no threads, and even if it did, threads are hard. // Deferreds are a way of abstracting non-blocking events, such as the // final response to an XMLHttpRequest. Deferreds create a promise to // return a response a some point in the future and an easy way to // register your interest in receiving that response. // // The most important methods for Deffered users are: // // * addCallback(handler) // * addErrback(handler) // * callback(result) // * errback(result) // // In general, when a function returns a Deferred, users then "fill // in" the second half of the contract by registering callbacks and // error handlers. You may register as many callback and errback // handlers as you like and they will be executed in the order // registered when a result is provided. Usually this result is // provided as the result of an asynchronous operation. The code // "managing" the Deferred (the code that made the promise to provide // an answer later) will use the callback() and errback() methods to // communicate with registered listeners about the result of the // operation. At this time, all registered result handlers are called // *with the most recent result value*. // // Deferred callback handlers are treated as a chain, and each item in // the chain is required to return a value that will be fed into // successive handlers. The most minimal callback may be registered // like this: // // | var d = new dojo.Deferred(); // | d.addCallback(function(result){ return result; }); // // Perhaps the most common mistake when first using Deferreds is to // forget to return a value (in most cases, the value you were // passed). // // The sequence of callbacks is internally represented as a list of // 2-tuples containing the callback/errback pair. For example, the // following call sequence: // // | var d = new dojo.Deferred(); // | d.addCallback(myCallback); // | d.addErrback(myErrback); // | d.addBoth(myBoth); // | d.addCallbacks(myCallback, myErrback); // // is translated into a Deferred with the following internal // representation: // // | [ // | [myCallback, null], // | [null, myErrback], // | [myBoth, myBoth], // | [myCallback, myErrback] // | ] // // The Deferred also keeps track of its current status (fired). Its // status may be one of three things: // // * -1: no value yet (initial condition) // * 0: success // * 1: error // // A Deferred will be in the error state if one of the following three // conditions are met: // // 1. The result given to callback or errback is "instanceof" Error // 2. The previous callback or errback raised an exception while // executing // 3. The previous callback or errback returned a value // "instanceof" Error // // Otherwise, the Deferred will be in the success state. The state of // the Deferred determines the next element in the callback sequence // to run. // // When a callback or errback occurs with the example deferred chain, // something equivalent to the following will happen (imagine // that exceptions are caught and returned): // // | // d.callback(result) or d.errback(result) // | if(!(result instanceof Error)){ // | result = myCallback(result); // | } // | if(result instanceof Error){ // | result = myErrback(result); // | } // | result = myBoth(result); // | if(result instanceof Error){ // | result = myErrback(result); // | }else{ // | result = myCallback(result); // | } // // The result is then stored away in case another step is added to the // callback sequence. Since the Deferred already has a value // available, any new callbacks added will be called immediately. // // There are two other "advanced" details about this implementation // that are useful: // // Callbacks are allowed to return Deferred instances themselves, so // you can build complicated sequences of events with ease. // // The creator of the Deferred may specify a canceller. The canceller // is a function that will be called if Deferred.cancel is called // before the Deferred fires. You can use this to implement clean // aborting of an XMLHttpRequest, etc. Note that cancel will fire the // deferred with a CancelledError (unless your canceller returns // another kind of error), so the errbacks should be prepared to // handle that error for cancellable Deferreds. // example: // | var deferred = new dojo.Deferred(); // | setTimeout(function(){ deferred.callback({success: true}); }, 1000); // | return deferred; // example: // Deferred objects are often used when making code asynchronous. It // may be easiest to write functions in a synchronous manner and then // split code using a deferred to trigger a response to a long-lived // operation. For example, instead of register a callback function to // denote when a rendering operation completes, the function can // simply return a deferred: // // | // callback style: // | function renderLotsOfData(data, callback){ // | var success = false // | try{ // | for(var x in data){ // | renderDataitem(data[x]); // | } // | success = true; // | }catch(e){ } // | if(callback){ // | callback(success); // | } // | } // // | // using callback style // | renderLotsOfData(someDataObj, function(success){ // | // handles success or failure // | if(!success){ // | promptUserToRecover(); // | } // | }); // | // NOTE: no way to add another callback here!! // example: // Using a Deferred doesn't simplify the sending code any, but it // provides a standard interface for callers and senders alike, // providing both with a simple way to service multiple callbacks for // an operation and freeing both sides from worrying about details // such as "did this get called already?". With Deferreds, new // callbacks can be added at any time. // // | // Deferred style: // | function renderLotsOfData(data){ // | var d = new dojo.Deferred(); // | try{ // | for(var x in data){ // | renderDataitem(data[x]); // | } // | d.callback(true); // | }catch(e){ // | d.errback(new Error("rendering failed")); // | } // | return d; // | } // // | // using Deferred style // | renderLotsOfData(someDataObj).addErrback(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).addErrback(function(){ // | promptUserToRecover(); // | }); // // Note that the caller doesn't have to change his code at all to // handle the asynchronous case. this.chain = []; this.id = this._nextId(); this.fired = -1; this.paused = 0; this.results = [null, null]; this.canceller = canceller; this.silentlyCancelled = false; }; dojo.extend(dojo.Deferred, { /* makeCalled: function(){ // summary: // returns a new, empty deferred, which is already in the called // state. Calling callback() or errback() on this deferred will // yeild an error and adding new handlers to it will result in // them being called immediately. var deferred = new dojo.Deferred(); deferred.callback(); return deferred; }, toString: function(){ var state; if(this.fired == -1){ state = 'unfired'; }else{ state = this.fired ? 'success' : 'error'; } return 'Deferred(' + this.id + ', ' + state + ')'; }, */ _nextId: (function(){ var n = 1; return function(){ return n++; }; })(), cancel: function(){ // summary: // Cancels a Deferred that has not yet received a value, or is // waiting on another Deferred as its value. // description: // If a canceller is defined, the canceller is called. If the // canceller did not return an error, or there was no canceller, // then the errback chain is started. var err; if(this.fired == -1){ if(this.canceller){ err = this.canceller(this); }else{ this.silentlyCancelled = true; } if(this.fired == -1){ if(!(err instanceof Error)){ var res = err; var msg = "Deferred Cancelled"; if(err && err.toString){ msg += ": " + err.toString(); } err = new Error(msg); err.dojoType = "cancel"; err.cancelResult = res; } this.errback(err); } }else if( (this.fired == 0) && (this.results[0] instanceof dojo.Deferred) ){ this.results[0].cancel(); } }, _resback: function(res){ // summary: // The private primitive that means either callback or errback this.fired = ((res instanceof Error) ? 1 : 0); this.results[this.fired] = res; this._fire(); }, _check: function(){ if(this.fired != -1){ if(!this.silentlyCancelled){ throw new Error("already called!"); } this.silentlyCancelled = false; return; } }, callback: function(res){ // summary: // Begin the callback sequence with a non-error value. /* callback or errback should only be called once on a given Deferred. */ this._check(); this._resback(res); }, errback: function(/*Error*/res){ // summary: // Begin the callback sequence with an error result. this._check(); if(!(res instanceof Error)){ res = new Error(res); } this._resback(res); }, addBoth: function(/*Function|Object*/cb, /*String?*/cbfn){ // summary: // Add the same function as both a callback and an errback as the // next element on the callback sequence.This is useful for code // that you want to guarantee to run, e.g. a finalizer. var enclosed = dojo.hitch.apply(dojo, arguments); return this.addCallbacks(enclosed, enclosed); // dojo.Deferred }, addCallback: function(/*Function|Object*/cb, /*String?*/cbfn /*...*/){ // summary: // Add a single callback to the end of the callback sequence. return this.addCallbacks(dojo.hitch.apply(dojo, arguments)); // dojo.Deferred }, addErrback: function(cb, cbfn){ // summary: // Add a single callback to the end of the callback sequence. return this.addCallbacks(null, dojo.hitch.apply(dojo, arguments)); // dojo.Deferred }, addCallbacks: function(cb, eb){ // summary: // Add separate callback and errback to the end of the callback // sequence. this.chain.push([cb, eb]) if(this.fired >= 0){ this._fire(); } return this; // dojo.Deferred }, _fire: function(){ // summary: // Used internally to exhaust the callback sequence when a result // is available. var chain = this.chain; var fired = this.fired; var res = this.results[fired]; var self = this; var cb = null; while( (chain.length > 0) && (this.paused == 0) ){ // Array var f = chain.shift()[fired]; if(!f){ continue; } var func = function(){ var ret = f(res); //If no response, then use previous response. if(typeof ret != "undefined"){ res = ret; } fired = ((res instanceof Error) ? 1 : 0); if(res instanceof dojo.Deferred){ cb = function(res){ self._resback(res); // inlined from _pause() self.paused--; if( (self.paused == 0) && (self.fired >= 0) ){ self._fire(); } } // inlined from _unpause this.paused++; } }; if(dojo.config.debugAtAllCosts){ func.call(this); }else{ try{ func.call(this); }catch(err){ fired = 1; res = err; } } } this.fired = fired; this.results[fired] = res; if((cb)&&(this.paused)){ // this is for "tail recursion" in case the dependent // deferred is already fired res.addBoth(cb); } } }); } 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. Throws for invalid JSON strings. // 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. 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){ // } // 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.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 [ dojo.isString(arr) ? arr.split("") : arr, obj || dojo.global, // FIXME: cache the anonymous functions we create here? dojo.isString(cb) ? new Function("item", "index", "array", cb) : cb ]; }; 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: // For details on this method, see: // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_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: // For details on this method, see: // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_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. // 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. For more details, see: // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach // example: // | // log out all members of the array: // | dojo.forEach( // | [ "thinger", "blah", "howdy", 10 ], // | function(item){ // | // | } // | ); // example: // | // log out the members and their indexes // | dojo.forEach( // | [ "thinger", "blah", "howdy", 10 ], // | function(item, idx, arr){ // | // | } // | ); // example: // | // use a scoped object member as the callback // | // | var obj = { // | prefix: "logged via obj.callback:", // | callback: function(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; i1; }); // example: // | // returns true // | dojo.every([1, 2, 3, 4], function(item){ return item>0; }); return dojo._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. For more details, see: // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_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 dojo._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. For more details, see: // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_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; i1; }); var _p = _getParts(arr, thisObject, callback); arr = _p[0]; var outArr = []; for(var i=0,l=arr.length; i>= 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 color from 1, 2, 3, or 4 element array 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. // 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; try{ dojo.doc = documentObject; delete dojo._bodyLtr; // uncache if(thisObject && dojo.isString(callback)){ callback = thisObject[callback]; } return callback.apply(thisObject, cbArguments || []); }finally{ dojo.doc = oldDoc; if(oldLtr !== undefined){ dojo._bodyLtr = oldLtr; } } }; } 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.isFF <= 2) { // check tagName to fix a FF2 bug with invalid nodes (hidden child DIV of INPUT) // which causes isDescendant to return false which causes // spurious, and more importantly, incorrect mouse events to fire. // TODO: remove tagName check when Firefox 2 is no longer supported try{ e.relatedTarget.tagName; }catch(e2){ return; } } 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, 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 }; // 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!=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}); 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, similar to "$" function in another library. // 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. // | if(dojo.byId("bar")){ ... } // // 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(dojo.isString(id)){ var _d = doc || dojo.doc; var 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{ return id; // DomNode } }; }else{ dojo.byId = function(id, doc){ return dojo.isString(id) ? (doc || dojo.doc).getElementById(id) : id; // DomNode }; } /*===== } =====*/ (function(){ var d = dojo; var _destroyContainer = null; 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 = d.byId(node); try{ if(!_destroyContainer || _destroyContainer.ownerDocument != node.ownerDocument){ _destroyContainer = node.ownerDocument.createElement("div"); } _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 try{ node = d.byId(node); ancestor = d.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. node = d.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 true if successful, false otherwise. // // 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(dojo.create('div'), dojo.body()); // // example: // Put a new LI as the first child of a list by id: // | dojo.place(dojo.create('li'), "someUl", "first"); refNode = d.byId(refNode); if(d.isString(node)){ node = node.charAt(0) == "<" ? d._toDom(node, refNode.ownerDocument) : d.byId(node); } if(typeof position == "number"){ 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*/){ var _dcm = document.compatMode; // client code may have to adjust if compatMode varies across iframes d.boxModel = _dcm == "BackCompat" || _dcm == "QuirksMode" || d.isIE < 6 ? "border-box" : "content-box"; // FIXME: remove IE < 6 support? } // ============================= // 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; node.style.zoom = 1.0; // 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 = !(opacity == 1); if(!af(node)){ node.style.filter += " progid:" + astr + "(Opacity=" + ov + ")"; }else{ af(node, 1).Opacity = ov; } 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() or // dojo.contentBox(). // 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 = d.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 && !d.isString(style)){ 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. // // * 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. // // * 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. // // * 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: optional. left offset from parent. // t: optional. top offset from parent. // w: optional. width in current box model. // h: optional. width in current box model. // u: optional. 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: // 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. var n = d.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. // 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. All properties are optional if passed. var n = d.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 _b = d.body(), _w = d.global, de = d.doc.documentElement; return { y: (_w.pageYOffset || de.scrollTop || _b.scrollTop || 0), x: (_w.pageXOffset || d._fixIeBiDiScrollLeft(de.scrollLeft) || _b.scrollLeft || 0) }; }; dojo._isBodyLtr = function(){ //FIXME: could check html and body tags directly instead of computed style? need to ignore case, accept empty values return ("_bodyLtr" in d) ? d._bodyLtr : d._bodyLtr = gcs(d.body()).direction == "ltr"; // Boolean } dojo._getIeDocumentElementOffset = function(){ // summary // 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; //FIXME: use this instead? var de = d.compatMode == "BackCompat" ? d.body : d.documentElement; if(d.isIE < 7){ return { x: d._isBodyLtr() || window.parent == window ? de.clientLeft : de.offsetWidth - de.clientWidth - de.clientLeft, y: de.clientTop }; // Object }else if(d.isIE < 8){ return {x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top}; }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 = dd.compatMode == "BackCompat" ? dd.body : dd.documentElement; return scrollLeft + de.clientWidth - de.scrollWidth; // Integer } return scrollLeft; // Integer } dojo._abs = function(/*DomNode*/node, /*Boolean?*/includeScroll){ // summary: // Gets the position of the passed element relative to // the viewport (if includeScroll==false), or relative to the // document root (if includeScroll==true). // // Returns an object of the form: // { x: 100, y: 300 } // if includeScroll is passed, the x and y values will include any // document offsets that may affect the position relative to the // viewport. // FIXME: need to decide in the brave-new-world if we're going to be // margin-box or border-box. // targetBoxType == "border-box" var db = d.body(), dh = d.body().parentNode, ret; if(node["getBoundingClientRect"]){ // IE6+, FF3+, super-modern WebKit, and Opera 9.6+ all take this branch var client = node.getBoundingClientRect(); ret = { x: client.left, y: client.top }; if(d.isFF >= 3){ // in FF3 you have to subtract the document element margins var cs = gcs(dh); ret.x -= px(dh, cs.marginLeft) + px(dh, cs.borderLeftWidth); ret.y -= px(dh, cs.marginTop) + px(dh, cs.borderTopWidth); } 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 : 0); ret.y -= offset.y + (d.isQuirks ? db.clientTop : 0); } }else{ // FF2 and Safari ret = { x: 0, y: 0 }; 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.isFF){ // 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.isFF && 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 } // FIXME: need a setter for coords or a moveTo!! dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll){ // summary: // Returns an object that measures margin box width/height and // absolute positioning data from dojo._abs(). // // description: // Returns an object that measures margin box width/height and // absolute positioning data from dojo._abs(). // 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._abs(). var n = d.byId(node), s = gcs(n), mb = d._getMarginBox(n, s); var abs = d._abs(n, includeScroll); mb.x = abs.x; mb.y = abs.y; return mb; } // ============================= // Element attribute Functions // ============================= var ieLT8 = d.isIE < 8; var _fixAttrName = function(/*String*/name){ switch(name.toLowerCase()){ // Internet Explorer will only set or remove tabindex/readonly // if it is spelled "tabIndex"/"readOnly" case "tabindex": return ieLT8 ? "tabIndex" : "tabindex"; case "readonly": return "readOnly"; case "class": return "className"; case "for": case "htmlfor": // to pick up for attrib set in markup via getAttribute() IE<8 uses "htmlFor" and others use "for" // get/setAttribute works in all as long use same value for both get/set return ieLT8 ? "htmlFor" : "for"; default: return name; } } // non-deprecated HTML4 attributes with default values // http://www.w3.org/TR/html401/index/attributes.html // FF and Safari will return the default values if you // access the attributes via a property but not // via getAttribute() var _attrProps = { colspan: "colSpan", enctype: "enctype", frameborder: "frameborder", method: "method", rowspan: "rowSpan", scrolling: "scrolling", shape: "shape", span: "span", type: "type", valuetype: "valueType", // the following attributes don't have the default but should be treated like properties classname: "className", innerhtml: "innerHTML" } 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 node = d.byId(node); var fixName = _fixAttrName(name); fixName = fixName == "htmlFor" ? "for" : fixName; //IE<8 uses htmlFor except in this case var attr = node.getAttributeNode && node.getAttributeNode(fixName); return attr ? attr.specified : false; // 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 argumnt 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, undefined // // 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 = d.byId(node); var args = arguments.length; if(args == 2 && !d.isString(name)){ // the object form of setter: the 2nd argument is a dictionary for(var x in name){ d.attr(node, x, name[x]); } // FIXME: return the node in this case? could be useful. return; } name = _fixAttrName(name); if(args == 3){ // setter if(d.isFunction(value)){ // 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][name]; if(h){ d.disconnect(h); }else{ try{ delete node[name]; }catch(e){} } // ensure that event objects are normalized, etc. _evtHdlrMap[attrId][name] = d.connect(node, name, value); }else if(typeof value == "boolean"){ // e.g. onsubmit, disabled node[name] = value; }else if(name === "style" && !d.isString(value)){ // when the name is "style" and value is an object, pass along d.style(node, value); }else if(name == "className"){ node.className = value; }else if(name === "innerHTML"){ if(d.isIE && node.tagName.toLowerCase() in _roInnerHtml){ d.empty(node); node.appendChild(d._toDom(value, node.ownerDocument)); }else{ node[name] = value; } }else{ node.setAttribute(name, value); } }else{ // getter // should we access this attribute via a property or // via getAttribute()? var prop = _attrProps[name.toLowerCase()]; if(prop){ return node[prop]; } var attrValue = node[name]; return (typeof attrValue == 'boolean' || typeof attrValue == 'function') ? attrValue : (d.hasAttr(node, name) ? node.getAttribute(name) : null); } } 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 d.byId(node).removeAttribute(_fixAttrName(name)); } 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:"

hi

" }); // // 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){ }) // | .place("#someNode"); // redundant, but cleaner. var doc = d.doc; if(refNode){ refNode = d.byId(refNode); doc = refNode.ownerDocument; } if(d.isString(tag)){ 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 = d.byId(node); for(var c; c = node.lastChild;){ // intentional assignment d.destroy(c); } } : function(node){ d.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("First!"); } =====*/ // 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" ? ' // | // | // | // | // // 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){ if(type == "radio" || type == "checkbox"){ if(item.checked){ setValue(ret, _in, item.value); } }else if(item.multiple){ ret[_in] = []; _d.query("option", item).forEach(function(opt){ if(opt.selected){ setValue(ret, _in, opt.value); } }); }else{ setValue(ret, _in, item.value); 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: // return 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: // returns an object representing a de-serialized query section of a // URL. Query keys with multiple values are returned in an array. // description: // 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(_d.isString(ret[name])){ ret[name] = [ret[name]]; } if(_d.isArray(ret[name])){ ret[name].push(val); }else{ ret[name] = val; } } }); return ret; // Object } /* from refactor.txt: all bind() replacement APIs take the following argument structure: { url: "blah.html", // all below are optional, but must be supported in some form by // every IO API timeout: 1000, // milliseconds handleAs: "text", // replaces the always-wrong "mimetype" content: { key: "value" }, // browser-specific, MAY be unsupported sync: true, // defaults to false form: dojo.byId("someForm") } */ // 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; dojo._contentHandlers = { text: function(xhr){ return xhr.responseText; }, json: function(xhr){ return _d.fromJson(xhr.responseText || null); }, "json-comment-filtered": function(xhr){ // NOTE: 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. 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){ // FIXME: try Moz and IE specific eval variants? return _d.eval(xhr.responseText); }, xml: function(xhr){ var result = xhr.responseXML; if(_d.isIE && (!result || !result.documentElement)){ 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 } }; dojo._contentHandlers["json-comment-optional"] = function(xhr){ var handlers = _d._contentHandlers; if(xhr.responseText && xhr.responseText.indexOf("\/*") != -1){ 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). // load: Function? // function(response, ioArgs){} response is of type Object, ioArgs // is of type dojo.__IoCallbackArgs. This function will be // called on a successful HTTP response code. // error: Function? // function(response, ioArgs){} response is of type Object, ioArgs // is of type dojo.__IoCallbackArgs. 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? // function(response, ioArgs){} response is of type Object, ioArgs // is of type dojo.__IoCallbackArgs. 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.load = load; this.error = error; this.handle = handle; } =====*/ /*===== 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._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); }); } 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 = _d._contentHandlers[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. 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 = []; 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); }else if(tif.ioCheck(dfd)){ _inFlight.splice(i--, 1); tif.resHandle(dfd); }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(); } } }; if(dojo.config.debugAtAllCosts){ func.call(this); }else{ try{ func.call(this); }catch(e){ dfd.errback(e); } } } } 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._ioWatch = function(/*Deferred*/dfd, /*Function*/validCheck, /*Function*/ioCheck, /*Function*/resHandle){ //summary: watches the io request represented by dfd to see if it completes. //dfd: // The Deferred object to watch. //validCheck: // Function used to check if the IO request is still valid. Gets the dfd // object as its only argument. //ioCheck: // Function used to check if basic IO call worked. Gets the dfd // object as its only argument. //resHandle: // 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 // 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. this.handleAs = handleAs; this.sync = sync; this.headers = headers; } }); =====*/ 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); //Pass the args to _xhrObj, to allow xhr iframe proxy interceptions. dfd.ioArgs.xhr = _d._xhrObj(dfd.ioArgs.args); if(hasBody){ if("postData" in args){ dfd.ioArgs.query = args.postData; }else if("putData" in args){ dfd.ioArgs.query = args.putData; } }else{ _d._ioAddQueryToUrl(dfd.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" var ioArgs = dfd.ioArgs; var xhr = ioArgs.xhr; 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{ xhr.setRequestHeader(hdr, args.headers[hdr]); } } } // FIXME: is this appropriate for all content types? xhr.setRequestHeader("Content-Type", args.contentType || _defaultContentType); if(!args.headers || !args.headers["X-Requested-With"]){ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); } // FIXME: set other headers here! if(dojo.config.debugAtAllCosts){ xhr.send(ioArgs.query); }else{ try{ xhr.send(ioArgs.query); }catch(e){ dfd.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 losely 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 } d.declare("dojo._Animation", null, { // summary // 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. constructor: function(/*Object*/ args){ _mixin(this, args); if(d.isArray(this.curve)){ /* curve: Array pId: a */ this.curve = new d._Line(this.curve[0], this.curve[1]); } }, // 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: rate/1000 = fps) rate: 10 /* 100 fps */, /*===== // delay: Integer // The time in milliseconds to wait before starting animation after it has been .play()'ed delay: null, // events // // 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, _fire: function(/*Event*/ evt, /*Array?*/ args){ // summary: // Convenience function. Fire event "evt" and pass it the // arguments specified in "args". // evt: // The event to fire. // args: // The arguments to pass to the event. if(this[evt]){ if(dojo.config.debugAtAllCosts){ this[evt].apply(this, args||[]); }else{ try{ this[evt].apply(this, args||[]); }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. 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; // dojo._Animation } _t._fire("beforeBegin"); var de = delay || _t.delay, _p = dojo.hitch(_t, "_play", gotoStart); if(de > 0){ _t._delayTimer = setTimeout(_p, de); return _t; // dojo._Animation } _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._endTime = _t._startTime + _t.duration; _t._active = true; _t._paused = false; var value = _t.curve.getValue(_t._percent); 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._percent)]); 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._percent)]); _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._endTime - _t._startTime); 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._stopTimer(); } } return _t; // dojo._Animation }, _clearTimer: function(){ // summary: Clear the play delay timer clearTimeout(this._delayTimer); delete this._delayTimer; } }); var ctr = 0, _globalTimerList = [], timer = null, runner = { run: function(){ } }; dojo._Animation.prototype._startTimer = function(){ // this._timer = setTimeout(dojo.hitch(this, "_cycle"), this.rate); if(!this._timer){ this._timer = d.connect(runner, "run", this, "_cycle"); ctr++; } if(!timer){ timer = setInterval(d.hitch(runner, "run"), this.rate); } }; dojo._Animation.prototype._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 'unit' 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 // // example: // Animate width, height, and padding over 2 seconds... the // pedantic way: // | dojo.animateProperty({ node: node, duration:2000, // | properties: { // | width: { start: '200', end: '400', unit:"px" }, // | height: { start:'200', end: '400', unit:"px" }, // | paddingTop: { start:'5', end:'50', unit:"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(){ // | // called when the animation finishes // | } // | }).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(){ // | // | }); // | // play the animation now: // | anim.play(); 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]; prop = pm[p] = _mixin({}, (d.isObject(prop) ? prop: { end: prop })); if(d.isFunction(prop.start)){ prop.start = prop.start(); } if(d.isFunction(prop.end)){ prop.end = prop.end(); } 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(this.node, p); }else if(!("start" in prop)){ prop.start = getStyle(this.node, 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({ 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 if(dojo.config.afterOnLoad && dojo.isBrowser){ //Dojo is being added to the page after page load, so just trigger //the init sequence after a timeout. 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. window.setTimeout(dojo._loadInit, 1000); } })();