/* Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ /* This is an optimized version of Dojo, built for deployment and not for development. To get sources and documentation, please visit: http://dojotoolkit.org */ (function( userConfig, defaultConfig ){ // summary: // This is the "source loader" and is the entry point for Dojo during development. You may also load Dojo with // any AMD-compliant loader via the package main module dojo/main. // description: // This is the "source loader" for Dojo. It provides an AMD-compliant loader that can be configured // to operate in either synchronous or asynchronous modes. After the loader is defined, dojo is loaded // IAW the package main module dojo/main. In the event you wish to use a foreign loader, you may load dojo as a package // via the package main module dojo/main and this loader is not required; see dojo/package.json for details. // // In order to keep compatibility with the v1.x line, this loader includes additional machinery that enables // the dojo.provide, dojo.require et al API. This machinery is loaded by default, but may be dynamically removed // via the has.js API and statically removed via the build system. // // This loader includes sniffing machinery to determine the environment; the following environments are supported: // // - browser // - node.js // - rhino // // This is the so-called "source loader". As such, it includes many optional features that may be discadred by // building a customized verion with the build system. // Design and Implementation Notes // // This is a dojo-specific adaption of bdLoad, donated to the dojo foundation by Altoviso LLC. // // This function defines an AMD-compliant (http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition) // loader that can be configured to operate in either synchronous or asynchronous modes. // // Since this machinery implements a loader, it does not have the luxury of using a load system and/or // leveraging a utility library. This results in an unpleasantly long file; here is a road map of the contents: // // 1. Small library for use implementing the loader. // 2. Define the has.js API; this is used throughout the loader to bracket features. // 3. Define the node.js and rhino sniffs and sniff. // 4. Define the loader's data. // 5. Define the configuration machinery. // 6. Define the script element sniffing machinery and sniff for configuration data. // 7. Configure the loader IAW the provided user, default, and sniffing data. // 8. Define the global require function. // 9. Define the module resolution machinery. // 10. Define the module and plugin module definition machinery // 11. Define the script injection machinery. // 12. Define the window load detection. // 13. Define the logging API. // 14. Define the tracing API. // 16. Define the AMD define function. // 17. Define the dojo v1.x provide/require machinery--so called "legacy" modes. // 18. Publish global variables. // // Language and Acronyms and Idioms // // moduleId: a CJS module identifier, (used for public APIs) // mid: moduleId (used internally) // packageId: a package identifier (used for public APIs) // pid: packageId (used internally); the implied system or default package has pid==="" // pack: package is used internally to reference a package object (since javascript has reserved words including "package") // prid: plugin resource identifier // The integer constant 1 is used in place of true and 0 in place of false. // define a minimal library to help build the loader var noop = function(){ }, isEmpty = function(it){ for(var p in it){ return 0; } return 1; }, toString = {}.toString, isFunction = function(it){ return toString.call(it) == "[object Function]"; }, isString = function(it){ return toString.call(it) == "[object String]"; }, isArray = function(it){ return toString.call(it) == "[object Array]"; }, forEach = function(vector, callback){ if(vector){ for(var i = 0; i < vector.length;){ callback(vector[i++]); } } }, mix = function(dest, src){ for(var p in src){ dest[p] = src[p]; } return dest; }, makeError = function(error, info){ return mix(new Error(error), {src:"dojoLoader", info:info}); }, uidSeed = 1, uid = function(){ // Returns a unique indentifier (within the lifetime of the document) of the form /_d+/. return "_" + uidSeed++; }, // FIXME: how to doc window.require() api // this will be the global require function; define it immediately so we can start hanging things off of it req = function( config, //(object, optional) hash of configuration properties dependencies, //(array of commonjs.moduleId, optional) list of modules to be loaded before applying callback callback //(function, optional) lamda expression to apply to module values implied by dependencies ){ return contextRequire(config, dependencies, callback, 0, req); }, // the loader uses the has.js API to control feature inclusion/exclusion; define then use throughout global = this, doc = global.document, element = doc && doc.createElement("DiV"), has = req.has = function(name){ return isFunction(hasCache[name]) ? (hasCache[name] = hasCache[name](global, doc, element)) : hasCache[name]; }, hasCache = has.cache = defaultConfig.hasCache; has.add = function(name, test, now, force){ (hasCache[name]===undefined || force) && (hasCache[name] = test); return now && has(name); }; 0 && has.add("host-node", userConfig.has && "host-node" in userConfig.has ? userConfig.has["host-node"] : (typeof process == "object" && process.versions && process.versions.node && process.versions.v8)); if( 0 ){ // fixup the default config for node.js environment require("./_base/configNode.js").config(defaultConfig); // remember node's require (with respect to baseUrl==dojo's root) defaultConfig.loaderPatch.nodeRequire = require; } 0 && has.add("host-rhino", userConfig.has && "host-rhino" in userConfig.has ? userConfig.has["host-rhino"] : (typeof load == "function" && (typeof Packages == "function" || typeof Packages == "object"))); if( 0 ){ // owing to rhino's lame feature that hides the source of the script, give the user a way to specify the baseUrl... for(var baseUrl = userConfig.baseUrl || ".", arg, rhinoArgs = this.arguments, i = 0; i < rhinoArgs.length;){ arg = (rhinoArgs[i++] + "").split("="); if(arg[0] == "baseUrl"){ baseUrl = arg[1]; break; } } load(baseUrl + "/_base/configRhino.js"); rhinoDojoConfig(defaultConfig, baseUrl, rhinoArgs); } // userConfig has tests override defaultConfig has tests; do this after the environment detection because // the environment detection usually sets some has feature values in the hasCache. for(var p in userConfig.has){ has.add(p, userConfig.has[p], 0, 1); } // // define the loader data // // the loader will use these like symbols if the loader has the traceApi; otherwise // define magic numbers so that modules can be provided as part of defaultConfig var requested = 1, arrived = 2, nonmodule = 3, executing = 4, executed = 5; if( 0 ){ // these make debugging nice; but using strings for symbols is a gross rookie error; don't do it for production code requested = "requested"; arrived = "arrived"; nonmodule = "not-a-module"; executing = "executing"; executed = "executed"; } var legacyMode = 0, sync = "sync", xd = "xd", syncExecStack = [], dojoRequirePlugin = 0, checkDojoRequirePlugin = noop, transformToAmd = noop, getXhr; if( 1 ){ req.isXdUrl = noop; req.initSyncLoader = function(dojoRequirePlugin_, checkDojoRequirePlugin_, transformToAmd_){ // the first dojo/_base/loader loaded gets to define these variables; they are designed to work // in the presense of zero to many mapped dojo/_base/loaders if(!dojoRequirePlugin){ dojoRequirePlugin = dojoRequirePlugin_; checkDojoRequirePlugin = checkDojoRequirePlugin_; transformToAmd = transformToAmd_; } return { sync:sync, requested:requested, arrived:arrived, nonmodule:nonmodule, executing:executing, executed:executed, syncExecStack:syncExecStack, modules:modules, execQ:execQ, getModule:getModule, injectModule:injectModule, setArrived:setArrived, signal:signal, finishExec:finishExec, execModule:execModule, dojoRequirePlugin:dojoRequirePlugin, getLegacyMode:function(){return legacyMode;}, guardCheckComplete:guardCheckComplete }; }; if( 1 ){ // in legacy sync mode, the loader needs a minimal XHR library var locationProtocol = location.protocol, locationHost = location.host; req.isXdUrl = function(url){ if(/^\./.test(url)){ // begins with a dot is always relative to page URL; therefore not xdomain return false; } if(/^\/\//.test(url)){ // for v1.6- backcompat, url starting with // indicates xdomain return true; } // get protocol and host // \/+ takes care of the typical file protocol that looks like file:///drive/path/to/file // locationHost is falsy if file protocol => if locationProtocol matches and is "file:", || will return false var match = url.match(/^([^\/\:]+\:)\/+([^\/]+)/); return match && (match[1] != locationProtocol || (locationHost && match[2] != locationHost)); }; // note: to get the file:// protocol to work in FF, you must set security.fileuri.strict_origin_policy to false in about:config 1 || has.add("dojo-xhr-factory", 1); has.add("dojo-force-activex-xhr", 1 && !doc.addEventListener && window.location.protocol == "file:"); has.add("native-xhr", typeof XMLHttpRequest != "undefined"); if(has("native-xhr") && !has("dojo-force-activex-xhr")){ getXhr = function(){ return new XMLHttpRequest(); }; }else{ // if in the browser an old IE; find an xhr for(var XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], progid, i = 0; i < 3;){ try{ progid = XMLHTTP_PROGIDS[i++]; if(new ActiveXObject(progid)){ // this progid works; therefore, use it from now on break; } }catch(e){ // squelch; we're just trying to find a good ActiveX progid // if they all fail, then progid ends up as the last attempt and that will signal the error // the first time the client actually tries to exec an xhr } } getXhr = function(){ return new ActiveXObject(progid); }; } req.getXhr = getXhr; has.add("dojo-gettext-api", 1); req.getText = function(url, async, onLoad){ var xhr = getXhr(); xhr.open('GET', fixupUrl(url), false); xhr.send(null); if(xhr.status == 200 || (!location.host && !xhr.status)){ if(onLoad){ onLoad(xhr.responseText, async); } }else{ throw makeError("xhrFailed", xhr.status); } return xhr.responseText; }; } }else{ req.async = 1; } // // loader eval // var eval_ = // use the function constructor so our eval is scoped close to (but not in) in the global space with minimal pollution new Function('return eval(arguments[0]);'); req.eval = function(text, hint){ return eval_(text + "\r\n////@ sourceURL=" + hint); }; // // loader micro events API // var listenerQueues = {}, error = "error", signal = req.signal = function(type, args){ var queue = listenerQueues[type]; // notice we run a copy of the queue; this allows listeners to add/remove // other listeners without affecting this particular signal forEach(queue && queue.slice(0), function(listener){ listener.apply(null, isArray(args) ? args : [args]); }); }, on = req.on = function(type, listener){ // notice a queue is not created until a client actually connects var queue = listenerQueues[type] || (listenerQueues[type] = []); queue.push(listener); return { remove:function(){ for(var i = 0; i (alias, actual) = [], paths // CommonJS paths = {}, pathsMapProg // list of (from-path, to-path, regex, length) derived from paths; // a "program" to apply paths; see computeMapProg = [], packs // a map from packageId to package configuration object; see fixupPackageInfo = {}, map = req.map // AMD map config variable; dojo/_base/kernel needs req.map to figure out the scope map = {}, mapProgs // vector of quads as described by computeMapProg; map-key is AMD map key, map-value is AMD map value = [], modules // A hash:(mid) --> (module-object) the module namespace // // pid: the package identifier to which the module belongs (e.g., "dojo"); "" indicates the system or default package // mid: the fully-resolved (i.e., mappings have been applied) module identifier without the package identifier (e.g., "dojo/io/script") // url: the URL from which the module was retrieved // pack: the package object of the package to which the module belongs // executed: 0 => not executed; executing => in the process of tranversing deps and running factory; executed => factory has been executed // deps: the dependency vector for this module (vector of modules objects) // def: the factory for this module // result: the result of the running the factory for this module // injected: (0 | requested | arrived) the status of the module; nonmodule means the resource did not call define // load: plugin load function; applicable only for plugins // // Modules go through several phases in creation: // // 1. Requested: some other module's definition or a require application contained the requested module in // its dependency vector or executing code explicitly demands a module via req.require. // // 2. Injected: a script element has been appended to the insert-point element demanding the resource implied by the URL // // 3. Loaded: the resource injected in [2] has been evalated. // // 4. Defined: the resource contained a define statement that advised the loader about the module. Notice that some // resources may just contain a bundle of code and never formally define a module via define // // 5. Evaluated: the module was defined via define and the loader has evaluated the factory and computed a result. = {}, cacheBust // query string to append to module URLs to bust browser cache = "", cache // hash:(mid | url)-->(function | string) // // A cache of resources. The resources arrive via a config.cache object, which is a hash from either mid --> function or // url --> string. The url key is distinguished from the mid key by always containing the prefix "url:". url keys as provided // by config.cache always have a string value that represents the contents of the resource at the given url. mid keys as provided // by configl.cache always have a function value that causes the same code to execute as if the module was script injected. // // Both kinds of key-value pairs are entered into cache via the function consumePendingCache, which may relocate keys as given // by any mappings *iff* the config.cache was received as part of a module resource request. // // Further, for mid keys, the implied url is computed and the value is entered into that key as well. This allows mapped modules // to retrieve cached items that may have arrived consequent to another namespace. // = {}, urlKeyPrefix // the prefix to prepend to a URL key in the cache. = "url:", pendingCacheInsert // hash:(mid)-->(function) // // Gives a set of cache modules pending entry into cache. When cached modules are published to the loader, they are // entered into pendingCacheInsert; modules are then pressed into cache upon (1) AMD define or (2) upon receiving another // independent set of cached modules. (1) is the usual case, and this case allows normalizing mids given in the pending // cache for the local configuration, possibly relocating modules. = {}, dojoSniffConfig // map of configuration variables // give the data-dojo-config as sniffed from the document (if any) = {}; if( 1 ){ var consumePendingCacheInsert = function(referenceModule){ var p, item, match, now, m; for(p in pendingCacheInsert){ item = pendingCacheInsert[p]; match = p.match(/^url\:(.+)/); if(match){ cache[urlKeyPrefix + toUrl(match[1], referenceModule)] = item; }else if(p=="*now"){ now = item; }else if(p!="*noref"){ m = getModuleInfo(p, referenceModule); cache[m.mid] = cache[urlKeyPrefix + m.url] = item; } } if(now){ now(createRequire(referenceModule)); } pendingCacheInsert = {}; }, escapeString = function(s){ return s.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(c){ return "\\" + c; }); }, computeMapProg = function(map, dest){ // This routine takes a map as represented by a JavaScript object and initializes dest, a vector of // quads of (map-key, map-value, refex-for-map-key, length-of-map-key), sorted decreasing by length- // of-map-key. The regex looks for the map-key followed by either "/" or end-of-string at the beginning // of a the search source. Notice the map-value is irrelevent to the algorithm dest.splice(0, dest.length); for(var p in map){ dest.push([ p, map[p], new RegExp("^" + escapeString(p) + "(\/|$)"), p.length]); } dest.sort(function(lhs, rhs){ return rhs[3] - lhs[3]; }); return dest; }, fixupPackageInfo = function(packageInfo){ // calculate the precise (name, location, main, mappings) for a package var name = packageInfo.name; if(!name){ // packageInfo must be a string that gives the name name = packageInfo; packageInfo = {name:name}; } packageInfo = mix({main:"main"}, packageInfo); packageInfo.location = packageInfo.location ? packageInfo.location : name; // packageMap is depricated in favor of AMD map if(packageInfo.packageMap){ map[name] = packageInfo.packageMap; } if(!packageInfo.main.indexOf("./")){ packageInfo.main = packageInfo.main.substring(2); } // now that we've got a fully-resolved package object, push it into the configuration packs[name] = packageInfo; }, delayedModuleConfig // module config cannot be consummed until the loader is completely initialized; therefore, all // module config detected during booting is memorized and applied at the end of loader initialization // TODO: this is a bit of a kludge; all config should be moved to end of loader initialization, but // we'll delay this chore and do it with a final loader 1.x cleanup after the 2.x loader prototyping is complete = [], config = function(config, booting, referenceModule){ for(var p in config){ if(p=="waitSeconds"){ req.waitms = (config[p] || 0) * 1000; } if(p=="cacheBust"){ cacheBust = config[p] ? (isString(config[p]) ? config[p] : (new Date()).getTime() + "") : ""; } if(p=="baseUrl" || p=="combo"){ req[p] = config[p]; } if( 1 && p=="async"){ // falsy or "sync" => legacy sync loader // "xd" => sync but loading xdomain tree and therefore loading asynchronously (not configurable, set automatically by the loader) // "legacyAsync" => permanently in "xd" by choice // "debugAtAllCosts" => trying to load everything via script injection (not implemented) // otherwise, must be truthy => AMD // legacyMode: sync | legacyAsync | xd | false var mode = config[p]; req.legacyMode = legacyMode = (isString(mode) && /sync|legacyAsync/.test(mode) ? mode : (!mode ? sync : false)); req.async = !legacyMode; } if(config[p]!==hasCache){ // accumulate raw config info for client apps which can use this to pass their own config req.rawConfig[p] = config[p]; p!="has" && has.add("config-"+p, config[p], 0, booting); } } // make sure baseUrl exists if(!req.baseUrl){ req.baseUrl = "./"; } // make sure baseUrl ends with a slash if(!/\/$/.test(req.baseUrl)){ req.baseUrl += "/"; } // now do the special work for has, packages, packagePaths, paths, aliases, and cache for(p in config.has){ has.add(p, config.has[p], 0, booting); } // for each package found in any packages config item, augment the packs map owned by the loader forEach(config.packages, fixupPackageInfo); // for each packagePath found in any packagePaths config item, augment the packageConfig // packagePaths is depricated; remove in 2.0 for(baseUrl in config.packagePaths){ forEach(config.packagePaths[baseUrl], function(packageInfo){ var location = baseUrl + "/" + packageInfo; if(isString(packageInfo)){ packageInfo = {name:packageInfo}; } packageInfo.location = location; fixupPackageInfo(packageInfo); }); } // notice that computeMapProg treats the dest as a reference; therefore, if/when that variable // is published (see dojo-publish-privates), the published variable will always hold a valid value. // this must come after all package processing since package processing may mutate map computeMapProg(mix(map, config.map), mapProgs); forEach(mapProgs, function(item){ item[1] = computeMapProg(item[1], []); if(item[0]=="*"){ mapProgs.star = item[1]; } }); // push in any paths and recompute the internal pathmap computeMapProg(mix(paths, config.paths), pathsMapProg); // aliases forEach(config.aliases, function(pair){ if(isString(pair[0])){ pair[0] = new RegExp("^" + escapeString(pair[0]) + "$"); } aliases.push(pair); }); if(booting){ delayedModuleConfig.push({config:config.config}); }else{ for(p in config.config){ var module = getModule(p, referenceModule); module.config = mix(module.config || {}, config.config[p]); } } // push in any new cache values if(config.cache){ consumePendingCacheInsert(); pendingCacheInsert = config.cache; if(config.cache["*noref"]){ consumePendingCacheInsert(); } } signal("config", [config, req.rawConfig]); }; // // execute the various sniffs; userConfig can override and value // if(has("dojo-cdn") || 1 ){ // the sniff regex looks for a src attribute ending in dojo.js, optionally preceeded with a path. // match[3] returns the path to dojo.js (if any) without the trailing slash. This is used for the // dojo location on CDN deployments and baseUrl when either/both of these are not provided // explicitly in the config data; this is the 1.6- behavior. var scripts = doc.getElementsByTagName("script"), i = 0, script, dojoDir, src, match; while(i < scripts.length){ script = scripts[i++]; if((src = script.getAttribute("src")) && (match = src.match(/(((.*)\/)|^)dojo\.js(\W|$)/i))){ // sniff dojoDir and baseUrl dojoDir = match[3] || ""; defaultConfig.baseUrl = defaultConfig.baseUrl || dojoDir; // sniff configuration on attribute in script element src = (script.getAttribute("data-dojo-config") || script.getAttribute("djConfig")); if(src){ dojoSniffConfig = req.eval("({ " + src + " })", "data-dojo-config"); } // sniff requirejs attribute if( 0 ){ var dataMain = script.getAttribute("data-main"); if(dataMain){ dojoSniffConfig.deps = dojoSniffConfig.deps || [dataMain]; } } break; } } } if( 0 ){ // pass down doh.testConfig from parent as if it were a data-dojo-config try{ if(window.parent != window && window.parent.require){ var doh = window.parent.require("doh"); doh && mix(dojoSniffConfig, doh.testConfig); } }catch(e){} } // configure the loader; let the user override defaults req.rawConfig = {}; config(defaultConfig, 1); // do this before setting userConfig/sniffConfig to allow userConfig/sniff overrides if(has("dojo-cdn")){ packs.dojo.location = dojoDir; if(dojoDir){ dojoDir += "/"; } packs.dijit.location = dojoDir + "../dijit/"; packs.dojox.location = dojoDir + "../dojox/"; } config(userConfig, 1); config(dojoSniffConfig, 1); }else{ // no config API, assume defaultConfig has everything the loader needs...for the entire lifetime of the application paths = defaultConfig.paths; pathsMapProg = defaultConfig.pathsMapProg; packs = defaultConfig.packs; aliases = defaultConfig.aliases; mapProgs = defaultConfig.mapProgs; modules = defaultConfig.modules; cache = defaultConfig.cache; cacheBust = defaultConfig.cacheBust; // remember the default config for other processes (e.g., dojo/config) req.rawConfig = defaultConfig; } if( 0 ){ req.combo = req.combo || {add:noop}; var comboPending = 0, combosPending = [], comboPendingTimer = null; } // build the loader machinery iaw configuration, including has feature tests var injectDependencies = function(module){ // checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies guardCheckComplete(function(){ forEach(module.deps, injectModule); if( 0 && comboPending && !comboPendingTimer){ comboPendingTimer = setTimeout(function() { comboPending = 0; comboPendingTimer = null; req.combo.done(function(mids, url) { var onLoadCallback= function(){ // defQ is a vector of module definitions 1-to-1, onto mids runDefQ(0, mids); checkComplete(); }; combosPending.push(mids); injectingModule = mids; req.injectUrl(url, onLoadCallback, mids); injectingModule = 0; }, req); }, 0); } }); }, contextRequire = function(a1, a2, a3, referenceModule, contextRequire){ var module, syntheticMid; if(isString(a1)){ // signature is (moduleId) module = getModule(a1, referenceModule, true); if(module && module.executed){ return module.result; } throw makeError("undefinedModule", a1); } if(!isArray(a1)){ // a1 is a configuration config(a1, 0, referenceModule); // juggle args; (a2, a3) may be (dependencies, callback) a1 = a2; a2 = a3; } if(isArray(a1)){ // signature is (requestList [,callback]) if(!a1.length){ a2 && a2(); }else{ syntheticMid = "require*" + uid(); // resolve the request list with respect to the reference module for(var mid, deps = [], i = 0; i < a1.length;){ mid = a1[i++]; deps.push(getModule(mid, referenceModule)); } // construct a synthetic module to control execution of the requestList, and, optionally, callback module = mix(makeModuleInfo("", syntheticMid, 0, ""), { injected: arrived, deps: deps, def: a2 || noop, require: referenceModule ? referenceModule.require : req, gc: 1 //garbage collect }); modules[module.mid] = module; // checkComplete!=0 holds the idle signal; we're not idle if we're injecting dependencies injectDependencies(module); // try to immediately execute // if already traversing a factory tree, then strict causes circular dependency to abort the execution; maybe // it's possible to execute this require later after the current traversal completes and avoid the circular dependency. // ...but *always* insist on immediate in synch mode var strict = checkCompleteGuard && legacyMode!=sync; guardCheckComplete(function(){ execModule(module, strict); }); if(!module.executed){ // some deps weren't on board or circular dependency detected and strict; therefore, push into the execQ execQ.push(module); } checkComplete(); } } return contextRequire; }, createRequire = function(module){ if(!module){ return req; } var result = module.require; if(!result){ result = function(a1, a2, a3){ return contextRequire(a1, a2, a3, module, result); }; module.require = mix(result, req); result.module = module; result.toUrl = function(name){ return toUrl(name, module); }; result.toAbsMid = function(mid){ return toAbsMid(mid, module); }; if( 0 ){ result.undef = function(mid){ req.undef(mid, module); }; } if( 1 ){ result.syncLoadNls = function(mid){ var nlsModuleInfo = getModuleInfo(mid, module), nlsModule = modules[nlsModuleInfo.mid]; if(!nlsModule || !nlsModule.executed){ cached = cache[nlsModuleInfo.mid] || cache[urlKeyPrefix + nlsModuleInfo.url]; if(cached){ evalModuleText(cached); nlsModule = modules[nlsModuleInfo.mid]; } } return nlsModule && nlsModule.executed && nlsModule.result; }; } } return result; }, execQ = // The list of modules that need to be evaluated. [], defQ = // The queue of define arguments sent to loader. [], waiting = // The set of modules upon which the loader is waiting for definition to arrive {}, setRequested = function(module){ module.injected = requested; waiting[module.mid] = 1; if(module.url){ waiting[module.url] = module.pack || 1; } startTimer(); }, setArrived = function(module){ module.injected = arrived; delete waiting[module.mid]; if(module.url){ delete waiting[module.url]; } if(isEmpty(waiting)){ clearTimer(); 1 && legacyMode==xd && (legacyMode = sync); } }, execComplete = req.idle = // says the loader has completed (or not) its work function(){ return !defQ.length && isEmpty(waiting) && !execQ.length && !checkCompleteGuard; }, runMapProg = function(targetMid, map){ // search for targetMid in map; return the map item if found; falsy otherwise if(map){ for(var i = 0; i < map.length; i++){ if(map[i][2].test(targetMid)){ return map[i]; } } } return 0; }, compactPath = function(path){ var result = [], segment, lastSegment; path = path.replace(/\\/g, '/').split('/'); while(path.length){ segment = path.shift(); if(segment==".." && result.length && lastSegment!=".."){ result.pop(); lastSegment = result[result.length - 1]; }else if(segment!="."){ result.push(lastSegment= segment); } // else ignore "." } return result.join("/"); }, makeModuleInfo = function(pid, mid, pack, url){ if( 1 ){ var xd= req.isXdUrl(url); return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0, isXd:xd, isAmd:!!(xd || (packs[pid] && packs[pid].isAmd))}; }else{ return {pid:pid, mid:mid, pack:pack, url:url, executed:0, def:0}; } }, getModuleInfo_ = function(mid, referenceModule, packs, modules, baseUrl, mapProgs, pathsMapProg, alwaysCreate){ // arguments are passed instead of using lexical variables so that this function my be used independent of the loader (e.g., the builder) // alwaysCreate is useful in this case so that getModuleInfo never returns references to real modules owned by the loader var pid, pack, midInPackage, mapProg, mapItem, url, result, isRelative, requestedMid; requestedMid = mid; isRelative = /^\./.test(mid); if(/(^\/)|(\:)|(\.js$)/.test(mid) || (isRelative && !referenceModule)){ // absolute path or protocol of .js filetype, or relative path but no reference module and therefore relative to page // whatever it is, it's not a module but just a URL of some sort // note: pid===0 indicates the routine is returning an unmodified mid return makeModuleInfo(0, mid, 0, mid); }else{ // relative module ids are relative to the referenceModule; get rid of any dots mid = compactPath(isRelative ? (referenceModule.mid + "/../" + mid) : mid); if(/^\./.test(mid)){ throw makeError("irrationalPath", mid); } // at this point, mid is an absolute mid // map the mid if(referenceModule){ mapItem = runMapProg(referenceModule.mid, mapProgs); } mapItem = mapItem || mapProgs.star; mapItem = mapItem && runMapProg(mid, mapItem[1]); if(mapItem){ mid = mapItem[1] + mid.substring(mapItem[3]); } match = mid.match(/^([^\/]+)(\/(.+))?$/); pid = match ? match[1] : ""; if((pack = packs[pid])){ mid = pid + "/" + (midInPackage = (match[3] || pack.main)); }else{ pid = ""; } // search aliases var candidateLength = 0, candidate = 0; forEach(aliases, function(pair){ var match = mid.match(pair[0]); if(match && match.length>candidateLength){ candidate = isFunction(pair[1]) ? mid.replace(pair[0], pair[1]) : pair[1]; } }); if(candidate){ return getModuleInfo_(candidate, 0, packs, modules, baseUrl, mapProgs, pathsMapProg, alwaysCreate); } result = modules[mid]; if(result){ return alwaysCreate ? makeModuleInfo(result.pid, result.mid, result.pack, result.url) : modules[mid]; } } // get here iff the sought-after module does not yet exist; therefore, we need to compute the URL given the // fully resolved (i.e., all relative indicators and package mapping resolved) module id // note: pid!==0 indicates the routine is returning a url that has .js appended unmodified mid mapItem = runMapProg(mid, pathsMapProg); if(mapItem){ url = mapItem[1] + mid.substring(mapItem[3]); }else if(pid){ url = pack.location + "/" + midInPackage; }else if(has("config-tlmSiblingOfDojo")){ url = "../" + mid; }else{ url = mid; } // if result is not absolute, add baseUrl if(!(/(^\/)|(\:)/.test(url))){ url = baseUrl + url; } url += ".js"; return makeModuleInfo(pid, mid, pack, compactPath(url)); }, getModuleInfo = function(mid, referenceModule){ return getModuleInfo_(mid, referenceModule, packs, modules, req.baseUrl, mapProgs, pathsMapProg); }, resolvePluginResourceId = function(plugin, prid, referenceModule){ return plugin.normalize ? plugin.normalize(prid, function(mid){return toAbsMid(mid, referenceModule);}) : toAbsMid(prid, referenceModule); }, dynamicPluginUidGenerator = 0, getModule = function(mid, referenceModule, immediate){ // compute and optionally construct (if necessary) the module implied by the mid with respect to referenceModule var match, plugin, prid, result; match = mid.match(/^(.+?)\!(.*)$/); if(match){ // name was ! plugin = getModule(match[1], referenceModule, immediate); if( 1 && legacyMode == sync && !plugin.executed){ injectModule(plugin); if(plugin.injected===arrived && !plugin.executed){ guardCheckComplete(function(){ execModule(plugin); }); } if(plugin.executed){ promoteModuleToPlugin(plugin); }else{ // we are in xdomain mode for some reason execQ.unshift(plugin); } } if(plugin.executed === executed && !plugin.load){ // executed the module not knowing it was a plugin promoteModuleToPlugin(plugin); } // if the plugin has not been loaded, then can't resolve the prid and must assume this plugin is dynamic until we find out otherwise if(plugin.load){ prid = resolvePluginResourceId(plugin, match[2], referenceModule); mid = (plugin.mid + "!" + (plugin.dynamic ? ++dynamicPluginUidGenerator + "!" : "") + prid); }else{ prid = match[2]; mid = plugin.mid + "!" + (++dynamicPluginUidGenerator) + "!waitingForPlugin"; } result = {plugin:plugin, mid:mid, req:createRequire(referenceModule), prid:prid}; }else{ result = getModuleInfo(mid, referenceModule); } return modules[result.mid] || (!immediate && (modules[result.mid] = result)); }, toAbsMid = req.toAbsMid = function(mid, referenceModule){ return getModuleInfo(mid, referenceModule).mid; }, toUrl = req.toUrl = function(name, referenceModule){ var moduleInfo = getModuleInfo(name+"/x", referenceModule), url= moduleInfo.url; return fixupUrl(moduleInfo.pid===0 ? // if pid===0, then name had a protocol or absolute path; either way, toUrl is the identify function in such cases name : // "/x.js" since getModuleInfo automatically appends ".js" and we appended "/x" to make name look likde a module id url.substring(0, url.length-5) ); }, nonModuleProps = { injected: arrived, executed: executed, def: nonmodule, result: nonmodule }, makeCjs = function(mid){ return modules[mid] = mix({mid:mid}, nonModuleProps); }, cjsRequireModule = makeCjs("require"), cjsExportsModule = makeCjs("exports"), cjsModuleModule = makeCjs("module"), runFactory = function(module, args){ req.trace("loader-run-factory", [module.mid]); var factory = module.def, result; 1 && syncExecStack.unshift(module); if(has("config-dojo-loader-catches")){ try{ result= isFunction(factory) ? factory.apply(null, args) : factory; }catch(e){ signal(error, module.result = makeError("factoryThrew", [module, e])); } }else{ result= isFunction(factory) ? factory.apply(null, args) : factory; } module.result = result===undefined && module.cjs ? module.cjs.exports : result; 1 && syncExecStack.shift(module); }, abortExec = {}, defOrder = 0, promoteModuleToPlugin = function(pluginModule){ var plugin = pluginModule.result; pluginModule.dynamic = plugin.dynamic; pluginModule.normalize = plugin.normalize; pluginModule.load = plugin.load; return pluginModule; }, resolvePluginLoadQ = function(plugin){ // plugins is a newly executed module that has a loadQ waiting to run // step 1: traverse the loadQ and fixup the mid and prid; remember the map from original mid to new mid // recall the original mid was created before the plugin was on board and therefore it was impossible to // compute the final mid; accordingly, prid may or may not change, but the mid will definitely change var map = {}; forEach(plugin.loadQ, function(pseudoPluginResource){ // manufacture and insert the real module in modules var prid = resolvePluginResourceId(plugin, pseudoPluginResource.prid, pseudoPluginResource.req.module), mid = plugin.dynamic ? pseudoPluginResource.mid.replace(/waitingForPlugin$/, prid) : (plugin.mid + "!" + prid), pluginResource = mix(mix({}, pseudoPluginResource), {mid:mid, prid:prid, injected:0}); if(!modules[mid]){ // create a new (the real) plugin resource and inject it normally now that the plugin is on board injectPlugin(modules[mid] = pluginResource); } // else this was a duplicate request for the same (plugin, rid) for a nondynamic plugin // pluginResource is really just a placeholder with the wrong mid (because we couldn't calculate it until the plugin was on board) // mark is as arrived and delete it from modules; the real module was requested above map[pseudoPluginResource.mid] = modules[mid]; setArrived(pseudoPluginResource); delete modules[pseudoPluginResource.mid]; }); plugin.loadQ = 0; // step2: replace all references to any placeholder modules with real modules var substituteModules = function(module){ for(var replacement, deps = module.deps || [], i = 0; i")]); return (!module.def || strict) ? abortExec : (module.cjs && module.cjs.exports); } // at this point the module is either not executed or fully executed if(!module.executed){ if(!module.def){ return abortExec; } var mid = module.mid, deps = module.deps || [], arg, argResult, args = [], i = 0; if( 0 ){ circleTrace.push(mid); req.trace("loader-exec-module", ["exec", circleTrace.length, mid]); } // for circular dependencies, assume the first module encountered was executed OK // modules that circularly depend on a module that has not run its factory will get // the premade cjs.exports===module.result. They can take a reference to this object and/or // add properties to it. When the module finally runs its factory, the factory can // read/write/replace this object. Notice that so long as the object isn't replaced, any // reference taken earlier while walking the deps list is still valid. module.executed = executing; while(i < deps.length){ arg = deps[i++]; argResult = ((arg === cjsRequireModule) ? createRequire(module) : ((arg === cjsExportsModule) ? module.cjs.exports : ((arg === cjsModuleModule) ? module.cjs : execModule(arg, strict)))); if(argResult === abortExec){ module.executed = 0; req.trace("loader-exec-module", ["abort", mid]); 0 && circleTrace.pop(); return abortExec; } args.push(argResult); } runFactory(module, args); finishExec(module); 0 && circleTrace.pop(); } // at this point the module is guaranteed fully executed return module.result; }, checkCompleteGuard = 0, guardCheckComplete = function(proc){ try{ checkCompleteGuard++; proc(); }finally{ checkCompleteGuard--; } if(execComplete()){ signal("idle", []); } }, checkComplete = function(){ // keep going through the execQ as long as at least one factory is executed // plugins, recursion, cached modules all make for many execution path possibilities if(checkCompleteGuard){ return; } guardCheckComplete(function(){ checkDojoRequirePlugin(); for(var currentDefOrder, module, i = 0; i < execQ.length;){ currentDefOrder = defOrder; module = execQ[i]; execModule(module); if(currentDefOrder!=defOrder){ // defOrder was bumped one or more times indicating something was executed (note, this indicates // the execQ was modified, maybe a lot (for example a later module causes an earlier module to execute) checkDojoRequirePlugin(); i = 0; }else{ // nothing happened; check the next module in the exec queue i++; } } }); }; if( 0 ){ req.undef = function(moduleId, referenceModule){ // In order to reload a module, it must be undefined (this routine) and then re-requested. // This is useful for testing frameworks (at least). var module = getModule(moduleId, referenceModule); setArrived(module); delete modules[module.mid]; }; } if( 1 ){ if(has("dojo-loader-eval-hint-url")===undefined){ has.add("dojo-loader-eval-hint-url", 1); } var fixupUrl= function(url){ url += ""; // make sure url is a Javascript string (some paths may be a Java string) return url + (cacheBust ? ((/\?/.test(url) ? "&" : "?") + cacheBust) : ""); }, injectPlugin = function( module ){ // injects the plugin module given by module; may have to inject the plugin itself var plugin = module.plugin; if(plugin.executed === executed && !plugin.load){ // executed the module not knowing it was a plugin promoteModuleToPlugin(plugin); } var onLoad = function(def){ module.result = def; setArrived(module); finishExec(module); checkComplete(); }; if(plugin.load){ plugin.load(module.prid, module.req, onLoad); }else if(plugin.loadQ){ plugin.loadQ.push(module); }else{ // the unshift instead of push is important: we don't want plugins to execute as // dependencies of some other module because this may cause circles when the plugin // loadQ is run; also, generally, we want plugins to run early since they may load // several other modules and therefore can potentially unblock many modules plugin.loadQ = [module]; execQ.unshift(plugin); injectModule(plugin); } }, // for IE, injecting a module may result in a recursive execution if the module is in the cache cached = 0, injectingModule = 0, injectingCachedModule = 0, evalModuleText = function(text, module){ // see def() for the injectingCachedModule bracket; it simply causes a short, safe curcuit if(has("config-stripStrict")){ text = text.replace(/"use strict"/g, ''); } injectingCachedModule = 1; if(has("config-dojo-loader-catches")){ try{ if(text===cached){ cached.call(null); }else{ req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid); } }catch(e){ signal(error, makeError("evalModuleThrew", module)); } }else{ if(text===cached){ cached.call(null); }else{ req.eval(text, has("dojo-loader-eval-hint-url") ? module.url : module.mid); } } injectingCachedModule = 0; }, injectModule = function(module){ // Inject the module. In the browser environment, this means appending a script element into // the document; in other environments, it means loading a file. // // If in synchronous mode, then get the module synchronously if it's not xdomainLoading. var mid = module.mid, url = module.url; if(module.executed || module.injected || waiting[mid] || (module.url && ((module.pack && waiting[module.url]===module.pack) || waiting[module.url]==1))){ return; } setRequested(module); if( 0 ){ var viaCombo = 0; if(module.plugin && module.plugin.isCombo){ // a combo plugin; therefore, must be handled by combo service // the prid should have already been converted to a URL (if required by the plugin) during // the normalze process; in any event, there is no way for the loader to know how to // to the conversion; therefore the third argument is zero req.combo.add(module.plugin.mid, module.prid, 0, req); viaCombo = 1; }else if(!module.plugin){ viaCombo = req.combo.add(0, module.mid, module.url, req); } if(viaCombo){ comboPending= 1; return; } } if(module.plugin){ injectPlugin(module); return; } // else a normal module (not a plugin) var onLoadCallback = function(){ runDefQ(module); if(module.injected !== arrived){ // the script that contained the module arrived and has been executed yet // nothing was added to the defQ (so it wasn't an AMD module) and the module // wasn't marked as arrived by dojo.provide (so it wasn't a v1.6- module); // therefore, it must not have been a module; adjust state accordingly setArrived(module); mix(module, nonModuleProps); req.trace("loader-define-nonmodule", [module.url]); } if( 1 && legacyMode){ // must call checkComplete even in for sync loader because we may be in xdomainLoading mode; // but, if xd loading, then don't call checkComplete until out of the current sync traversal // in order to preserve order of execution of the dojo.required modules !syncExecStack.length && checkComplete(); }else{ checkComplete(); } }; cached = cache[mid] || cache[urlKeyPrefix + module.url]; if(cached){ req.trace("loader-inject", ["cache", module.mid, url]); evalModuleText(cached, module); onLoadCallback(); return; } if( 1 && legacyMode){ if(module.isXd){ // switch to async mode temporarily; if current legacyMode!=sync, then is must be one of {legacyAsync, xd, false} legacyMode==sync && (legacyMode = xd); // fall through and load via script injection }else if(module.isAmd && legacyMode!=sync){ // fall through and load via script injection }else{ // mode may be sync, xd/legacyAsync, or async; module may be AMD or legacy; but module is always located on the same domain var xhrCallback = function(text){ if(legacyMode==sync){ // the top of syncExecStack gives the current synchronously executing module; the loader needs // to know this if it has to switch to async loading in the middle of evaluating a legacy module // this happens when a modules dojo.require's a module that must be loaded async because it's xdomain // (using unshift/shift because there is no back() methods for Javascript arrays) syncExecStack.unshift(module); evalModuleText(text, module); syncExecStack.shift(); // maybe the module was an AMD module runDefQ(module); // legacy modules never get to defineModule() => cjs and injected never set; also evaluation implies executing if(!module.cjs){ setArrived(module); finishExec(module); } if(module.finish){ // while synchronously evaluating this module, dojo.require was applied referencing a module // that had to be loaded async; therefore, the loader stopped answering all dojo.require // requests so they could be answered completely in the correct sequence; module.finish gives // the list of dojo.requires that must be re-applied once all target modules are available; // make a synthetic module to execute the dojo.require's in the correct order // compute a guarnateed-unique mid for the synthetic finish module; remember the finish vector; remove it from the reference module // TODO: can we just leave the module.finish...what's it hurting? var finishMid = mid + "*finish", finish = module.finish; delete module.finish; def(finishMid, ["dojo", ("dojo/require!" + finish.join(",")).replace(/\./g, "/")], function(dojo){ forEach(finish, function(mid){ dojo.require(mid); }); }); // unshift, not push, which causes the current traversal to be reattempted from the top execQ.unshift(getModule(finishMid)); } onLoadCallback(); }else{ text = transformToAmd(module, text); if(text){ evalModuleText(text, module); onLoadCallback(); }else{ // if transformToAmd returned falsy, then the module was already AMD and it can be script-injected // do so to improve debugability(even though it means another download...which probably won't happen with a good browser cache) injectingModule = module; req.injectUrl(fixupUrl(url), onLoadCallback, module); injectingModule = 0; } } }; req.trace("loader-inject", ["xhr", module.mid, url, legacyMode!=sync]); if(has("config-dojo-loader-catches")){ try{ req.getText(url, legacyMode!=sync, xhrCallback); }catch(e){ signal(error, makeError("xhrInjectFailed", [module, e])); } }else{ req.getText(url, legacyMode!=sync, xhrCallback); } return; } } // else async mode or fell through in xdomain loading mode; either way, load by script injection req.trace("loader-inject", ["script", module.mid, url]); injectingModule = module; req.injectUrl(fixupUrl(url), onLoadCallback, module); injectingModule = 0; }, defineModule = function(module, deps, def){ req.trace("loader-define-module", [module.mid, deps]); if( 0 && module.plugin && module.plugin.isCombo){ // the module is a plugin resource loaded by the combo service // note: check for module.plugin should be enough since normal plugin resources should // not follow this path; module.plugin.isCombo is future-proofing belt and suspenders module.result = isFunction(def) ? def() : def; setArrived(module); finishExec(module); return module; }; var mid = module.mid; if(module.injected === arrived){ signal(error, makeError("multipleDefine", module)); return module; } mix(module, { deps: deps, def: def, cjs: { id: module.mid, uri: module.url, exports: (module.result = {}), setExports: function(exports){ module.cjs.exports = exports; }, config:function(){ return module.config; } } }); // resolve deps with respect to this module for(var i = 0; i < deps.length; i++){ deps[i] = getModule(deps[i], module); } if( 1 && legacyMode && !waiting[mid]){ // the module showed up without being asked for; it was probably in a // return new NodeList(); // dojo/NodeList }; =====*/ // the query that is returned from this module is slightly different than dojo.query, // because dojo.query has to maintain backwards compatibility with returning a // true array which has performance problems. The query returned from the module // does not use true arrays, but rather inherits from Array, making it much faster to // instantiate. dojo.query = queryForEngine(defaultEngine, function(array){ // call it without the new operator to invoke the back-compat behavior that returns a true array return NodeList(array); // dojo/NodeList }); query.load = function(id, parentRequire, loaded){ // summary: // can be used as AMD plugin to conditionally load new query engine // example: // | require(["dojo/query!custom"], function(qsa){ // | // loaded selector/custom.js as engine // | qsa("#foobar").forEach(...); // | }); loader.load(id, parentRequire, function(engine){ loaded(queryForEngine(engine, NodeList)); }); }; dojo._filterQueryResult = query._filterResult = function(nodes, selector, root){ return new NodeList(query.filter(nodes, selector, root)); }; dojo.NodeList = query.NodeList = NodeList; return query; }); }, 'dojo/has':function(){ define(["require", "module"], function(require, module){ // module: // dojo/has // summary: // Defines the has.js API and several feature tests used by dojo. // description: // This module defines the has API as described by the project has.js with the following additional features: // // - the has test cache is exposed at has.cache. // - the method has.add includes a forth parameter that controls whether or not existing tests are replaced // - the loader's has cache may be optionally copied into this module's has cahce. // // This module adopted from https://github.com/phiggins42/has.js; thanks has.js team! // try to pull the has implementation from the loader; both the dojo loader and bdLoad provide one // if using a foreign loader, then the has cache may be initialized via the config object for this module // WARNING: if a foreign loader defines require.has to be something other than the has.js API, then this implementation fail var has = require.has || function(){}; if(! 1 ){ var isBrowser = // the most fundamental decision: are we in the browser? typeof window != "undefined" && typeof location != "undefined" && typeof document != "undefined" && window.location == location && window.document == document, // has API variables global = this, doc = isBrowser && document, element = doc && doc.createElement("DiV"), cache = (module.config && module.config()) || {}; has = function(name){ // summary: // Return the current value of the named feature. // // name: String|Integer // The name (if a string) or identifier (if an integer) of the feature to test. // // description: // Returns the value of the feature named by name. The feature must have been // previously added to the cache by has.add. return typeof cache[name] == "function" ? (cache[name] = cache[name](global, doc, element)) : cache[name]; // Boolean }; has.cache = cache; has.add = function(name, test, now, force){ // summary: // Register a new feature test for some named feature. // name: String|Integer // The name (if a string) or identifier (if an integer) of the feature to test. // test: Function // A test function to register. If a function, queued for testing until actually // needed. The test function should return a boolean indicating // the presence of a feature or bug. // now: Boolean? // Optional. Omit if `test` is not a function. Provides a way to immediately // run the test and cache the result. // force: Boolean? // Optional. If the test already exists and force is truthy, then the existing // test will be replaced; otherwise, add does not replace an existing test (that // is, by default, the first test advice wins). // example: // A redundant test, testFn with immediate execution: // | has.add("javascript", function(){ return true; }, true); // // example: // Again with the redundantness. You can do this in your tests, but we should // not be doing this in any internal has.js tests // | has.add("javascript", true); // // example: // Three things are passed to the testFunction. `global`, `document`, and a generic element // from which to work your test should the need arise. // | has.add("bug-byid", function(g, d, el){ // | // g == global, typically window, yadda yadda // | // d == document object // | // el == the generic element. a `has` element. // | return false; // fake test, byid-when-form-has-name-matching-an-id is slightly longer // | }); (typeof cache[name]=="undefined" || force) && (cache[name]= test); return now && has(name); }; // since we're operating under a loader that doesn't provide a has API, we must explicitly initialize // has as it would have otherwise been initialized by the dojo loader; use has.add to the builder // can optimize these away iff desired 1 || has.add("host-browser", isBrowser); 1 || has.add("dom", isBrowser); 1 || has.add("dojo-dom-ready-api", 1); 1 || has.add("dojo-sniff", 1); } if( 1 ){ // Common application level tests has.add("dom-addeventlistener", !!document.addEventListener); has.add("touch", "ontouchstart" in document); // I don't know if any of these tests are really correct, just a rough guess has.add("device-width", screen.availWidth || innerWidth); // Tests for DOMNode.attributes[] behavior: // - dom-attributes-explicit - attributes[] only lists explicitly user specified attributes // - dom-attributes-specified-flag (IE8) - need to check attr.specified flag to skip attributes user didn't specify // - Otherwise, in IE6-7. attributes[] will list hundreds of values, so need to do outerHTML to get attrs instead. var form = document.createElement("form"); has.add("dom-attributes-explicit", form.attributes.length == 0); // W3C has.add("dom-attributes-specified-flag", form.attributes.length > 0 && form.attributes.length < 40); // IE8 } has.clearElement = function(element){ // summary: // Deletes the contents of the element passed to test functions. element.innerHTML= ""; return element; }; has.normalize = function(id, toAbsMid){ // summary: // Resolves id into a module id based on possibly-nested tenary expression that branches on has feature test value(s). // // toAbsMid: Function // Resolves a relative module id into an absolute module id var tokens = id.match(/[\?:]|[^:\?]*/g), i = 0, get = function(skip){ var term = tokens[i++]; if(term == ":"){ // empty string module name, resolves to 0 return 0; }else{ // postfixed with a ? means it is a feature to branch on, the term is the name of the feature if(tokens[i++] == "?"){ if(!skip && has(term)){ // matched the feature, get the first value from the options return get(); }else{ // did not match, get the second value, passing over the first get(true); return get(skip); } } // a module return term || 0; } }; id = get(); return id && toAbsMid(id); }; has.load = function(id, parentRequire, loaded){ // summary: // Conditional loading of AMD modules based on a has feature test value. // id: String // Gives the resolved module id to load. // parentRequire: Function // The loader require function with respect to the module that contained the plugin resource in it's // dependency list. // loaded: Function // Callback to loader that consumes result of plugin demand. if(id){ parentRequire([id], loaded); }else{ loaded(); } }; return has; }); }, 'dojo/_base/loader':function(){ define(["./kernel", "../has", "require", "module", "./json", "./lang", "./array"], function(dojo, has, require, thisModule, json, lang, array) { // module: // dojo/_base/loader // This module defines the v1.x synchronous loader API. // signal the loader in sync mode... //>>pure-amd if (! 1 ){ console.error("cannot load the Dojo v1.x loader with a foreign loader"); return 0; } 1 || has.add("dojo-fast-sync-require", 1); var makeErrorToken = function(id){ return {src:thisModule.id, id:id}; }, slashName = function(name){ return name.replace(/\./g, "/"); }, buildDetectRe = /\/\/>>built/, dojoRequireCallbacks = [], dojoRequireModuleStack = [], dojoRequirePlugin = function(mid, require, loaded){ dojoRequireCallbacks.push(loaded); array.forEach(mid.split(","), function(mid){ var module = getModule(mid, require.module); dojoRequireModuleStack.push(module); injectModule(module); }); checkDojoRequirePlugin(); }, checkDojoRequirePlugin = ( 1 ? // This version of checkDojoRequirePlugin makes the observation that all dojoRequireCallbacks can be released // when all *non-dojo/require!, dojo/loadInit!* modules are either executed, not requested, or arrived. This is // the case since there are no more modules the loader is waiting for, therefore, dojo/require! must have // everything it needs on board. // // The potential weakness of this algorithm is that dojo/require will not execute callbacks until *all* dependency // trees are ready. It is possible that some trees may be ready earlier than others, and this extra wait is non-optimal. // Still, for big projects, this seems better than the original algorithm below that proved slow in some cases. // Note, however, the original algorithm had the potential to execute partial trees, but that potential was never enabled. // There are also other optimization available with the original algorithm that have not been explored. function(){ var module, mid; for(mid in modules){ module = modules[mid]; if(module.noReqPluginCheck===undefined){ // tag the module as either a loadInit or require plugin or not for future reference module.noReqPluginCheck = /loadInit\!/.test(mid) || /require\!/.test(mid) ? 1 : 0; } if(!module.executed && !module.noReqPluginCheck && module.injected==requested){ return; } } guardCheckComplete(function(){ var oldCallbacks = dojoRequireCallbacks; dojoRequireCallbacks = []; array.forEach(oldCallbacks, function(cb){cb(1);}); }); } : (function(){ // Note: this is the original checkDojoRequirePlugin that is much slower than the algorithm above. However, we know it // works, so we leave it here in case the algorithm above fails in some corner case. // // checkDojoRequirePlugin inspects all of the modules demanded by a dojo/require! dependency // to see if they have arrived. The loader does not release *any* of these modules to be instantiated // until *all* of these modules are on board, thereby preventing the evaluation of a module with dojo.require's // that reference modules that are not available. // // The algorithm works by traversing the dependency graphs (remember, there can be cycles so they are not trees) // of each module in the dojoRequireModuleStack array (which contains the list of modules demanded by dojo/require!). // The moment a single module is discovered that is missing, the algorithm gives up and indicates that not all // modules are on board. dojo/loadInit! and dojo/require! are ignored because there dependencies are inserted // directly in dojoRequireModuleStack. For example, if "your/module" module depends on "dojo/require!my/module", then // *both* "dojo/require!my/module" and "my/module" will be in dojoRequireModuleStack. Obviously, if "my/module" // is on board, then "dojo/require!my/module" is also satisfied, so the algorithm doesn't check for "dojo/require!my/module". // // Note: inserting a dojo/require! dependency in the dojoRequireModuleStack achieves nothing // with the current algorithm; however, having such modules present makes it possible to optimize the algorithm // // Note: prior versions of this algorithm had an optimization that signaled loaded on dojo/require! dependencies // individually (rather than waiting for them all to be resolved). The implementation proved problematic with cycles // and plugins. However, it is possible to reattach that strategy in the future. // a set from module-id to {undefined | 1 | 0}, where... // undefined => the module has not been inspected // 0 => the module or at least one of its dependencies has not arrived // 1 => the module is a loadInit! or require! plugin resource, or is currently being traversed (therefore, assume // OK until proven otherwise), or has been completely traversed and all dependencies have arrived var touched, traverse = function(m){ touched[m.mid] = 1; for(var t, module, deps = m.deps || [], i= 0; i a built module, always AMD // extractResult==0 => no sync API return 0; } // manufacture a synthetic module id that can never be a real mdule id (just like require does) id = module.mid + "-*loadInit"; // construct the dojo/loadInit names vector which causes any relocated names to be defined as lexical variables under their not-relocated name // the dojo/loadInit plugin assumes the first name in names is "dojo" for(var p in getModule("dojo", module).result.scopeMap){ names.push(p); namesAsStrings.push('"' + p + '"'); } // rewrite the module as a synthetic dojo/loadInit plugin resource + the module expressed as an AMD module that depends on this synthetic resource // don't have to map dojo/init since that will occur when the dependency is resolved return "// xdomain rewrite of " + module.mid + "\n" + "define('" + id + "',{\n" + "\tnames:" + dojo.toJson(names) + ",\n" + "\tdef:function(" + names.join(",") + "){" + extractResult[1] + "}" + "});\n\n" + "define(" + dojo.toJson(names.concat(["dojo/loadInit!"+id])) + ", function(" + names.join(",") + "){\n" + extractResult[0] + "});"; }, loaderVars = require.initSyncLoader(dojoRequirePlugin, checkDojoRequirePlugin, transformToAmd), sync = loaderVars.sync, requested = loaderVars.requested, arrived = loaderVars.arrived, nonmodule = loaderVars.nonmodule, executing = loaderVars.executing, executed = loaderVars.executed, syncExecStack = loaderVars.syncExecStack, modules = loaderVars.modules, execQ = loaderVars.execQ, getModule = loaderVars.getModule, injectModule = loaderVars.injectModule, setArrived = loaderVars.setArrived, signal = loaderVars.signal, finishExec = loaderVars.finishExec, execModule = loaderVars.execModule, getLegacyMode = loaderVars.getLegacyMode, guardCheckComplete = loaderVars.guardCheckComplete; // there is exactly one dojoRequirePlugin among possibly-many dojo/_base/loader's (owing to mapping) dojoRequirePlugin = loaderVars.dojoRequirePlugin; dojo.provide = function(mid){ var executingModule = syncExecStack[0], module = lang.mixin(getModule(slashName(mid), require.module), { executed:executing, result:lang.getObject(mid, true) }); setArrived(module); if(executingModule){ (executingModule.provides || (executingModule.provides = [])).push(function(){ module.result = lang.getObject(mid); delete module.provides; module.executed!==executed && finishExec(module); }); }// else dojo.provide called not consequent to loading; therefore, give up trying to publish module value to loader namespace return module.result; }; has.add("config-publishRequireResult", 1, 0, 0); dojo.require = function(moduleName, omitModuleCheck) { // summary: // loads a Javascript module from the appropriate URI // // moduleName: String // module name to load, using periods for separators, // e.g. "dojo.date.locale". Module paths are de-referenced by dojo's // internal mapping of locations to names and are disambiguated by // longest prefix. See `dojo.registerModulePath()` for details on // registering new modules. // // omitModuleCheck: Boolean? // if `true`, omitModuleCheck skips the step of ensuring that the // loaded file actually defines the symbol it is referenced by. // For example if it called as `dojo.require("a.b.c")` and the // file located at `a/b/c.js` does not define an object `a.b.c`, // and exception will be throws whereas no exception is raised // when called as `dojo.require("a.b.c", true)` // // description: // Modules are loaded via dojo.require by using one of two loaders: the normal loader // and the xdomain loader. The xdomain loader is used when dojo was built with a // custom build that specified loader=xdomain and the module lives on a modulePath // that is a whole URL, with protocol and a domain. The versions of Dojo that are on // the Google and AOL CDNs use the xdomain loader. // // If the module is loaded via the xdomain loader, it is an asynchronous load, since // the module is added via a dynamically created script tag. This // means that dojo.require() can return before the module has loaded. However, this // should only happen in the case where you do dojo.require calls in the top-level // HTML page, or if you purposely avoid the loader checking for dojo.require // dependencies in your module by using a syntax like dojo["require"] to load the module. // // Sometimes it is useful to not have the loader detect the dojo.require calls in the // module so that you can dynamically load the modules as a result of an action on the // page, instead of right at module load time. // // Also, for script blocks in an HTML page, the loader does not pre-process them, so // it does not know to download the modules before the dojo.require calls occur. // // So, in those two cases, when you want on-the-fly module loading or for script blocks // in the HTML page, special care must be taken if the dojo.required code is loaded // asynchronously. To make sure you can execute code that depends on the dojo.required // modules, be sure to add the code that depends on the modules in a dojo.addOnLoad() // callback. dojo.addOnLoad waits for all outstanding modules to finish loading before // executing. // // This type of syntax works with both xdomain and normal loaders, so it is good // practice to always use this idiom for on-the-fly code loading and in HTML script // blocks. If at some point you change loaders and where the code is loaded from, // it will all still work. // // More on how dojo.require // `dojo.require("A.B")` first checks to see if symbol A.B is // defined. If it is, it is simply returned (nothing to do). // // If it is not defined, it will look for `A/B.js` in the script root // directory. // // `dojo.require` throws an exception if it cannot find a file // to load, or if the symbol `A.B` is not defined after loading. // // It returns the object `A.B`, but note the caveats above about on-the-fly loading and // HTML script blocks when the xdomain loader is loading a module. // // `dojo.require()` does nothing about importing symbols into // the current namespace. It is presumed that the caller will // take care of that. // // example: // To use dojo.require in conjunction with dojo.ready: // // | dojo.require("foo"); // | dojo.require("bar"); // | dojo.addOnLoad(function(){ // | //you can now safely do something with foo and bar // | }); // // example: // For example, to import all symbols into a local block, you might write: // // | with (dojo.require("A.B")) { // | ... // | } // // And to import just the leaf symbol to a local variable: // // | var B = dojo.require("A.B"); // | ... // // returns: // the required namespace object function doRequire(mid, omitModuleCheck){ var module = getModule(slashName(mid), require.module); if(syncExecStack.length && syncExecStack[0].finish){ // switched to async loading in the middle of evaluating a legacy module; stop // applying dojo.require so the remaining dojo.requires are applied in order syncExecStack[0].finish.push(mid); return undefined; } // recall module.executed has values {0, executing, executed}; therefore, truthy indicates executing or executed if(module.executed){ return module.result; } omitModuleCheck && (module.result = nonmodule); // rcg...why here and in two lines?? var currentMode = getLegacyMode(); // recall, in sync mode to inject is to *eval* the module text // if the module is a legacy module, this is the same as executing // but if the module is an AMD module, this means defining, not executing injectModule(module); // the inject may have changed the mode currentMode = getLegacyMode(); // in sync mode to dojo.require is to execute if(module.executed!==executed && module.injected===arrived){ // the module was already here before injectModule was called probably finishing up a xdomain // load, but maybe a module given to the loader directly rather than having the loader retrieve it loaderVars.guardCheckComplete(function(){ execModule(module); }); } if(module.executed){ return module.result; } if(currentMode==sync){ // the only way to get here is in sync mode and dojo.required a module that // * was loaded async in the injectModule application a few lines up // * was an AMD module that had deps that are being loaded async and therefore couldn't execute if(module.cjs){ // the module was an AMD module; unshift, not push, which causes the current traversal to be reattempted from the top execQ.unshift(module); }else{ // the module was a legacy module syncExecStack.length && (syncExecStack[0].finish= [mid]); } }else{ // the loader wasn't in sync mode on entry; probably async mode; therefore, no expectation of getting // the module value synchronously; make sure it gets executed though execQ.push(module); } return undefined; } var result = doRequire(moduleName, omitModuleCheck); if(has("config-publishRequireResult") && !lang.exists(moduleName) && result!==undefined){ lang.setObject(moduleName, result); } return result; }; dojo.loadInit = function(f) { f(); }; dojo.registerModulePath = function(/*String*/moduleName, /*String*/prefix){ // summary: // Maps a module name to a path // description: // An unregistered module is given the default path of ../[module], // relative to Dojo root. For example, module acme is mapped to // ../acme. If you want to use a different module name, use // dojo.registerModulePath. // example: // If your dojo.js is located at this location in the web root: // | /myapp/js/dojo/dojo/dojo.js // and your modules are located at: // | /myapp/js/foo/bar.js // | /myapp/js/foo/baz.js // | /myapp/js/foo/thud/xyzzy.js // Your application can tell Dojo to locate the "foo" namespace by calling: // | dojo.registerModulePath("foo", "../../foo"); // At which point you can then use dojo.require() to load the // modules (assuming they provide() the same things which are // required). The full code might be: // | // | var paths = {}; paths[moduleName.replace(/\./g, "/")] = prefix; require({paths:paths}); }; dojo.platformRequire = function(/*Object*/modMap){ // summary: // require one or more modules based on which host environment // Dojo is currently operating in // description: // This method takes a "map" of arrays which one can use to // optionally load dojo modules. The map is indexed by the // possible dojo.name_ values, with two additional values: // "default" and "common". The items in the "default" array will // be loaded if none of the other items have been choosen based on // dojo.name_, set by your host environment. The items in the // "common" array will *always* be loaded, regardless of which // list is chosen. // example: // | dojo.platformRequire({ // | browser: [ // | "foo.sample", // simple module // | "foo.test", // | ["foo.bar.baz", true] // skip object check in _loadModule (dojo.require) // | ], // | default: [ "foo.sample._base" ], // | common: [ "important.module.common" ] // | }); var result = (modMap.common || []).concat(modMap[dojo._name] || modMap["default"] || []), temp; while(result.length){ if(lang.isArray(temp = result.shift())){ dojo.require.apply(dojo, temp); }else{ dojo.require(temp); } } }; dojo.requireIf = dojo.requireAfterIf = function(/*Boolean*/ condition, /*String*/ moduleName, /*Boolean?*/omitModuleCheck){ // summary: // If the condition is true then call `dojo.require()` for the specified // resource // // example: // | dojo.requireIf(dojo.isBrowser, "my.special.Module"); if(condition){ dojo.require(moduleName, omitModuleCheck); } }; dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale){ require(["../i18n"], function(i18n){ i18n.getLocalization(moduleName, bundleName, locale); }); }; return { // summary: // This module defines the v1.x synchronous loader API. extractLegacyApiApplications:extractLegacyApiApplications, require:dojoRequirePlugin, loadInit:dojoLoadInitPlugin }; }); }, 'dojo/json':function(){ define(["./has"], function(has){ "use strict"; var hasJSON = typeof JSON != "undefined"; has.add("json-parse", hasJSON); // all the parsers work fine // Firefox 3.5/Gecko 1.9 fails to use replacer in stringify properly https://bugzilla.mozilla.org/show_bug.cgi?id=509184 has.add("json-stringify", hasJSON && JSON.stringify({a:0}, function(k,v){return v||1;}) == '{"a":1}'); /*===== return { // summary: // Functions to parse and serialize JSON parse: function(str, strict){ // summary: // Parses a [JSON](http://json.org) string to return a JavaScript object. // description: // This function follows [native JSON API](https://developer.mozilla.org/en/JSON) // Throws for invalid JSON strings. This delegates to eval() if native JSON // support is not available. By default this will evaluate any valid JS expression. // With the strict parameter set to true, the parser will ensure that only // valid JSON strings are parsed (otherwise throwing an error). Without the strict // parameter, the content passed to this method must come // from a trusted source. // str: // a string literal of a JSON item, for instance: // `'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'` // strict: // When set to true, this will ensure that only valid, secure JSON is ever parsed. // Make sure this is set to true for untrusted content. Note that on browsers/engines // without native JSON support, setting this to true will run slower. }, stringify: function(value, replacer, spacer){ // summary: // Returns a [JSON](http://json.org) serialization of an object. // description: // Returns a [JSON](http://json.org) serialization of an object. // This function follows [native JSON API](https://developer.mozilla.org/en/JSON) // Note that this doesn't check for infinite recursion, so don't do that! // value: // A value to be serialized. // replacer: // A replacer function that is called for each value and can return a replacement // spacer: // A spacer string to be used for pretty printing of JSON // example: // simple serialization of a trivial object // | define(["dojo/json"], function(JSON){ // | var jsonStr = JSON.stringify({ howdy: "stranger!", isStrange: true }); // | doh.is('{"howdy":"stranger!","isStrange":true}', jsonStr); } }; =====*/ if(has("json-stringify")){ return JSON; }else{ var 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 }; return { parse: has("json-parse") ? JSON.parse : function(str, strict){ if(strict && !/^([\s\[\{]*(?:"(?:\\.|[^"])+"|-?\d[\d\.]*(?:[Ee][+-]?\d+)?|null|true|false|)[\s\]\}]*(?:,|:|$))+$/.test(str)){ throw new SyntaxError("Invalid characters in JSON"); } return eval('(' + str + ')'); }, stringify: function(value, replacer, spacer){ var undef; if(typeof replacer == "string"){ spacer = replacer; replacer = null; } function stringify(it, indent, key){ if(replacer){ it = replacer(key, it); } var val, objtype = typeof it; if(objtype == "number"){ return isFinite(it) ? it + "" : "null"; } if(objtype == "boolean"){ return it + ""; } if(it === null){ return "null"; } if(typeof it == "string"){ return escapeString(it); } if(objtype == "function" || objtype == "undefined"){ return undef; // undefined } // short-circuit for objects that support "json" serialization // if they return "self" then just pass-through... if(typeof it.toJSON == "function"){ return stringify(it.toJSON(key), indent, key); } if(it instanceof Date){ return '"{FullYear}-{Month+}-{Date}T{Hours}:{Minutes}:{Seconds}Z"'.replace(/\{(\w+)(\+)?\}/g, function(t, prop, plus){ var num = it["getUTC" + prop]() + (plus ? 1 : 0); return num < 10 ? "0" + num : num; }); } if(it.valueOf() !== it){ // primitive wrapper, try again unwrapped: return stringify(it.valueOf(), indent, key); } var nextIndent= spacer ? (indent + spacer) : ""; /* we used to test for DOM nodes and throw, but FF serializes them as {}, so cross-browser consistency is probably not efficiently attainable */ var sep = spacer ? " " : ""; var newLine = spacer ? "\n" : ""; // array if(it instanceof Array){ var itl = it.length, res = []; for(key = 0; key < itl; key++){ var obj = it[key]; val = stringify(obj, nextIndent, key); if(typeof val != "string"){ val = "null"; } res.push(newLine + nextIndent + val); } return "[" + res.join(",") + newLine + indent + "]"; } // generic object code path var output = []; for(key in it){ var keyStr; if(it.hasOwnProperty(key)){ if(typeof key == "number"){ keyStr = '"' + key + '"'; }else if(typeof key == "string"){ keyStr = escapeString(key); }else{ // skip non-string or number keys continue; } val = stringify(it[key], nextIndent, key); if(typeof val != "string"){ // skip non-serializable values continue; } // At this point, the most non-IE browsers don't get in this branch // (they have native JSON), so push is definitely the way to output.push(newLine + nextIndent + keyStr + ":" + sep + val); } } return "{" + output.join(",") + newLine + indent + "}"; // String } return stringify(value, "", ""); } }; } }); }, 'dojo/_base/declare':function(){ define(["./kernel", "../has", "./lang"], function(dojo, has, lang){ // module: // dojo/_base/declare var mix = lang.mixin, op = Object.prototype, opts = op.toString, xtor = new Function, counter = 0, cname = "constructor"; function err(msg, cls){ throw new Error("declare" + (cls ? " " + cls : "") + ": " + msg); } // C3 Method Resolution Order (see http://www.python.org/download/releases/2.3/mro/) function c3mro(bases, className){ var result = [], roots = [{cls: 0, refs: []}], nameMap = {}, clsCount = 1, l = bases.length, i = 0, j, lin, base, top, proto, rec, name, refs; // build a list of bases naming them if needed for(; i < l; ++i){ base = bases[i]; if(!base){ err("mixin #" + i + " is unknown. Did you use dojo.require to pull it in?", className); }else if(opts.call(base) != "[object Function]"){ err("mixin #" + i + " is not a callable constructor.", className); } lin = base._meta ? base._meta.bases : [base]; top = 0; // add bases to the name map for(j = lin.length - 1; j >= 0; --j){ proto = lin[j].prototype; if(!proto.hasOwnProperty("declaredClass")){ proto.declaredClass = "uniqName_" + (counter++); } name = proto.declaredClass; if(!nameMap.hasOwnProperty(name)){ nameMap[name] = {count: 0, refs: [], cls: lin[j]}; ++clsCount; } rec = nameMap[name]; if(top && top !== rec){ rec.refs.push(top); ++top.count; } top = rec; } ++top.count; roots[0].refs.push(top); } // remove classes without external references recursively while(roots.length){ top = roots.pop(); result.push(top.cls); --clsCount; // optimization: follow a single-linked chain while(refs = top.refs, refs.length == 1){ top = refs[0]; if(!top || --top.count){ // branch or end of chain => do not end to roots top = 0; break; } result.push(top.cls); --clsCount; } if(top){ // branch for(i = 0, l = refs.length; i < l; ++i){ top = refs[i]; if(!--top.count){ roots.push(top); } } } } if(clsCount){ err("can't build consistent linearization", className); } // calculate the superclass offset base = bases[0]; result[0] = base ? base._meta && base === result[result.length - base._meta.bases.length] ? base._meta.bases.length : 1 : 0; return result; } function inherited(args, a, f){ var name, chains, bases, caller, meta, base, proto, opf, pos, cache = this._inherited = this._inherited || {}; // crack arguments if(typeof args == "string"){ name = args; args = a; a = f; } f = 0; caller = args.callee; name = name || caller.nom; if(!name){ err("can't deduce a name to call inherited()", this.declaredClass); } meta = this.constructor._meta; bases = meta.bases; pos = cache.p; if(name != cname){ // method if(cache.c !== caller){ // cache bust pos = 0; base = bases[0]; meta = base._meta; if(meta.hidden[name] !== caller){ // error detection chains = meta.chains; if(chains && typeof chains[name] == "string"){ err("calling chained method with inherited: " + name, this.declaredClass); } // find caller do{ meta = base._meta; proto = base.prototype; if(meta && (proto[name] === caller && proto.hasOwnProperty(name) || meta.hidden[name] === caller)){ break; } }while(base = bases[++pos]); // intentional assignment pos = base ? pos : -1; } } // find next base = bases[++pos]; if(base){ proto = base.prototype; if(base._meta && proto.hasOwnProperty(name)){ f = proto[name]; }else{ opf = op[name]; do{ proto = base.prototype; f = proto[name]; if(f && (base._meta ? proto.hasOwnProperty(name) : f !== opf)){ break; } }while(base = bases[++pos]); // intentional assignment } } f = base && f || op[name]; }else{ // constructor if(cache.c !== caller){ // cache bust pos = 0; meta = bases[0]._meta; if(meta && meta.ctor !== caller){ // error detection chains = meta.chains; if(!chains || chains.constructor !== "manual"){ err("calling chained constructor with inherited", this.declaredClass); } // find caller while(base = bases[++pos]){ // intentional assignment meta = base._meta; if(meta && meta.ctor === caller){ break; } } pos = base ? pos : -1; } } // find next while(base = bases[++pos]){ // intentional assignment meta = base._meta; f = meta ? meta.ctor : base; if(f){ break; } } f = base && f; } // cache the found super method cache.c = f; cache.p = pos; // now we have the result if(f){ return a === true ? f : f.apply(this, a || args); } // intentionally no return if a super method was not found } function getInherited(name, args){ if(typeof name == "string"){ return this.__inherited(name, args, true); } return this.__inherited(name, true); } function inherited__debug(args, a1, a2){ var f = this.getInherited(args, a1); if(f){ return f.apply(this, a2 || a1 || args); } // intentionally no return if a super method was not found } var inheritedImpl = dojo.config.isDebug ? inherited__debug : inherited; // emulation of "instanceof" function isInstanceOf(cls){ var bases = this.constructor._meta.bases; for(var i = 0, l = bases.length; i < l; ++i){ if(bases[i] === cls){ return true; } } return this instanceof cls; } function mixOwn(target, source){ // add props adding metadata for incoming functions skipping a constructor for(var name in source){ if(name != cname && source.hasOwnProperty(name)){ target[name] = source[name]; } } if(has("bug-for-in-skips-shadowed")){ for(var extraNames= lang._extraNames, i= extraNames.length; i;){ name = extraNames[--i]; if(name != cname && source.hasOwnProperty(name)){ target[name] = source[name]; } } } } // implementation of safe mixin function function safeMixin(target, source){ // summary: // Mix in properties skipping a constructor and decorating functions // like it is done by declare(). // target: Object // Target object to accept new properties. // source: Object // Source object for new properties. // description: // This function is used to mix in properties like lang.mixin does, // but it skips a constructor property and decorates functions like // declare() does. // // It is meant to be used with classes and objects produced with // declare. Functions mixed in with dojo.safeMixin can use // this.inherited() like normal methods. // // This function is used to implement extend() method of a constructor // produced with declare(). // // example: // | var A = declare(null, { // | m1: function(){ // | console.log("A.m1"); // | }, // | m2: function(){ // | console.log("A.m2"); // | } // | }); // | var B = declare(A, { // | m1: function(){ // | this.inherited(arguments); // | console.log("B.m1"); // | } // | }); // | B.extend({ // | m2: function(){ // | this.inherited(arguments); // | console.log("B.m2"); // | } // | }); // | var x = new B(); // | dojo.safeMixin(x, { // | m1: function(){ // | this.inherited(arguments); // | console.log("X.m1"); // | }, // | m2: function(){ // | this.inherited(arguments); // | console.log("X.m2"); // | } // | }); // | x.m2(); // | // prints: // | // A.m1 // | // B.m1 // | // X.m1 var name, t; // add props adding metadata for incoming functions skipping a constructor for(name in source){ t = source[name]; if((t !== op[name] || !(name in op)) && name != cname){ if(opts.call(t) == "[object Function]"){ // non-trivial function method => attach its name t.nom = name; } target[name] = t; } } if(has("bug-for-in-skips-shadowed")){ for(var extraNames= lang._extraNames, i= extraNames.length; i;){ name = extraNames[--i]; t = source[name]; if((t !== op[name] || !(name in op)) && name != cname){ if(opts.call(t) == "[object Function]"){ // non-trivial function method => attach its name t.nom = name; } target[name] = t; } } } return target; } function extend(source){ declare.safeMixin(this.prototype, source); return this; } function createSubclass(mixins){ return declare([this].concat(mixins)); } // chained constructor compatible with the legacy declare() function chainedConstructor(bases, ctorSpecial){ return function(){ var a = arguments, args = a, a0 = a[0], f, i, m, l = bases.length, preArgs; if(!(this instanceof a.callee)){ // not called via new, so force it return applyNew(a); } //this._inherited = {}; // perform the shaman's rituals of the original declare() // 1) call two types of the preamble if(ctorSpecial && (a0 && a0.preamble || this.preamble)){ // full blown ritual preArgs = new Array(bases.length); // prepare parameters preArgs[0] = a; for(i = 0;;){ // process the preamble of the 1st argument a0 = a[0]; if(a0){ f = a0.preamble; if(f){ a = f.apply(this, a) || a; } } // process the preamble of this class f = bases[i].prototype; f = f.hasOwnProperty("preamble") && f.preamble; if(f){ a = f.apply(this, a) || a; } // one peculiarity of the preamble: // it is called if it is not needed, // e.g., there is no constructor to call // let's watch for the last constructor // (see ticket #9795) if(++i == l){ break; } preArgs[i] = a; } } // 2) call all non-trivial constructors using prepared arguments for(i = l - 1; i >= 0; --i){ f = bases[i]; m = f._meta; f = m ? m.ctor : f; if(f){ f.apply(this, preArgs ? preArgs[i] : a); } } // 3) continue the original ritual: call the postscript f = this.postscript; if(f){ f.apply(this, args); } }; } // chained constructor compatible with the legacy declare() function singleConstructor(ctor, ctorSpecial){ return function(){ var a = arguments, t = a, a0 = a[0], f; if(!(this instanceof a.callee)){ // not called via new, so force it return applyNew(a); } //this._inherited = {}; // perform the shaman's rituals of the original declare() // 1) call two types of the preamble if(ctorSpecial){ // full blown ritual if(a0){ // process the preamble of the 1st argument f = a0.preamble; if(f){ t = f.apply(this, t) || t; } } f = this.preamble; if(f){ // process the preamble of this class f.apply(this, t); // one peculiarity of the preamble: // it is called even if it is not needed, // e.g., there is no constructor to call // let's watch for the last constructor // (see ticket #9795) } } // 2) call a constructor if(ctor){ ctor.apply(this, a); } // 3) continue the original ritual: call the postscript f = this.postscript; if(f){ f.apply(this, a); } }; } // plain vanilla constructor (can use inherited() to call its base constructor) function simpleConstructor(bases){ return function(){ var a = arguments, i = 0, f, m; if(!(this instanceof a.callee)){ // not called via new, so force it return applyNew(a); } //this._inherited = {}; // perform the shaman's rituals of the original declare() // 1) do not call the preamble // 2) call the top constructor (it can use this.inherited()) for(; f = bases[i]; ++i){ // intentional assignment m = f._meta; f = m ? m.ctor : f; if(f){ f.apply(this, a); break; } } // 3) call the postscript f = this.postscript; if(f){ f.apply(this, a); } }; } function chain(name, bases, reversed){ return function(){ var b, m, f, i = 0, step = 1; if(reversed){ i = bases.length - 1; step = -1; } for(; b = bases[i]; i += step){ // intentional assignment m = b._meta; f = (m ? m.hidden : b.prototype)[name]; if(f){ f.apply(this, arguments); } } }; } // forceNew(ctor) // return a new object that inherits from ctor.prototype but // without actually running ctor on the object. function forceNew(ctor){ // create object with correct prototype using a do-nothing // constructor xtor.prototype = ctor.prototype; var t = new xtor; xtor.prototype = null; // clean up return t; } // applyNew(args) // just like 'new ctor()' except that the constructor and its arguments come // from args, which must be an array or an arguments object function applyNew(args){ // create an object with ctor's prototype but without // calling ctor on it. var ctor = args.callee, t = forceNew(ctor); // execute the real constructor on the new object ctor.apply(t, args); return t; } function declare(className, superclass, props){ // summary: // Create a feature-rich constructor from compact notation. // className: String? // The optional name of the constructor (loosely, a "class") // stored in the "declaredClass" property in the created prototype. // It will be used as a global name for a created constructor. // superclass: Function|Function[] // May be null, a Function, or an Array of Functions. This argument // specifies a list of bases (the left-most one is the most deepest // base). // props: Object // An object whose properties are copied to the created prototype. // Add an instance-initialization function by making it a property // named "constructor". // returns: dojo/_base/declare.__DeclareCreatedObject // New constructor function. // description: // Create a constructor using a compact notation for inheritance and // prototype extension. // // Mixin ancestors provide a type of multiple inheritance. // Prototypes of mixin ancestors are copied to the new class: // changes to mixin prototypes will not affect classes to which // they have been mixed in. // // Ancestors can be compound classes created by this version of // declare(). In complex cases all base classes are going to be // linearized according to C3 MRO algorithm // (see http://www.python.org/download/releases/2.3/mro/ for more // details). // // "className" is cached in "declaredClass" property of the new class, // if it was supplied. The immediate super class will be cached in // "superclass" property of the new class. // // Methods in "props" will be copied and modified: "nom" property // (the declared name of the method) will be added to all copied // functions to help identify them for the internal machinery. Be // very careful, while reusing methods: if you use the same // function under different names, it can produce errors in some // cases. // // It is possible to use constructors created "manually" (without // declare()) as bases. They will be called as usual during the // creation of an instance, their methods will be chained, and even // called by "this.inherited()". // // Special property "-chains-" governs how to chain methods. It is // a dictionary, which uses method names as keys, and hint strings // as values. If a hint string is "after", this method will be // called after methods of its base classes. If a hint string is // "before", this method will be called before methods of its base // classes. // // If "constructor" is not mentioned in "-chains-" property, it will // be chained using the legacy mode: using "after" chaining, // calling preamble() method before each constructor, if available, // and calling postscript() after all constructors were executed. // If the hint is "after", it is chained as a regular method, but // postscript() will be called after the chain of constructors. // "constructor" cannot be chained "before", but it allows // a special hint string: "manual", which means that constructors // are not going to be chained in any way, and programmer will call // them manually using this.inherited(). In the latter case // postscript() will be called after the construction. // // All chaining hints are "inherited" from base classes and // potentially can be overridden. Be very careful when overriding // hints! Make sure that all chained methods can work in a proposed // manner of chaining. // // Once a method was chained, it is impossible to unchain it. The // only exception is "constructor". You don't need to define a // method in order to supply a chaining hint. // // If a method is chained, it cannot use this.inherited() because // all other methods in the hierarchy will be called automatically. // // Usually constructors and initializers of any kind are chained // using "after" and destructors of any kind are chained as // "before". Note that chaining assumes that chained methods do not // return any value: any returned value will be discarded. // // example: // | declare("my.classes.bar", my.classes.foo, { // | // properties to be added to the class prototype // | someValue: 2, // | // initialization function // | constructor: function(){ // | this.myComplicatedObject = new ReallyComplicatedObject(); // | }, // | // other functions // | someMethod: function(){ // | doStuff(); // | } // | }); // // example: // | var MyBase = declare(null, { // | // constructor, properties, and methods go here // | // ... // | }); // | var MyClass1 = declare(MyBase, { // | // constructor, properties, and methods go here // | // ... // | }); // | var MyClass2 = declare(MyBase, { // | // constructor, properties, and methods go here // | // ... // | }); // | var MyDiamond = declare([MyClass1, MyClass2], { // | // constructor, properties, and methods go here // | // ... // | }); // // example: // | var F = function(){ console.log("raw constructor"); }; // | F.prototype.method = function(){ // | console.log("raw method"); // | }; // | var A = declare(F, { // | constructor: function(){ // | console.log("A.constructor"); // | }, // | method: function(){ // | console.log("before calling F.method..."); // | this.inherited(arguments); // | console.log("...back in A"); // | } // | }); // | new A().method(); // | // will print: // | // raw constructor // | // A.constructor // | // before calling F.method... // | // raw method // | // ...back in A // // example: // | var A = declare(null, { // | "-chains-": { // | destroy: "before" // | } // | }); // | var B = declare(A, { // | constructor: function(){ // | console.log("B.constructor"); // | }, // | destroy: function(){ // | console.log("B.destroy"); // | } // | }); // | var C = declare(B, { // | constructor: function(){ // | console.log("C.constructor"); // | }, // | destroy: function(){ // | console.log("C.destroy"); // | } // | }); // | new C().destroy(); // | // prints: // | // B.constructor // | // C.constructor // | // C.destroy // | // B.destroy // // example: // | var A = declare(null, { // | "-chains-": { // | constructor: "manual" // | } // | }); // | var B = declare(A, { // | constructor: function(){ // | // ... // | // call the base constructor with new parameters // | this.inherited(arguments, [1, 2, 3]); // | // ... // | } // | }); // // example: // | var A = declare(null, { // | "-chains-": { // | m1: "before" // | }, // | m1: function(){ // | console.log("A.m1"); // | }, // | m2: function(){ // | console.log("A.m2"); // | } // | }); // | var B = declare(A, { // | "-chains-": { // | m2: "after" // | }, // | m1: function(){ // | console.log("B.m1"); // | }, // | m2: function(){ // | console.log("B.m2"); // | } // | }); // | var x = new B(); // | x.m1(); // | // prints: // | // B.m1 // | // A.m1 // | x.m2(); // | // prints: // | // A.m2 // | // B.m2 // crack parameters if(typeof className != "string"){ props = superclass; superclass = className; className = ""; } props = props || {}; var proto, i, t, ctor, name, bases, chains, mixins = 1, parents = superclass; // build a prototype if(opts.call(superclass) == "[object Array]"){ // C3 MRO bases = c3mro(superclass, className); t = bases[0]; mixins = bases.length - t; superclass = bases[mixins]; }else{ bases = [0]; if(superclass){ if(opts.call(superclass) == "[object Function]"){ t = superclass._meta; bases = bases.concat(t ? t.bases : superclass); }else{ err("base class is not a callable constructor.", className); } }else if(superclass !== null){ err("unknown base class. Did you use dojo.require to pull it in?", className); } } if(superclass){ for(i = mixins - 1;; --i){ proto = forceNew(superclass); if(!i){ // stop if nothing to add (the last base) break; } // mix in properties t = bases[i]; (t._meta ? mixOwn : mix)(proto, t.prototype); // chain in new constructor ctor = new Function; ctor.superclass = superclass; ctor.prototype = proto; superclass = proto.constructor = ctor; } }else{ proto = {}; } // add all properties declare.safeMixin(proto, props); // add constructor t = props.constructor; if(t !== op.constructor){ t.nom = cname; proto.constructor = t; } // collect chains and flags for(i = mixins - 1; i; --i){ // intentional assignment t = bases[i]._meta; if(t && t.chains){ chains = mix(chains || {}, t.chains); } } if(proto["-chains-"]){ chains = mix(chains || {}, proto["-chains-"]); } // build ctor t = !chains || !chains.hasOwnProperty(cname); bases[0] = ctor = (chains && chains.constructor === "manual") ? simpleConstructor(bases) : (bases.length == 1 ? singleConstructor(props.constructor, t) : chainedConstructor(bases, t)); // add meta information to the constructor ctor._meta = {bases: bases, hidden: props, chains: chains, parents: parents, ctor: props.constructor}; ctor.superclass = superclass && superclass.prototype; ctor.extend = extend; ctor.createSubclass = createSubclass; ctor.prototype = proto; proto.constructor = ctor; // add "standard" methods to the prototype proto.getInherited = getInherited; proto.isInstanceOf = isInstanceOf; proto.inherited = inheritedImpl; proto.__inherited = inherited; // add name if specified if(className){ proto.declaredClass = className; lang.setObject(className, ctor); } // build chains and add them to the prototype if(chains){ for(name in chains){ if(proto[name] && typeof chains[name] == "string" && name != cname){ t = proto[name] = chain(name, bases, chains[name] === "after"); t.nom = name; } } } // chained methods do not return values // no need to chain "invisible" functions return ctor; // Function } /*===== declare.__DeclareCreatedObject = { // summary: // dojo/_base/declare() returns a constructor `C`. `new C()` returns an Object with the following // methods, in addition to the methods and properties specified via the arguments passed to declare(). inherited: function(name, args, newArgs){ // summary: // Calls a super method. // name: String? // The optional method name. Should be the same as the caller's // name. Usually "name" is specified in complex dynamic cases, when // the calling method was dynamically added, undecorated by // declare(), and it cannot be determined. // args: Arguments // The caller supply this argument, which should be the original // "arguments". // newArgs: Object? // If "true", the found function will be returned without // executing it. // If Array, it will be used to call a super method. Otherwise // "args" will be used. // returns: // Whatever is returned by a super method, or a super method itself, // if "true" was specified as newArgs. // description: // This method is used inside method of classes produced with // declare() to call a super method (next in the chain). It is // used for manually controlled chaining. Consider using the regular // chaining, because it is faster. Use "this.inherited()" only in // complex cases. // // This method cannot me called from automatically chained // constructors including the case of a special (legacy) // constructor chaining. It cannot be called from chained methods. // // If "this.inherited()" cannot find the next-in-chain method, it // does nothing and returns "undefined". The last method in chain // can be a default method implemented in Object, which will be // called last. // // If "name" is specified, it is assumed that the method that // received "args" is the parent method for this call. It is looked // up in the chain list and if it is found the next-in-chain method // is called. If it is not found, the first-in-chain method is // called. // // If "name" is not specified, it will be derived from the calling // method (using a methoid property "nom"). // // example: // | var B = declare(A, { // | method1: function(a, b, c){ // | this.inherited(arguments); // | }, // | method2: function(a, b){ // | return this.inherited(arguments, [a + b]); // | } // | }); // | // next method is not in the chain list because it is added // | // manually after the class was created. // | B.prototype.method3 = function(){ // | console.log("This is a dynamically-added method."); // | this.inherited("method3", arguments); // | }; // example: // | var B = declare(A, { // | method: function(a, b){ // | var super = this.inherited(arguments, true); // | // ... // | if(!super){ // | console.log("there is no super method"); // | return 0; // | } // | return super.apply(this, arguments); // | } // | }); return {}; // Object }, getInherited: function(name, args){ // summary: // Returns a super method. // name: String? // The optional method name. Should be the same as the caller's // name. Usually "name" is specified in complex dynamic cases, when // the calling method was dynamically added, undecorated by // declare(), and it cannot be determined. // args: Arguments // The caller supply this argument, which should be the original // "arguments". // returns: // Returns a super method (Function) or "undefined". // description: // This method is a convenience method for "this.inherited()". // It uses the same algorithm but instead of executing a super // method, it returns it, or "undefined" if not found. // // example: // | var B = declare(A, { // | method: function(a, b){ // | var super = this.getInherited(arguments); // | // ... // | if(!super){ // | console.log("there is no super method"); // | return 0; // | } // | return super.apply(this, arguments); // | } // | }); return {}; // Object }, isInstanceOf: function(cls){ // summary: // Checks the inheritance chain to see if it is inherited from this // class. // cls: Function // Class constructor. // returns: // "true", if this object is inherited from this class, "false" // otherwise. // description: // This method is used with instances of classes produced with // declare() to determine of they support a certain interface or // not. It models "instanceof" operator. // // example: // | var A = declare(null, { // | // constructor, properties, and methods go here // | // ... // | }); // | var B = declare(null, { // | // constructor, properties, and methods go here // | // ... // | }); // | var C = declare([A, B], { // | // constructor, properties, and methods go here // | // ... // | }); // | var D = declare(A, { // | // constructor, properties, and methods go here // | // ... // | }); // | // | var a = new A(), b = new B(), c = new C(), d = new D(); // | // | console.log(a.isInstanceOf(A)); // true // | console.log(b.isInstanceOf(A)); // false // | console.log(c.isInstanceOf(A)); // true // | console.log(d.isInstanceOf(A)); // true // | // | console.log(a.isInstanceOf(B)); // false // | console.log(b.isInstanceOf(B)); // true // | console.log(c.isInstanceOf(B)); // true // | console.log(d.isInstanceOf(B)); // false // | // | console.log(a.isInstanceOf(C)); // false // | console.log(b.isInstanceOf(C)); // false // | console.log(c.isInstanceOf(C)); // true // | console.log(d.isInstanceOf(C)); // false // | // | console.log(a.isInstanceOf(D)); // false // | console.log(b.isInstanceOf(D)); // false // | console.log(c.isInstanceOf(D)); // false // | console.log(d.isInstanceOf(D)); // true return {}; // Object }, extend: function(source){ // summary: // Adds all properties and methods of source to constructor's // prototype, making them available to all instances created with // constructor. This method is specific to constructors created with // declare(). // source: Object // Source object which properties are going to be copied to the // constructor's prototype. // description: // Adds source properties to the constructor's prototype. It can // override existing properties. // // This method is similar to dojo.extend function, but it is specific // to constructors produced by declare(). It is implemented // using dojo.safeMixin, and it skips a constructor property, // and properly decorates copied functions. // // example: // | var A = declare(null, { // | m1: function(){}, // | s1: "Popokatepetl" // | }); // | A.extend({ // | m1: function(){}, // | m2: function(){}, // | f1: true, // | d1: 42 // | }); } }; =====*/ // For back-compat, remove for 2.0 dojo.safeMixin = declare.safeMixin = safeMixin; dojo.declare = declare; return declare; }); }, 'dojo/dom':function(){ define(["./sniff", "./_base/lang", "./_base/window"], function(has, lang, win){ // module: // dojo/dom // FIXME: need to add unit tests for all the semi-public methods if(has("ie") <= 7){ try{ document.execCommand("BackgroundImageCache", false, true); }catch(e){ // sane browsers don't have cache "issues" } } // ============================= // DOM Functions // ============================= // the result object var dom = { // summary: // This module defines the core dojo DOM API. }; if(has("ie")){ dom.byId = function(id, doc){ if(typeof id != "string"){ return id; } var _d = doc || win.doc, te = id && _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{ dom.byId = function(id, doc){ // inline'd type check. // be sure to return null per documentation, to match IE branch. return ((typeof id == "string") ? (doc || win.doc).getElementById(id) : id) || null; // DOMNode }; } /*===== dom.byId = function(id, doc){ // summary: // Returns DOM node with matching `id` attribute or falsy value (ex: null or undefined) // if not found. If `id` is a DomNode, this function is a no-op. // // id: String|DOMNode // A string to match an HTML id attribute or a reference to a DOM Node // // doc: Document? // Document to work in. Defaults to the current value of // dojo.doc. Can be used to retrieve // node references from other documents. // // example: // Look up a node by ID: // | var n = dojo.byId("foo"); // // example: // Check if a node exists, and use it. // | var n = dojo.byId("bar"); // | if(n){ doStuff() ... } // // example: // Allow string or DomNode references to be passed to a custom function: // | var foo = function(nodeOrId){ // | nodeOrId = dojo.byId(nodeOrId); // | // ... more stuff // | } }; =====*/ dom.isDescendant = function(/*DOMNode|String*/ node, /*DOMNode|String*/ ancestor){ // summary: // Returns true if node is a descendant of ancestor // node: DOMNode|String // string id or node reference to test // ancestor: DOMNode|String // string id or node reference of potential parent to test against // // example: // Test is node id="bar" is a descendant of node id="foo" // | if(dojo.isDescendant("bar", "foo")){ ... } try{ node = dom.byId(node); ancestor = dom.byId(ancestor); while(node){ if(node == ancestor){ return true; // Boolean } node = node.parentNode; } }catch(e){ /* squelch, return false */ } return false; // Boolean }; // TODO: do we need this function in the base? dom.setSelectable = function(/*DOMNode|String*/ node, /*Boolean*/ selectable){ // summary: // Enable or disable selection on a node // node: DOMNode|String // id or reference to node // selectable: Boolean // state to put the node in. false indicates unselectable, true // allows selection. // example: // Make the node id="bar" unselectable // | dojo.setSelectable("bar"); // example: // Make the node id="bar" selectable // | dojo.setSelectable("bar", true); node = dom.byId(node); if(has("mozilla")){ node.style.MozUserSelect = selectable ? "" : "none"; }else if(has("khtml") || has("webkit")){ node.style.KhtmlUserSelect = selectable ? "auto" : "none"; }else if(has("ie")){ var v = (node.unselectable = selectable ? "" : "on"), cs = node.getElementsByTagName("*"), i = 0, l = cs.length; for(; i < l; ++i){ cs.item(i).unselectable = v; } } //FIXME: else? Opera? }; return dom; }); }, 'dojo/_base/browser':function(){ if(require.has){ require.has.add("config-selectorEngine", "acme"); } define([ "../ready", "./kernel", "./connect", // until we decide if connect is going back into non-browser environments "./unload", "./window", "./event", "./html", "./NodeList", "../query", "./xhr", "./fx"], function(dojo){ // module: // dojo/_base/browser /*===== return { // summary: // This module causes the browser-only base modules to be loaded. }; =====*/ return dojo; }); }, 'dojo/selector/acme':function(){ define([ "../dom", "../sniff", "../_base/array", "../_base/lang", "../_base/window" ], function(dom, has, array, lang, win){ // module: // dojo/selector/acme /* acme architectural overview: acme is a relatively full-featured CSS3 query library. It is designed to take any valid CSS3 selector and return the nodes matching the selector. To do this quickly, it processes queries in several steps, applying caching where profitable. The steps (roughly in reverse order of the way they appear in the code): 1.) check to see if we already have a "query dispatcher" - if so, use that with the given parameterization. Skip to step 4. 2.) attempt to determine which branch to dispatch the query to: - JS (optimized DOM iteration) - native (FF3.1+, Safari 3.1+, IE 8+) 3.) tokenize and convert to executable "query dispatcher" - this is where the lion's share of the complexity in the system lies. In the DOM version, the query dispatcher is assembled as a chain of "yes/no" test functions pertaining to a section of a simple query statement (".blah:nth-child(odd)" but not "div div", which is 2 simple statements). Individual statement dispatchers are cached (to prevent re-definition) as are entire dispatch chains (to make re-execution of the same query fast) 4.) the resulting query dispatcher is called in the passed scope (by default the top-level document) - for DOM queries, this results in a recursive, top-down evaluation of nodes based on each simple query section - for native implementations, this may mean working around spec bugs. So be it. 5.) matched nodes are pruned to ensure they are unique (if necessary) */ //////////////////////////////////////////////////////////////////////// // Toolkit aliases //////////////////////////////////////////////////////////////////////// // if you are extracting acme for use in your own system, you will // need to provide these methods and properties. No other porting should be // necessary, save for configuring the system to use a class other than // dojo/NodeList as the return instance instantiator var trim = lang.trim; var each = array.forEach; var getDoc = function(){ return win.doc; }; // NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo var cssCaseBug = (getDoc().compatMode) == "BackCompat"; //////////////////////////////////////////////////////////////////////// // Global utilities //////////////////////////////////////////////////////////////////////// var specials = ">~+"; // global thunk to determine whether we should treat the current query as // case sensitive or not. This switch is flipped by the query evaluator // based on the document passed as the context to search. var caseSensitive = false; // how high? var yesman = function(){ return true; }; //////////////////////////////////////////////////////////////////////// // Tokenizer //////////////////////////////////////////////////////////////////////// var getQueryParts = function(query){ // summary: // state machine for query tokenization // description: // instead of using a brittle and slow regex-based CSS parser, // acme implements an AST-style query representation. This // representation is only generated once per query. For example, // the same query run multiple times or under different root nodes // does not re-parse the selector expression but instead uses the // cached data structure. The state machine implemented here // terminates on the last " " (space) character and returns an // ordered array of query component structures (or "parts"). Each // part represents an operator or a simple CSS filtering // expression. The structure for parts is documented in the code // below. // NOTE: // this code is designed to run fast and compress well. Sacrifices // to readability and maintainability have been made. Your best // bet when hacking the tokenizer is to put The Donnas on *really* // loud (may we recommend their "Spend The Night" release?) and // just assume you're gonna make mistakes. Keep the unit tests // open and run them frequently. Knowing is half the battle ;-) if(specials.indexOf(query.slice(-1)) >= 0){ // if we end with a ">", "+", or "~", that means we're implicitly // searching all children, so make it explicit query += " * "; }else{ // if you have not provided a terminator, one will be provided for // you... query += " "; } var ts = function(/*Integer*/ s, /*Integer*/ e){ // trim and slice. // take an index to start a string slice from and an end position // and return a trimmed copy of that sub-string return trim(query.slice(s, e)); }; // the overall data graph of the full query, as represented by queryPart objects var queryParts = []; // state keeping vars var inBrackets = -1, inParens = -1, inMatchFor = -1, inPseudo = -1, inClass = -1, inId = -1, inTag = -1, currentQuoteChar, lc = "", cc = "", pStart; // iteration vars var x = 0, // index in the query ql = query.length, currentPart = null, // data structure representing the entire clause _cp = null; // the current pseudo or attr matcher // several temporary variables are assigned to this structure during a // potential sub-expression match: // attr: // a string representing the current full attribute match in a // bracket expression // type: // if there's an operator in a bracket expression, this is // used to keep track of it // value: // the internals of parenthetical expression for a pseudo. for // :nth-child(2n+1), value might be "2n+1" var endTag = function(){ // called when the tokenizer hits the end of a particular tag name. // Re-sets state variables for tag matching and sets up the matcher // to handle the next type of token (tag or operator). if(inTag >= 0){ var tv = (inTag == x) ? null : ts(inTag, x); // .toLowerCase(); currentPart[ (specials.indexOf(tv) < 0) ? "tag" : "oper" ] = tv; inTag = -1; } }; var endId = function(){ // called when the tokenizer might be at the end of an ID portion of a match if(inId >= 0){ currentPart.id = ts(inId, x).replace(/\\/g, ""); inId = -1; } }; var endClass = function(){ // called when the tokenizer might be at the end of a class name // match. CSS allows for multiple classes, so we augment the // current item with another class in its list if(inClass >= 0){ currentPart.classes.push(ts(inClass + 1, x).replace(/\\/g, "")); inClass = -1; } }; var endAll = function(){ // at the end of a simple fragment, so wall off the matches endId(); endTag(); endClass(); }; var endPart = function(){ endAll(); if(inPseudo >= 0){ currentPart.pseudos.push({ name: ts(inPseudo + 1, x) }); } // hint to the selector engine to tell it whether or not it // needs to do any iteration. Many simple selectors don't, and // we can avoid significant construction-time work by advising // the system to skip them currentPart.loops = ( currentPart.pseudos.length || currentPart.attrs.length || currentPart.classes.length ); currentPart.oquery = currentPart.query = ts(pStart, x); // save the full expression as a string // otag/tag are hints to suggest to the system whether or not // it's an operator or a tag. We save a copy of otag since the // tag name is cast to upper-case in regular HTML matches. The // system has a global switch to figure out if the current // expression needs to be case sensitive or not and it will use // otag or tag accordingly currentPart.otag = currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*"); if(currentPart.tag){ // if we're in a case-insensitive HTML doc, we likely want // the toUpperCase when matching on element.tagName. If we // do it here, we can skip the string op per node // comparison currentPart.tag = currentPart.tag.toUpperCase(); } // add the part to the list if(queryParts.length && (queryParts[queryParts.length-1].oper)){ // operators are always infix, so we remove them from the // list and attach them to the next match. The evaluator is // responsible for sorting out how to handle them. currentPart.infixOper = queryParts.pop(); currentPart.query = currentPart.infixOper.query + " " + currentPart.query; /* console.debug( "swapping out the infix", currentPart.infixOper, "and attaching it to", currentPart); */ } queryParts.push(currentPart); currentPart = null; }; // iterate over the query, character by character, building up a // list of query part objects for(; lc=cc, cc=query.charAt(x), x < ql; x++){ // cc: the current character in the match // lc: the last character (if any) // someone is trying to escape something, so don't try to match any // fragments. We assume we're inside a literal. if(lc == "\\"){ continue; } if(!currentPart){ // a part was just ended or none has yet been created // NOTE: I hate all this alloc, but it's shorter than writing tons of if's pStart = x; // rules describe full CSS sub-expressions, like: // #someId // .className:first-child // but not: // thinger > div.howdy[type=thinger] // the indidual components of the previous query would be // split into 3 parts that would be represented a structure like: // [ // { // query: "thinger", // tag: "thinger", // }, // { // query: "div.howdy[type=thinger]", // classes: ["howdy"], // infixOper: { // query: ">", // oper: ">", // } // }, // ] currentPart = { query: null, // the full text of the part's rule pseudos: [], // CSS supports multiple pseud-class matches in a single rule attrs: [], // CSS supports multi-attribute match, so we need an array classes: [], // class matches may be additive, e.g.: .thinger.blah.howdy tag: null, // only one tag... oper: null, // ...or operator per component. Note that these wind up being exclusive. id: null, // the id component of a rule getTag: function(){ return caseSensitive ? this.otag : this.tag; } }; // if we don't have a part, we assume we're going to start at // the beginning of a match, which should be a tag name. This // might fault a little later on, but we detect that and this // iteration will still be fine. inTag = x; } // Skip processing all quoted characters. // If we are inside quoted text then currentQuoteChar stores the character that began the quote, // thus that character that will end it. if(currentQuoteChar){ if(cc == currentQuoteChar){ currentQuoteChar = null; } continue; }else if (cc == "'" || cc == '"'){ currentQuoteChar = cc; continue; } if(inBrackets >= 0){ // look for a the close first if(cc == "]"){ // if we're in a [...] clause and we end, do assignment if(!_cp.attr){ // no attribute match was previously begun, so we // assume this is an attribute existence match in the // form of [someAttributeName] _cp.attr = ts(inBrackets+1, x); }else{ // we had an attribute already, so we know that we're // matching some sort of value, as in [attrName=howdy] _cp.matchFor = ts((inMatchFor||inBrackets+1), x); } var cmf = _cp.matchFor; if(cmf){ // try to strip quotes from the matchFor value. We want // [attrName=howdy] to match the same // as [attrName = 'howdy' ] if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){ _cp.matchFor = cmf.slice(1, -1); } } // remove backslash escapes from an attribute match, since DOM // querying will get attribute values without backslashes if(_cp.matchFor){ _cp.matchFor = _cp.matchFor.replace(/\\/g, ""); } // end the attribute by adding it to the list of attributes. currentPart.attrs.push(_cp); _cp = null; // necessary? inBrackets = inMatchFor = -1; }else if(cc == "="){ // if the last char was an operator prefix, make sure we // record it along with the "=" operator. var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : ""; _cp.type = addToCc+cc; _cp.attr = ts(inBrackets+1, x-addToCc.length); inMatchFor = x+1; } // now look for other clause parts }else if(inParens >= 0){ // if we're in a parenthetical expression, we need to figure // out if it's attached to a pseudo-selector rule like // :nth-child(1) if(cc == ")"){ if(inPseudo >= 0){ _cp.value = ts(inParens+1, x); } inPseudo = inParens = -1; } }else if(cc == "#"){ // start of an ID match endAll(); inId = x+1; }else if(cc == "."){ // start of a class match endAll(); inClass = x; }else if(cc == ":"){ // start of a pseudo-selector match endAll(); inPseudo = x; }else if(cc == "["){ // start of an attribute match. endAll(); inBrackets = x; // provide a new structure for the attribute match to fill-in _cp = { /*===== attr: null, type: null, matchFor: null =====*/ }; }else if(cc == "("){ // we really only care if we've entered a parenthetical // expression if we're already inside a pseudo-selector match if(inPseudo >= 0){ // provide a new structure for the pseudo match to fill-in _cp = { name: ts(inPseudo+1, x), value: null }; currentPart.pseudos.push(_cp); } inParens = x; }else if( (cc == " ") && // if it's a space char and the last char is too, consume the // current one without doing more work (lc != cc) ){ endPart(); } } return queryParts; }; //////////////////////////////////////////////////////////////////////// // DOM query infrastructure //////////////////////////////////////////////////////////////////////// var agree = function(first, second){ // the basic building block of the yes/no chaining system. agree(f1, // f2) generates a new function which returns the boolean results of // both of the passed functions to a single logical-anded result. If // either are not passed, the other is used exclusively. if(!first){ return second; } if(!second){ return first; } return function(){ return first.apply(window, arguments) && second.apply(window, arguments); }; }; var getArr = function(i, arr){ // helps us avoid array alloc when we don't need it var r = arr||[]; // FIXME: should this be 'new d._NodeListCtor()' ? if(i){ r.push(i); } return r; }; var _isElement = function(n){ return (1 == n.nodeType); }; // FIXME: need to coalesce _getAttr with defaultGetter var blank = ""; var _getAttr = function(elem, attr){ if(!elem){ return blank; } if(attr == "class"){ return elem.className || blank; } if(attr == "for"){ return elem.htmlFor || blank; } if(attr == "style"){ return elem.style.cssText || blank; } return (caseSensitive ? elem.getAttribute(attr) : elem.getAttribute(attr, 2)) || blank; }; var attrs = { "*=": function(attr, value){ return function(elem){ // E[foo*="bar"] // an E element whose "foo" attribute value contains // the substring "bar" return (_getAttr(elem, attr).indexOf(value)>=0); }; }, "^=": function(attr, value){ // E[foo^="bar"] // an E element whose "foo" attribute value begins exactly // with the string "bar" return function(elem){ return (_getAttr(elem, attr).indexOf(value)==0); }; }, "$=": function(attr, value){ // E[foo$="bar"] // an E element whose "foo" attribute value ends exactly // with the string "bar" return function(elem){ var ea = " "+_getAttr(elem, attr); var lastIndex = ea.lastIndexOf(value); return lastIndex > -1 && (lastIndex==(ea.length-value.length)); }; }, "~=": function(attr, value){ // E[foo~="bar"] // an E element whose "foo" attribute value is a list of // space-separated values, one of which is exactly equal // to "bar" // return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]"; var tval = " "+value+" "; return function(elem){ var ea = " "+_getAttr(elem, attr)+" "; return (ea.indexOf(tval)>=0); }; }, "|=": function(attr, value){ // E[hreflang|="en"] // an E element whose "hreflang" attribute has a // hyphen-separated list of values beginning (from the // left) with "en" var valueDash = value+"-"; return function(elem){ var ea = _getAttr(elem, attr); return ( (ea == value) || (ea.indexOf(valueDash)==0) ); }; }, "=": function(attr, value){ return function(elem){ return (_getAttr(elem, attr) == value); }; } }; // avoid testing for node type if we can. Defining this in the negative // here to avoid negation in the fast path. var _noNES = (typeof getDoc().firstChild.nextElementSibling == "undefined"); var _ns = !_noNES ? "nextElementSibling" : "nextSibling"; var _ps = !_noNES ? "previousElementSibling" : "previousSibling"; var _simpleNodeTest = (_noNES ? _isElement : yesman); var _lookLeft = function(node){ // look left while(node = node[_ps]){ if(_simpleNodeTest(node)){ return false; } } return true; }; var _lookRight = function(node){ // look right while(node = node[_ns]){ if(_simpleNodeTest(node)){ return false; } } return true; }; var getNodeIndex = function(node){ var root = node.parentNode; root = root.nodeType != 7 ? root : root.nextSibling; // PROCESSING_INSTRUCTION_NODE var i = 0, tret = root.children || root.childNodes, ci = (node["_i"]||node.getAttribute("_i")||-1), cl = (root["_l"]|| (typeof root.getAttribute !== "undefined" ? root.getAttribute("_l") : -1)); if(!tret){ return -1; } var l = tret.length; // we calculate the parent length as a cheap way to invalidate the // cache. It's not 100% accurate, but it's much more honest than what // other libraries do if( cl == l && ci >= 0 && cl >= 0 ){ // if it's legit, tag and release return ci; } // else re-key things if(has("ie") && typeof root.setAttribute !== "undefined"){ root.setAttribute("_l", l); }else{ root["_l"] = l; } ci = -1; for(var te = root["firstElementChild"]||root["firstChild"]; te; te = te[_ns]){ if(_simpleNodeTest(te)){ if(has("ie")){ te.setAttribute("_i", ++i); }else{ te["_i"] = ++i; } if(node === te){ // NOTE: // shortcutting the return at this step in indexing works // very well for benchmarking but we avoid it here since // it leads to potential O(n^2) behavior in sequential // getNodexIndex operations on a previously un-indexed // parent. We may revisit this at a later time, but for // now we just want to get the right answer more often // than not. ci = i; } } } return ci; }; var isEven = function(elem){ return !((getNodeIndex(elem)) % 2); }; var isOdd = function(elem){ return ((getNodeIndex(elem)) % 2); }; var pseudos = { "checked": function(name, condition){ return function(elem){ return !!("checked" in elem ? elem.checked : elem.selected); }; }, "disabled": function(name, condition){ return function(elem){ return elem.disabled; }; }, "enabled": function(name, condition){ return function(elem){ return !elem.disabled; }; }, "first-child": function(){ return _lookLeft; }, "last-child": function(){ return _lookRight; }, "only-child": function(name, condition){ return function(node){ return _lookLeft(node) && _lookRight(node); }; }, "empty": function(name, condition){ return function(elem){ // DomQuery and jQuery get this wrong, oddly enough. // The CSS 3 selectors spec is pretty explicit about it, too. var cn = elem.childNodes; var cnl = elem.childNodes.length; // if(!cnl){ return true; } for(var x=cnl-1; x >= 0; x--){ var nt = cn[x].nodeType; if((nt === 1)||(nt == 3)){ return false; } } return true; }; }, "contains": function(name, condition){ var cz = condition.charAt(0); if( cz == '"' || cz == "'" ){ //remove quote condition = condition.slice(1, -1); } return function(elem){ return (elem.innerHTML.indexOf(condition) >= 0); }; }, "not": function(name, condition){ var p = getQueryParts(condition)[0]; var ignores = { el: 1 }; if(p.tag != "*"){ ignores.tag = 1; } if(!p.classes.length){ ignores.classes = 1; } var ntf = getSimpleFilterFunc(p, ignores); return function(elem){ return (!ntf(elem)); }; }, "nth-child": function(name, condition){ var pi = parseInt; // avoid re-defining function objects if we can if(condition == "odd"){ return isOdd; }else if(condition == "even"){ return isEven; } // FIXME: can we shorten this? if(condition.indexOf("n") != -1){ var tparts = condition.split("n", 2); var pred = tparts[0] ? ((tparts[0] == '-') ? -1 : pi(tparts[0])) : 1; var idx = tparts[1] ? pi(tparts[1]) : 0; var lb = 0, ub = -1; if(pred > 0){ if(idx < 0){ idx = (idx % pred) && (pred + (idx % pred)); }else if(idx>0){ if(idx >= pred){ lb = idx - idx % pred; } idx = idx % pred; } }else if(pred<0){ pred *= -1; // idx has to be greater than 0 when pred is negative; // shall we throw an error here? if(idx > 0){ ub = idx; idx = idx % pred; } } if(pred > 0){ return function(elem){ var i = getNodeIndex(elem); return (i>=lb) && (ub<0 || i<=ub) && ((i % pred) == idx); }; }else{ condition = idx; } } var ncount = pi(condition); return function(elem){ return (getNodeIndex(elem) == ncount); }; } }; var defaultGetter = (has("ie") && (has("ie") < 9 || has("quirks"))) ? function(cond){ var clc = cond.toLowerCase(); if(clc == "class"){ cond = "className"; } return function(elem){ return (caseSensitive ? elem.getAttribute(cond) : elem[cond]||elem[clc]); }; } : function(cond){ return function(elem){ return (elem && elem.getAttribute && elem.hasAttribute(cond)); }; }; var getSimpleFilterFunc = function(query, ignores){ // generates a node tester function based on the passed query part. The // query part is one of the structures generated by the query parser // when it creates the query AST. The "ignores" object specifies which // (if any) tests to skip, allowing the system to avoid duplicating // work where it may have already been taken into account by other // factors such as how the nodes to test were fetched in the first // place if(!query){ return yesman; } ignores = ignores||{}; var ff = null; if(!("el" in ignores)){ ff = agree(ff, _isElement); } if(!("tag" in ignores)){ if(query.tag != "*"){ ff = agree(ff, function(elem){ return (elem && ((caseSensitive ? elem.tagName : elem.tagName.toUpperCase()) == query.getTag())); }); } } if(!("classes" in ignores)){ each(query.classes, function(cname, idx, arr){ // get the class name /* var isWildcard = cname.charAt(cname.length-1) == "*"; if(isWildcard){ cname = cname.substr(0, cname.length-1); } // I dislike the regex thing, even if memoized in a cache, but it's VERY short var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)"); */ var re = new RegExp("(?:^|\\s)" + cname + "(?:\\s|$)"); ff = agree(ff, function(elem){ return re.test(elem.className); }); ff.count = idx; }); } if(!("pseudos" in ignores)){ each(query.pseudos, function(pseudo){ var pn = pseudo.name; if(pseudos[pn]){ ff = agree(ff, pseudos[pn](pn, pseudo.value)); } }); } if(!("attrs" in ignores)){ each(query.attrs, function(attr){ var matcher; var a = attr.attr; // type, attr, matchFor if(attr.type && attrs[attr.type]){ matcher = attrs[attr.type](a, attr.matchFor); }else if(a.length){ matcher = defaultGetter(a); } if(matcher){ ff = agree(ff, matcher); } }); } if(!("id" in ignores)){ if(query.id){ ff = agree(ff, function(elem){ return (!!elem && (elem.id == query.id)); }); } } if(!ff){ if(!("default" in ignores)){ ff = yesman; } } return ff; }; var _nextSibling = function(filterFunc){ return function(node, ret, bag){ while(node = node[_ns]){ if(_noNES && (!_isElement(node))){ continue; } if( (!bag || _isUnique(node, bag)) && filterFunc(node) ){ ret.push(node); } break; } return ret; }; }; var _nextSiblings = function(filterFunc){ return function(root, ret, bag){ var te = root[_ns]; while(te){ if(_simpleNodeTest(te)){ if(bag && !_isUnique(te, bag)){ break; } if(filterFunc(te)){ ret.push(te); } } te = te[_ns]; } return ret; }; }; // get an array of child *elements*, skipping text and comment nodes var _childElements = function(filterFunc){ filterFunc = filterFunc||yesman; return function(root, ret, bag){ // get an array of child elements, skipping text and comment nodes var te, x = 0, tret = root.children || root.childNodes; while(te = tret[x++]){ if( _simpleNodeTest(te) && (!bag || _isUnique(te, bag)) && (filterFunc(te, x)) ){ ret.push(te); } } return ret; }; }; // test to see if node is below root var _isDescendant = function(node, root){ var pn = node.parentNode; while(pn){ if(pn == root){ break; } pn = pn.parentNode; } return !!pn; }; var _getElementsFuncCache = {}; var getElementsFunc = function(query){ var retFunc = _getElementsFuncCache[query.query]; // if we've got a cached dispatcher, just use that if(retFunc){ return retFunc; } // else, generate a new on // NOTE: // this function returns a function that searches for nodes and // filters them. The search may be specialized by infix operators // (">", "~", or "+") else it will default to searching all // descendants (the " " selector). Once a group of children is // found, a test function is applied to weed out the ones we // don't want. Many common cases can be fast-pathed. We spend a // lot of cycles to create a dispatcher that doesn't do more work // than necessary at any point since, unlike this function, the // dispatchers will be called every time. The logic of generating // efficient dispatchers looks like this in pseudo code: // // # if it's a purely descendant query (no ">", "+", or "~" modifiers) // if infixOperator == " ": // if only(id): // return def(root): // return d.byId(id, root); // // elif id: // return def(root): // return filter(d.byId(id, root)); // // elif cssClass && getElementsByClassName: // return def(root): // return filter(root.getElementsByClassName(cssClass)); // // elif only(tag): // return def(root): // return root.getElementsByTagName(tagName); // // else: // # search by tag name, then filter // return def(root): // return filter(root.getElementsByTagName(tagName||"*")); // // elif infixOperator == ">": // # search direct children // return def(root): // return filter(root.children); // // elif infixOperator == "+": // # search next sibling // return def(root): // return filter(root.nextElementSibling); // // elif infixOperator == "~": // # search rightward siblings // return def(root): // return filter(nextSiblings(root)); var io = query.infixOper; var oper = (io ? io.oper : ""); // the default filter func which tests for all conditions in the query // part. This is potentially inefficient, so some optimized paths may // re-define it to test fewer things. var filterFunc = getSimpleFilterFunc(query, { el: 1 }); var qt = query.tag; var wildcardTag = ("*" == qt); var ecs = getDoc()["getElementsByClassName"]; if(!oper){ // if there's no infix operator, then it's a descendant query. ID // and "elements by class name" variants can be accelerated so we // call them out explicitly: if(query.id){ // testing shows that the overhead of yesman() is acceptable // and can save us some bytes vs. re-defining the function // everywhere. filterFunc = (!query.loops && wildcardTag) ? yesman : getSimpleFilterFunc(query, { el: 1, id: 1 }); retFunc = function(root, arr){ var te = dom.byId(query.id, (root.ownerDocument||root)); if(!te || !filterFunc(te)){ return; } if(9 == root.nodeType){ // if root's a doc, we just return directly return getArr(te, arr); }else{ // otherwise check ancestry if(_isDescendant(te, root)){ return getArr(te, arr); } } }; }else if( ecs && // isAlien check. Workaround for Prototype.js being totally evil/dumb. /\{\s*\[native code\]\s*\}/.test(String(ecs)) && query.classes.length && !cssCaseBug ){ // it's a class-based query and we've got a fast way to run it. // ignore class and ID filters since we will have handled both filterFunc = getSimpleFilterFunc(query, { el: 1, classes: 1, id: 1 }); var classesString = query.classes.join(" "); retFunc = function(root, arr, bag){ var ret = getArr(0, arr), te, x=0; var tret = root.getElementsByClassName(classesString); while((te = tret[x++])){ if(filterFunc(te, root) && _isUnique(te, bag)){ ret.push(te); } } return ret; }; }else if(!wildcardTag && !query.loops){ // it's tag only. Fast-path it. retFunc = function(root, arr, bag){ var ret = getArr(0, arr), te, x=0; var tag = query.getTag(), tret = tag ? root.getElementsByTagName(tag) : []; while((te = tret[x++])){ if(_isUnique(te, bag)){ ret.push(te); } } return ret; }; }else{ // the common case: // a descendant selector without a fast path. By now it's got // to have a tag selector, even if it's just "*" so we query // by that and filter filterFunc = getSimpleFilterFunc(query, { el: 1, tag: 1, id: 1 }); retFunc = function(root, arr, bag){ var ret = getArr(0, arr), te, x=0; // we use getTag() to avoid case sensitivity issues var tag = query.getTag(), tret = tag ? root.getElementsByTagName(tag) : []; while((te = tret[x++])){ if(filterFunc(te, root) && _isUnique(te, bag)){ ret.push(te); } } return ret; }; } }else{ // the query is scoped in some way. Instead of querying by tag we // use some other collection to find candidate nodes var skipFilters = { el: 1 }; if(wildcardTag){ skipFilters.tag = 1; } filterFunc = getSimpleFilterFunc(query, skipFilters); if("+" == oper){ retFunc = _nextSibling(filterFunc); }else if("~" == oper){ retFunc = _nextSiblings(filterFunc); }else if(">" == oper){ retFunc = _childElements(filterFunc); } } // cache it and return return _getElementsFuncCache[query.query] = retFunc; }; var filterDown = function(root, queryParts){ // NOTE: // this is the guts of the DOM query system. It takes a list of // parsed query parts and a root and finds children which match // the selector represented by the parts var candidates = getArr(root), qp, x, te, qpl = queryParts.length, bag, ret; for(var i = 0; i < qpl; i++){ ret = []; qp = queryParts[i]; x = candidates.length - 1; if(x > 0){ // if we have more than one root at this level, provide a new // hash to use for checking group membership but tell the // system not to post-filter us since we will already have been // guaranteed to be unique bag = {}; ret.nozip = true; } var gef = getElementsFunc(qp); for(var j = 0; (te = candidates[j]); j++){ // for every root, get the elements that match the descendant // selector, adding them to the "ret" array and filtering them // via membership in this level's bag. If there are more query // parts, then this level's return will be used as the next // level's candidates gef(te, ret, bag); } if(!ret.length){ break; } candidates = ret; } return ret; }; //////////////////////////////////////////////////////////////////////// // the query runner //////////////////////////////////////////////////////////////////////// // these are the primary caches for full-query results. The query // dispatcher functions are generated then stored here for hash lookup in // the future var _queryFuncCacheDOM = {}, _queryFuncCacheQSA = {}; // this is the second level of splitting, from full-length queries (e.g., // "div.foo .bar") into simple query expressions (e.g., ["div.foo", // ".bar"]) var getStepQueryFunc = function(query){ var qparts = getQueryParts(trim(query)); // if it's trivial, avoid iteration and zipping costs if(qparts.length == 1){ // we optimize this case here to prevent dispatch further down the // chain, potentially slowing things down. We could more elegantly // handle this in filterDown(), but it's slower for simple things // that need to be fast (e.g., "#someId"). var tef = getElementsFunc(qparts[0]); return function(root){ var r = tef(root, []); if(r){ r.nozip = true; } return r; }; } // otherwise, break it up and return a runner that iterates over the parts recursively return function(root){ return filterDown(root, qparts); }; }; // NOTES: // * we can't trust QSA for anything but document-rooted queries, so // caching is split into DOM query evaluators and QSA query evaluators // * caching query results is dirty and leak-prone (or, at a minimum, // prone to unbounded growth). Other toolkits may go this route, but // they totally destroy their own ability to manage their memory // footprint. If we implement it, it should only ever be with a fixed // total element reference # limit and an LRU-style algorithm since JS // has no weakref support. Caching compiled query evaluators is also // potentially problematic, but even on large documents the size of the // query evaluators is often < 100 function objects per evaluator (and // LRU can be applied if it's ever shown to be an issue). // * since IE's QSA support is currently only for HTML documents and even // then only in IE 8's "standards mode", we have to detect our dispatch // route at query time and keep 2 separate caches. Ugg. // we need to determine if we think we can run a given query via // querySelectorAll or if we'll need to fall back on DOM queries to get // there. We need a lot of information about the environment and the query // to make the determination (e.g. does it support QSA, does the query in // question work in the native QSA impl, etc.). // IE QSA queries may incorrectly include comment nodes, so we throw the // zipping function into "remove" comments mode instead of the normal "skip // it" which every other QSA-clued browser enjoys var noZip = has("ie") ? "commentStrip" : "nozip"; var qsa = "querySelectorAll"; var qsaAvail = !!getDoc()[qsa]; //Don't bother with n+3 type of matches, IE complains if we modify those. var infixSpaceRe = /\\[>~+]|n\+\d|([^ \\])?([>~+])([^ =])?/g; var infixSpaceFunc = function(match, pre, ch, post){ return ch ? (pre ? pre + " " : "") + ch + (post ? " " + post : "") : /*n+3*/ match; }; //Don't apply the infixSpaceRe to attribute value selectors var attRe = /([^[]*)([^\]]*])?/g; var attFunc = function(match, nonAtt, att){ return nonAtt.replace(infixSpaceRe, infixSpaceFunc) + (att||""); }; var getQueryFunc = function(query, forceDOM){ //Normalize query. The CSS3 selectors spec allows for omitting spaces around //infix operators, >, ~ and + //Do the work here since detection for spaces is used as a simple "not use QSA" //test below. query = query.replace(attRe, attFunc); if(qsaAvail){ // if we've got a cached variant and we think we can do it, run it! var qsaCached = _queryFuncCacheQSA[query]; if(qsaCached && !forceDOM){ return qsaCached; } } // else if we've got a DOM cached variant, assume that we already know // all we need to and use it var domCached = _queryFuncCacheDOM[query]; if(domCached){ return domCached; } // TODO: // today we're caching DOM and QSA branches separately so we // recalc useQSA every time. If we had a way to tag root+query // efficiently, we'd be in good shape to do a global cache. var qcz = query.charAt(0); var nospace = (-1 == query.indexOf(" ")); // byId searches are wicked fast compared to QSA, even when filtering // is required if( (query.indexOf("#") >= 0) && (nospace) ){ forceDOM = true; } var useQSA = ( qsaAvail && (!forceDOM) && // as per CSS 3, we can't currently start w/ combinator: // http://www.w3.org/TR/css3-selectors/#w3cselgrammar (specials.indexOf(qcz) == -1) && // IE's QSA impl sucks on pseudos (!has("ie") || (query.indexOf(":") == -1)) && (!(cssCaseBug && (query.indexOf(".") >= 0))) && // FIXME: // need to tighten up browser rules on ":contains" and "|=" to // figure out which aren't good // Latest webkit (around 531.21.8) does not seem to do well with :checked on option // elements, even though according to spec, selected options should // match :checked. So go nonQSA for it: // http://bugs.dojotoolkit.org/ticket/5179 (query.indexOf(":contains") == -1) && (query.indexOf(":checked") == -1) && (query.indexOf("|=") == -1) // some browsers don't grok it ); // TODO: // if we've got a descendant query (e.g., "> .thinger" instead of // just ".thinger") in a QSA-able doc, but are passed a child as a // root, it should be possible to give the item a synthetic ID and // trivially rewrite the query to the form "#synid > .thinger" to // use the QSA branch if(useQSA){ var tq = (specials.indexOf(query.charAt(query.length-1)) >= 0) ? (query + " *") : query; return _queryFuncCacheQSA[query] = function(root){ try{ // the QSA system contains an egregious spec bug which // limits us, effectively, to only running QSA queries over // entire documents. See: // http://ejohn.org/blog/thoughts-on-queryselectorall/ // despite this, we can also handle QSA runs on simple // selectors, but we don't want detection to be expensive // so we're just checking for the presence of a space char // right now. Not elegant, but it's cheaper than running // the query parser when we might not need to if(!((9 == root.nodeType) || nospace)){ throw ""; } var r = root[qsa](tq); // skip expensive duplication checks and just wrap in a NodeList r[noZip] = true; return r; }catch(e){ // else run the DOM branch on this query, ensuring that we // default that way in the future return getQueryFunc(query, true)(root); } }; }else{ // DOM branch var parts = query.match(/([^\s,](?:"(?:\\.|[^"])+"|'(?:\\.|[^'])+'|[^,])*)/g); return _queryFuncCacheDOM[query] = ((parts.length < 2) ? // if not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher getStepQueryFunc(query) : // if it *is* a complex query, break it up into its // constituent parts and return a dispatcher that will // merge the parts when run function(root){ var pindex = 0, // avoid array alloc for every invocation ret = [], tp; while((tp = parts[pindex++])){ ret = ret.concat(getStepQueryFunc(tp)(root)); } return ret; } ); } }; var _zipIdx = 0; // NOTE: // this function is Moo inspired, but our own impl to deal correctly // with XML in IE var _nodeUID = has("ie") ? function(node){ if(caseSensitive){ // XML docs don't have uniqueID on their nodes return (node.getAttribute("_uid") || node.setAttribute("_uid", ++_zipIdx) || _zipIdx); }else{ return node.uniqueID; } } : function(node){ return (node._uid || (node._uid = ++_zipIdx)); }; // determine if a node in is unique in a "bag". In this case we don't want // to flatten a list of unique items, but rather just tell if the item in // question is already in the bag. Normally we'd just use hash lookup to do // this for us but IE's DOM is busted so we can't really count on that. On // the upside, it gives us a built in unique ID function. var _isUnique = function(node, bag){ if(!bag){ return 1; } var id = _nodeUID(node); if(!bag[id]){ return bag[id] = 1; } return 0; }; // attempt to efficiently determine if an item in a list is a dupe, // returning a list of "uniques", hopefully in document order var _zipIdxName = "_zipIdx"; var _zip = function(arr){ if(arr && arr.nozip){ return arr; } var ret = []; if(!arr || !arr.length){ return ret; } if(arr[0]){ ret.push(arr[0]); } if(arr.length < 2){ return ret; } _zipIdx++; // we have to fork here for IE and XML docs because we can't set // expandos on their nodes (apparently). *sigh* var x, te; if(has("ie") && caseSensitive){ var szidx = _zipIdx+""; arr[0].setAttribute(_zipIdxName, szidx); for(x = 1; te = arr[x]; x++){ if(arr[x].getAttribute(_zipIdxName) != szidx){ ret.push(te); } te.setAttribute(_zipIdxName, szidx); } }else if(has("ie") && arr.commentStrip){ try{ for(x = 1; te = arr[x]; x++){ if(_isElement(te)){ ret.push(te); } } }catch(e){ /* squelch */ } }else{ if(arr[0]){ arr[0][_zipIdxName] = _zipIdx; } for(x = 1; te = arr[x]; x++){ if(arr[x][_zipIdxName] != _zipIdx){ ret.push(te); } te[_zipIdxName] = _zipIdx; } } return ret; }; // the main executor var query = function(/*String*/ query, /*String|DOMNode?*/ root){ // summary: // Returns nodes which match the given CSS3 selector, searching the // entire document by default but optionally taking a node to scope // the search by. Returns an array. // description: // dojo.query() is the swiss army knife of DOM node manipulation in // Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's // "$" function, dojo.query provides robust, high-performance // CSS-based node selector support with the option of scoping searches // to a particular sub-tree of a document. // // Supported Selectors: // -------------------- // // acme supports a rich set of CSS3 selectors, including: // // - class selectors (e.g., `.foo`) // - node type selectors like `span` // - ` ` descendant selectors // - `>` child element selectors // - `#foo` style ID selectors // - `*` universal selector // - `~`, the preceded-by sibling selector // - `+`, the immediately preceded-by sibling selector // - attribute queries: // - `[foo]` attribute presence selector // - `[foo='bar']` attribute value exact match // - `[foo~='bar']` attribute value list item match // - `[foo^='bar']` attribute start match // - `[foo$='bar']` attribute end match // - `[foo*='bar']` attribute substring match // - `:first-child`, `:last-child`, and `:only-child` positional selectors // - `:empty` content emtpy selector // - `:checked` pseudo selector // - `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations // - `:nth-child(even)`, `:nth-child(odd)` positional selectors // - `:not(...)` negation pseudo selectors // // Any legal combination of these selectors will work with // `dojo.query()`, including compound selectors ("," delimited). // Very complex and useful searches can be constructed with this // palette of selectors and when combined with functions for // manipulation presented by dojo/NodeList, many types of DOM // manipulation operations become very straightforward. // // Unsupported Selectors: // ---------------------- // // While dojo.query handles many CSS3 selectors, some fall outside of // what's reasonable for a programmatic node querying engine to // handle. Currently unsupported selectors include: // // - namespace-differentiated selectors of any form // - all `::` pseduo-element selectors // - certain pseudo-selectors which don't get a lot of day-to-day use: // - `:root`, `:lang()`, `:target`, `:focus` // - all visual and state selectors: // - `:root`, `:active`, `:hover`, `:visited`, `:link`, // `:enabled`, `:disabled` // - `:*-of-type` pseudo selectors // // dojo.query and XML Documents: // ----------------------------- // // `dojo.query` (as of dojo 1.2) supports searching XML documents // in a case-sensitive manner. If an HTML document is served with // a doctype that forces case-sensitivity (e.g., XHTML 1.1 // Strict), dojo.query() will detect this and "do the right // thing". Case sensitivity is dependent upon the document being // searched and not the query used. It is therefore possible to // use case-sensitive queries on strict sub-documents (iframes, // etc.) or XML documents while still assuming case-insensitivity // for a host/root document. // // Non-selector Queries: // --------------------- // // If something other than a String is passed for the query, // `dojo.query` will return a new `dojo/NodeList` instance // constructed from that parameter alone and all further // processing will stop. This means that if you have a reference // to a node or NodeList, you can quickly construct a new NodeList // from the original by calling `dojo.query(node)` or // `dojo.query(list)`. // // query: // The CSS3 expression to match against. For details on the syntax of // CSS3 selectors, see // root: // A DOMNode (or node id) to scope the search from. Optional. // returns: Array // example: // search the entire document for elements with the class "foo": // | dojo.query(".foo"); // these elements will match: // | // | // |

// example: // search the entire document for elements with the classes "foo" *and* "bar": // | dojo.query(".foo.bar"); // these elements will match: // | // while these will not: // | // |

// example: // find `` elements which are descendants of paragraphs and // which have a "highlighted" class: // | dojo.query("p span.highlighted"); // the innermost span in this fragment matches: // |

// | ... // | ... // | // |

// example: // set an "odd" class on all odd table rows inside of the table // `#tabular_data`, using the `>` (direct child) selector to avoid // affecting any nested tables: // | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd"); // example: // remove all elements with the class "error" from the document // and store them in a list: // | var errors = dojo.query(".error").orphan(); // example: // add an onclick handler to every submit button in the document // which causes the form to be sent via Ajax instead: // | dojo.query("input[type='submit']").onclick(function(e){ // | dojo.stopEvent(e); // prevent sending the form // | var btn = e.target; // | dojo.xhrPost({ // | form: btn.form, // | load: function(data){ // | // replace the form with the response // | var div = dojo.doc.createElement("div"); // | dojo.place(div, btn.form, "after"); // | div.innerHTML = data; // | dojo.style(btn.form, "display", "none"); // | } // | }); // | }); root = root || getDoc(); // throw the big case sensitivity switch var od = root.ownerDocument || root; // root is either Document or a node inside the document caseSensitive = (od.createElement("div").tagName === "div"); // NOTE: // adding "true" as the 2nd argument to getQueryFunc is useful for // testing the DOM branch without worrying about the // behavior/performance of the QSA branch. var r = getQueryFunc(query)(root); // FIXME: // need to investigate this branch WRT #8074 and #8075 if(r && r.nozip){ return r; } return _zip(r); // dojo/NodeList }; query.filter = function(/*Node[]*/ nodeList, /*String*/ filter, /*String|DOMNode?*/ root){ // summary: // function for filtering a NodeList based on a selector, optimized for simple selectors var tmpNodeList = [], parts = getQueryParts(filter), filterFunc = (parts.length == 1 && !/[^\w#\.]/.test(filter)) ? getSimpleFilterFunc(parts[0]) : function(node){ return array.indexOf(query(filter, dom.byId(root)), node) != -1; }; for(var x = 0, te; te = nodeList[x]; x++){ if(filterFunc(te)){ tmpNodeList.push(te); } } return tmpNodeList; }; return query; }); }, 'dojo/errors/RequestTimeoutError':function(){ define("dojo/errors/RequestTimeoutError", ['./create', './RequestError'], function(create, RequestError){ // module: // dojo/errors/RequestTimeoutError /*===== return function(){ // summary: // TODOC }; =====*/ return create("RequestTimeoutError", null, RequestError, { dojoType: "timeout" }); }); }, 'dojo/dom-style':function(){ define("dojo/dom-style", ["./sniff", "./dom"], function(has, dom){ // module: // dojo/dom-style // ============================= // 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. // 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 getComputedStyle, style = { // summary: // This module defines the core dojo DOM style API. }; if(has("webkit")){ getComputedStyle = 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(has("ie") && (has("ie") < 9 || has("quirks"))){ getComputedStyle = function(node){ // IE (as of 7) doesn't expose Element like sane browsers // currentStyle can be null on IE8! return node.nodeType == 1 /* ELEMENT_NODE*/ && node.currentStyle ? node.currentStyle : {}; }; }else{ getComputedStyle = function(node){ return node.nodeType == 1 /* ELEMENT_NODE*/ ? node.ownerDocument.defaultView.getComputedStyle(node, null) : {}; }; } style.getComputedStyle = getComputedStyle; /*===== style.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 }; =====*/ var toPixel; if(!has("ie")){ toPixel = function(element, value){ // style values can be floats, client code may want // to round for integer pixels. return parseFloat(value) || 0; }; }else{ toPixel = 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); } var s = element.style, rs = element.runtimeStyle, cs = element.currentStyle, sLeft = s.left, rsLeft = rs.left; rs.left = cs.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 s.left = avalue; avalue = s.pixelLeft; }catch(e){ avalue = 0; } s.left = sLeft; rs.left = rsLeft; return avalue; }; } style.toPixelValue = toPixel; /*===== style.toPixelValue = function(node, value){ // summary: // converts style value to pixels on IE or return a numeric value. // node: DOMNode // value: String // returns: Number }; =====*/ // FIXME: there opacity quirks on FF that we haven't ported over. Hrm. var astr = "DXImageTransform.Microsoft.Alpha"; var af = function(n, f){ try{ return n.filters.item(astr); }catch(e){ return f ? {} : null; } }; var _getOpacity = has("ie") < 9 || (has("ie") && has("quirks")) ? function(node){ try{ return af(node).Opacity / 100; // Number }catch(e){ return 1; // Number } } : function(node){ return getComputedStyle(node).opacity; }; var _setOpacity = has("ie") < 9 || (has("ie") && has("quirks")) ? function(/*DomNode*/ node, /*Number*/ opacity){ var ov = opacity * 100, opaque = opacity == 1; node.style.zoom = opaque ? "" : 1; if(!af(node)){ if(opaque){ return opacity; } node.style.filter += " progid:" + astr + "(Opacity=" + ov + ")"; }else{ af(node, 1).Opacity = ov; } // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so disable it altogether (bug #2661), //but still update the opacity value so we can get a correct reading if it is read later. af(node, 1).Enabled = !opaque; if(node.tagName.toLowerCase() == "tr"){ for(var td = node.firstChild; td; td = td.nextSibling){ if(td.tagName.toLowerCase() == "td"){ _setOpacity(td, 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 function _toStyleValue(node, type, value){ //TODO: should we really be doing string case conversion here? Should we cache it? Need to profile! type = type.toLowerCase(); if(has("ie")){ 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] ? toPixel(node, value) : value; } var _floatStyle = has("ie") ? "styleFloat" : "cssFloat", _floatAliases = {"cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle}; // public API style.get = function getStyle(/*DOMNode|String*/ node, /*String?*/ name){ // summary: // Accesses styles on a node. // description: // Getting the style value uses the computed style for the node, so the value // will be a calculated value, not just the immediate node.style value. // Also when getting values, use specific style names, // like "borderBottomWidth" instead of "border" since compound values like // "border" are not necessarily reflected as expected. // If you want to get node dimensions, use `dojo.marginBox()`, // `dojo.contentBox()` or `dojo.position()`. // node: DOMNode|String // id or reference to node to get style for // name: String? // the style property to get // example: // Passing only an ID or node returns the computed style object of // the node: // | dojo.getStyle("thinger"); // example: // Passing a node and a style property returns the current // normalized, computed value for that property: // | dojo.getStyle("thinger", "opacity"); // 1 by default var n = dom.byId(node), l = arguments.length, op = (name == "opacity"); if(l == 2 && op){ return _getOpacity(n); } name = _floatAliases[name] || name; var s = style.getComputedStyle(n); return (l == 1) ? s : _toStyleValue(n, name, s[name] || n.style[name]); /* CSS2Properties||String||Number */ }; style.set = function setStyle(/*DOMNode|String*/ node, /*String|Object*/ name, /*String?*/ value){ // summary: // Sets styles on a node. // node: DOMNode|String // id or reference to node to set style for // name: String|Object // 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: String? // 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 a node, a style property, and a value changes the // current display of the node and returns the new computed value // | dojo.setStyle("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.setStyle("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.setStyle("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 = dom.byId(node), l = arguments.length, op = (name == "opacity"); name = _floatAliases[name] || name; if(l == 3){ return op ? _setOpacity(n, value) : n.style[name] = value; // Number } for(var x in name){ style.set(node, x, name[x]); } return style.getComputedStyle(n); }; return style; }); }, 'dojo/dom-geometry':function(){ define(["./sniff", "./_base/window","./dom", "./dom-style"], function(has, win, dom, style){ // module: // dojo/dom-geometry // the result object var geom = { // summary: // This module defines the core dojo DOM geometry API. }; // 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) geom.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(has("ie") /*|| has("opera")*/){ // client code may have to adjust if compatMode varies across iframes geom.boxModel = document.compatMode == "BackCompat" ? "border-box" : "content-box"; } geom.getPadExtents = function getPadExtents(/*DomNode*/ node, /*Object*/ computedStyle){ // summary: // Returns object with special values specifically useful for node // fitting. // description: // Returns an object with `w`, `h`, `l`, `t` properties: // | l/t/r/b = left/top/right/bottom 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. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo.getComputedStyle to get one. It is a better way, calling // dojo.computedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), px = style.toPixelValue, l = px(node, s.paddingLeft), t = px(node, s.paddingTop), r = px(node, s.paddingRight), b = px(node, s.paddingBottom); return {l: l, t: t, r: r, b: b, w: l + r, h: t + b}; }; var none = "none"; geom.getBorderExtents = function getBorderExtents(/*DomNode*/ node, /*Object*/ computedStyle){ // summary: // returns an object with properties useful for noting the border // dimensions. // description: // - l/t/r/b = the sum of left/top/right/bottom 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. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo.getComputedStyle to get one. It is a better way, calling // dojo.computedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var px = style.toPixelValue, s = computedStyle || style.getComputedStyle(node), l = s.borderLeftStyle != none ? px(node, s.borderLeftWidth) : 0, t = s.borderTopStyle != none ? px(node, s.borderTopWidth) : 0, r = s.borderRightStyle != none ? px(node, s.borderRightWidth) : 0, b = s.borderBottomStyle != none ? px(node, s.borderBottomWidth) : 0; return {l: l, t: t, r: r, b: b, w: l + r, h: t + b}; }; geom.getPadBorderExtents = function getPadBorderExtents(/*DomNode*/ node, /*Object*/ computedStyle){ // summary: // Returns object with properties useful for box fitting with // regards to padding. // description: // - l/t/r/b = the sum of left/top/right/bottom padding and left/top/right/bottom 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. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo.getComputedStyle to get one. It is a better way, calling // dojo.computedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), p = geom.getPadExtents(node, s), b = geom.getBorderExtents(node, s); return { l: p.l + b.l, t: p.t + b.t, r: p.r + b.r, b: p.b + b.b, w: p.w + b.w, h: p.h + b.h }; }; geom.getMarginExtents = function getMarginExtents(node, 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. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo.getComputedStyle to get one. It is a better way, calling // dojo.computedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), px = style.toPixelValue, l = px(node, s.marginLeft), t = px(node, s.marginTop), r = px(node, s.marginRight), b = px(node, s.marginBottom); return {l: l, t: t, r: r, b: b, 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 geom.getMarginBox = function getMarginBox(/*DomNode*/ node, /*Object*/ computedStyle){ // summary: // returns an object that encodes the width, height, left and top // positions of the node's margin box. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo.getComputedStyle to get one. It is a better way, calling // dojo.computedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), me = geom.getMarginExtents(node, s), l = node.offsetLeft - me.l, t = node.offsetTop - me.t, p = node.parentNode, px = style.toPixelValue, pcs; if(has("mozilla")){ // 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){ pcs = style.getComputedStyle(p); if(pcs.overflow != "visible"){ l += pcs.borderLeftStyle != none ? px(node, pcs.borderLeftWidth) : 0; t += pcs.borderTopStyle != none ? px(node, pcs.borderTopWidth) : 0; } } } }else if(has("opera") || (has("ie") == 8 && !has("quirks"))){ // On Opera and IE 8, offsetLeft/Top includes the parent's border if(p){ pcs = style.getComputedStyle(p); l -= pcs.borderLeftStyle != none ? px(node, pcs.borderLeftWidth) : 0; t -= pcs.borderTopStyle != none ? px(node, pcs.borderTopWidth) : 0; } } return {l: l, t: t, w: node.offsetWidth + me.w, h: node.offsetHeight + me.h}; }; geom.getContentBox = function getContentBox(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. // node: DOMNode // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo.getComputedStyle to get one. It is a better way, calling // dojo.computedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). // clientWidth/Height are important since the automatically account for scrollbars // fallback to offsetWidth/Height for special cases (see #3378) node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), w = node.clientWidth, h, pe = geom.getPadExtents(node, s), be = geom.getBorderExtents(node, s); 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(has("opera")){ 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}; }; // Box setters depend on box context because interpretation of width/height styles // vary wrt box context. // // The value of boxModel is used to determine box context. // 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. function setBox(/*DomNode*/ node, /*Number?*/ l, /*Number?*/ t, /*Number?*/ w, /*Number?*/ h, /*String?*/ u){ // summary: // sets width/height/left/top in the current (native) box-model // dimensions. Uses the unit passed in u. // node: // DOM Node reference. Id string not supported for performance // reasons. // l: // left offset from parent. // t: // top offset from parent. // w: // width in current box model. // h: // width in current box model. // u: // unit measure to use for other measures. Defaults to "px". u = u || "px"; var s = node.style; if(!isNaN(l)){ s.left = l + u; } if(!isNaN(t)){ s.top = t + u; } if(w >= 0){ s.width = w + u; } if(h >= 0){ s.height = h + u; } } function isButtonTag(/*DomNode*/ node){ // summary: // True if the node is BUTTON or INPUT.type="button". return node.tagName.toLowerCase() == "button" || node.tagName.toLowerCase() == "input" && (node.getAttribute("type") || "").toLowerCase() == "button"; // boolean } function usesBorderBox(/*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. return geom.boxModel == "border-box" || node.tagName.toLowerCase() == "table" || isButtonTag(node); // boolean } geom.setContentSize = function setContentSize(/*DomNode*/ node, /*Object*/ box, /*Object*/ computedStyle){ // summary: // Sets the size of the node's contents, irrespective of margins, // padding, or borders. // node: DOMNode // box: Object // hash with optional "w", and "h" properties for "width", and "height" // respectively. All specified properties should have numeric values in whole pixels. // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo.getComputedStyle to get one. It is a better way, calling // dojo.computedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var w = box.w, h = box.h; if(usesBorderBox(node)){ var pb = geom.getPadBorderExtents(node, computedStyle); if(w >= 0){ w += pb.w; } if(h >= 0){ h += pb.h; } } setBox(node, NaN, NaN, w, h); }; var nilExtents = {l: 0, t: 0, w: 0, h: 0}; geom.setMarginBox = function setMarginBox(/*DomNode*/ node, /*Object*/ box, /*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 setBox that handles box-model vagaries for // you. // node: DOMNode // box: Object // hash with optional "l", "t", "w", and "h" properties for "left", "right", "width", and "height" // respectively. All specified properties should have numeric values in whole pixels. // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo.getComputedStyle to get one. It is a better way, calling // dojo.computedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var s = computedStyle || style.getComputedStyle(node), w = box.w, h = box.h, // 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/dom-geometry.boxModel. pb = usesBorderBox(node) ? nilExtents : geom.getPadBorderExtents(node, s), mb = geom.getMarginExtents(node, s); if(has("webkit")){ // 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(isButtonTag(node)){ var ns = node.style; if(w >= 0 && !ns.width){ ns.width = "4px"; } if(h >= 0 && !ns.height){ ns.height = "4px"; } } } if(w >= 0){ w = Math.max(w - pb.w - mb.w, 0); } if(h >= 0){ h = Math.max(h - pb.h - mb.h, 0); } setBox(node, box.l, box.t, w, h); }; // ============================= // Positioning // ============================= geom.isBodyLtr = function isBodyLtr(/*Document?*/ doc){ // summary: // Returns true if the current language is left-to-right, and false otherwise. // doc: Document? // Optional document to query. If unspecified, use win.doc. // returns: Boolean doc = doc || win.doc; return (win.body(doc).dir || doc.documentElement.dir || "ltr").toLowerCase() == "ltr"; // Boolean }; geom.docScroll = function docScroll(/*Document?*/ doc){ // summary: // Returns an object with {node, x, y} with corresponding offsets. // doc: Document? // Optional document to query. If unspecified, use win.doc. // returns: Object doc = doc || win.doc; var node = win.doc.parentWindow || win.doc.defaultView; // use UI window, not dojo.global window. TODO: use dojo/window::get() except for circular dependency problem return "pageXOffset" in node ? {x: node.pageXOffset, y: node.pageYOffset } : (node = has("quirks") ? win.body(doc) : doc.documentElement) && {x: geom.fixIeBiDiScrollLeft(node.scrollLeft || 0, doc), y: node.scrollTop || 0 }; }; if(has("ie")){ geom.getIeDocumentElementOffset = function getIeDocumentElementOffset(/*Document?*/ doc){ // summary: // returns the offset in x and y from the document body to the // visual edge of the page for IE // doc: Document? // Optional document to query. If unspecified, use win.doc. // description: // The following values in IE contain an offset: // | event.clientX // | event.clientY // | node.getBoundingClientRect().left // | node.getBoundingClientRect().top // But other position related values do not contain this offset, // such as node.offsetLeft, node.offsetTop, node.style.left and // node.style.top. The offset is always (2, 2) in LTR direction. // When the body is in RTL direction, the offset counts the width // of left scroll bar's width. This function computes the actual // offset. //NOTE: assumes we're being called in an IE browser doc = doc || win.doc; var de = doc.documentElement; // only deal with HTML element here, position() handles body/quirks if(has("ie") < 8){ var r = de.getBoundingClientRect(), // works well for IE6+ l = r.left, t = r.top; if(has("ie") < 7){ l += de.clientLeft; // scrollbar size in strict/RTL, or, t += de.clientTop; // HTML border size in strict } return { x: l < 0 ? 0 : l, // FRAME element border size can lead to inaccurate negative values y: t < 0 ? 0 : t }; }else{ return { x: 0, y: 0 }; } }; } geom.fixIeBiDiScrollLeft = function fixIeBiDiScrollLeft(/*Integer*/ scrollLeft, /*Document?*/ doc){ // summary: // In RTL direction, scrollLeft should be a negative value, but IE // returns a positive one. All codes using documentElement.scrollLeft // must call this function to fix this error, otherwise the position // will offset to right when there is a horizontal scrollbar. // scrollLeft: Number // doc: Document? // Optional document to query. If unspecified, use win.doc. // returns: Number // In RTL direction, scrollLeft should be a negative value, but IE // returns a positive one. All codes using documentElement.scrollLeft // must call this function to fix this error, otherwise the position // will offset to right when there is a horizontal scrollbar. doc = doc || win.doc; var ie = has("ie"); if(ie && !geom.isBodyLtr(doc)){ var qk = has("quirks"), de = qk ? win.body(doc) : doc.documentElement, pwin = win.global; // TODO: use winUtils.get(doc) after resolving circular dependency b/w dom-geometry.js and dojo/window.js if(ie == 6 && !qk && pwin.frameElement && de.scrollHeight > de.clientHeight){ scrollLeft += de.clientLeft; // workaround ie6+strict+rtl+iframe+vertical-scrollbar bug where clientWidth is too small by clientLeft pixels } return (ie < 8 || qk) ? (scrollLeft + de.clientWidth - de.scrollWidth) : -scrollLeft; // Integer } return scrollLeft; // Integer }; geom.position = function(/*DomNode*/ node, /*Boolean?*/ includeScroll){ // summary: // Gets the position and size of the passed element relative to // the viewport (if includeScroll==false), or relative to the // document root (if includeScroll==true). // // description: // Returns an object of the form: // `{ x: 100, y: 300, w: 20, h: 15 }`. // If includeScroll==true, the x and y values will include any // document offsets that may affect the position relative to the // viewport. // Uses the border-box model (inclusive of border and padding but // not margin). Does not act as a setter. // node: DOMNode|String // includeScroll: Boolean? // returns: Object node = dom.byId(node); var db = win.body(node.ownerDocument), ret = node.getBoundingClientRect(); ret = {x: ret.left, y: ret.top, w: ret.right - ret.left, h: ret.bottom - ret.top}; if(has("ie")){ // On IE there's a 2px offset that we need to adjust for, see dojo.getIeDocumentElementOffset() var offset = geom.getIeDocumentElementOffset(node.ownerDocument); // fixes the position in IE, quirks mode ret.x -= offset.x + (has("quirks") ? db.clientLeft + db.offsetLeft : 0); ret.y -= offset.y + (has("quirks") ? db.clientTop + db.offsetTop : 0); } // 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 = geom.docScroll(node.ownerDocument); ret.x += scroll.x; ret.y += scroll.y; } return ret; // Object }; // random "private" functions wildly used throughout the toolkit geom.getMarginSize = function getMarginSize(/*DomNode*/ node, /*Object*/ computedStyle){ // summary: // returns an object that encodes the width and height of // the node's margin box // node: DOMNode|String // computedStyle: Object? // This parameter accepts computed styles object. // If this parameter is omitted, the functions will call // dojo.getComputedStyle to get one. It is a better way, calling // dojo.computedStyle once, and then pass the reference to this // computedStyle parameter. Wherever possible, reuse the returned // object of dojo/dom-style.getComputedStyle(). node = dom.byId(node); var me = geom.getMarginExtents(node, computedStyle || style.getComputedStyle(node)); var size = node.getBoundingClientRect(); return { w: (size.right - size.left) + me.w, h: (size.bottom - size.top) + me.h }; }; geom.normalizeEvent = function(event){ // summary: // Normalizes the geometry of a DOM event, normalizing the pageX, pageY, // offsetX, offsetY, layerX, and layerX properties // event: Object if(!("layerX" in event)){ event.layerX = event.offsetX; event.layerY = event.offsetY; } if(!has("dom-addeventlistener")){ // old IE version // 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 = event.target; var 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 = has("quirks") ? doc.body : doc.documentElement; var offset = geom.getIeDocumentElementOffset(doc); event.pageX = event.clientX + geom.fixIeBiDiScrollLeft(docBody.scrollLeft || 0, doc) - offset.x; event.pageY = event.clientY + (docBody.scrollTop || 0) - offset.y; } }; // TODO: evaluate separate getters/setters for position and sizes? return geom; }); }, 'dojo/dom-prop':function(){ define(["exports", "./_base/kernel", "./sniff", "./_base/lang", "./dom", "./dom-style", "./dom-construct", "./_base/connect"], function(exports, dojo, has, lang, dom, style, ctr, conn){ // module: // dojo/dom-prop // summary: // This module defines the core dojo DOM properties API. // Indirectly depends on dojo.empty() and dojo.toDom(). // TODOC: summary not showing up in output, see https://github.com/csnover/js-doc-parse/issues/42 // ============================= // Element properties Functions // ============================= // helper to connect events var _evtHdlrMap = {}, _ctr = 0, _attrId = dojo._scopeName + "attrid"; exports.names = { // properties renamed to avoid clashes with reserved words "class": "className", "for": "htmlFor", // properties written as camelCase tabindex: "tabIndex", readonly: "readOnly", colspan: "colSpan", frameborder: "frameBorder", rowspan: "rowSpan", valuetype: "valueType" }; exports.get = function getProp(/*DOMNode|String*/ node, /*String*/ name){ // summary: // Gets a property on an HTML element. // description: // Handles normalized getting of properties on DOM nodes. // // node: DOMNode|String // id or reference to the element to get the property on // name: String // the name of the property to get. // returns: // the value of the requested property or its default value // // example: // | // get the current value of the "foo" property on a node // | dojo.getProp(dojo.byId("nodeId"), "foo"); // | // or we can just pass the id: // | dojo.getProp("nodeId", "foo"); node = dom.byId(node); var lc = name.toLowerCase(), propName = exports.names[lc] || name; return node[propName]; // Anything }; exports.set = function setProp(/*DOMNode|String*/ node, /*String|Object*/ name, /*String?*/ value){ // summary: // Sets a property on an HTML element. // description: // Handles normalized setting of properties on DOM nodes. // // 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: DOMNode|String // id or reference to the element to set the property on // name: String|Object // the name of the property to set, or a hash object to set // multiple properties at once. // value: String? // The value to set for the property // returns: // the DOM node // // example: // | // use prop() to set the tab index // | dojo.setProp("nodeId", "tabIndex", 3); // | // // example: // Set multiple values at once, including event handlers: // | dojo.setProp("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.setProp("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.setProp("someNode", "style", obj); // | // | // though shorter to use `dojo.style()` in this case: // | dojo.style("someNode", obj); node = dom.byId(node); var l = arguments.length; if(l == 2 && typeof name != "string"){ // inline'd type check // the object form of setter: the 2nd argument is a dictionary for(var x in name){ exports.set(node, x, name[x]); } return node; // DomNode } var lc = name.toLowerCase(), propName = exports.names[lc] || name; if(propName == "style" && typeof value != "string"){ // inline'd type check // special case: setting a style style.set(node, value); return node; // DomNode } if(propName == "innerHTML"){ // special case: assigning HTML // the hash lists elements with read-only innerHTML on IE if(has("ie") && node.tagName.toLowerCase() in {col: 1, colgroup: 1, table: 1, tbody: 1, tfoot: 1, thead: 1, tr: 1, title: 1}){ ctr.empty(node); node.appendChild(ctr.toDom(value, node.ownerDocument)); }else{ node[propName] = value; } return node; // DomNode } if(lang.isFunction(value)){ // special case: assigning an event handler // clobber if we can var attrId = node[_attrId]; if(!attrId){ attrId = _ctr++; node[_attrId] = attrId; } if(!_evtHdlrMap[attrId]){ _evtHdlrMap[attrId] = {}; } var h = _evtHdlrMap[attrId][propName]; if(h){ //h.remove(); conn.disconnect(h); }else{ try{ delete node[propName]; }catch(e){} } // ensure that event objects are normalized, etc. if(value){ //_evtHdlrMap[attrId][propName] = on(node, propName, value); _evtHdlrMap[attrId][propName] = conn.connect(node, propName, value); }else{ node[propName] = null; } return node; // DomNode } node[propName] = value; return node; // DomNode }; }); }, 'dojo/when':function(){ define([ "./Deferred", "./promise/Promise" ], function(Deferred, Promise){ "use strict"; // module: // dojo/when return function when(valueOrPromise, callback, errback, progback){ // summary: // Transparently applies callbacks to values and/or promises. // description: // Accepts promises but also transparently handles non-promises. If no // callbacks are provided returns a promise, regardless of the initial // value. Foreign promises are converted. // // If callbacks are provided and the initial value is not a promise, // the callback is executed immediately with no error handling. Returns // a promise if the initial value is a promise, or the result of the // callback otherwise. // valueOrPromise: // Either a regular value or an object with a `then()` method that // follows the Promises/A specification. // callback: Function? // Callback to be invoked when the promise is resolved, or a non-promise // is received. // errback: Function? // Callback to be invoked when the promise is rejected. // progback: Function? // Callback to be invoked when the promise emits a progress update. // returns: dojo/promise/Promise // Promise, or if a callback is provided, the result of the callback. var receivedPromise = valueOrPromise && typeof valueOrPromise.then === "function"; var nativePromise = receivedPromise && valueOrPromise instanceof Promise; if(!receivedPromise){ if(callback){ return callback(valueOrPromise); }else{ return new Deferred().resolve(valueOrPromise); } }else if(!nativePromise){ var deferred = new Deferred(valueOrPromise.cancel); valueOrPromise.then(deferred.resolve, deferred.reject, deferred.progress); valueOrPromise = deferred.promise; } if(callback || errback || progback){ return valueOrPromise.then(callback, errback, progback); } return valueOrPromise; }; }); }, 'dojo/dom-attr':function(){ define(["exports", "./sniff", "./_base/lang", "./dom", "./dom-style", "./dom-prop"], function(exports, has, lang, dom, style, prop){ // module: // dojo/dom-attr // summary: // This module defines the core dojo DOM attributes API. // TODOC: summary not showing up in output see https://github.com/csnover/js-doc-parse/issues/42 // ============================= // Element attribute Functions // ============================= // This module will be obsolete soon. Use dojo/prop instead. // dojo.attr() should conform to http://www.w3.org/TR/DOM-Level-2-Core/ // attribute-related functions (to be obsolete soon) var forcePropNames = { innerHTML: 1, className: 1, htmlFor: has("ie"), value: 1 }, attrNames = { // original attribute names classname: "class", htmlfor: "for", // for IE tabindex: "tabIndex", readonly: "readOnly" }; function _hasAttr(node, name){ var attr = node.getAttributeNode && node.getAttributeNode(name); return attr && attr.specified; // Boolean } // There is a difference in the presence of certain properties and their default values // between browsers. For example, on IE "disabled" is present on all elements, // but it is value is "false"; "tabIndex" of
returns 0 by default on IE, yet other browsers // can return -1. exports.has = function hasAttr(/*DOMNode|String*/ node, /*String*/ name){ // summary: // Returns true if the requested attribute is specified on the // given element, and false otherwise. // node: DOMNode|String // id or reference to the element to check // name: String // the name of the attribute // returns: Boolean // true if the requested attribute is specified on the // given element, and false otherwise var lc = name.toLowerCase(); return forcePropNames[prop.names[lc] || name] || _hasAttr(dom.byId(node), attrNames[lc] || name); // Boolean }; exports.get = function getAttr(/*DOMNode|String*/ node, /*String*/ name){ // summary: // Gets an attribute on an HTML element. // description: // Handles normalized getting of attributes on DOM Nodes. // node: DOMNode|String // id or reference to the element to get the attribute on // name: String // the name of the attribute to get. // returns: // the value of the requested attribute or null if that attribute does not have a specified or // default value; // // example: // | // get the current value of the "foo" attribute on a node // | dojo.getAttr(dojo.byId("nodeId"), "foo"); // | // or we can just pass the id: // | dojo.getAttr("nodeId", "foo"); node = dom.byId(node); var lc = name.toLowerCase(), propName = prop.names[lc] || name, forceProp = forcePropNames[propName], value = node[propName]; // should we access this attribute via a property or via getAttribute()? if(forceProp && typeof value != "undefined"){ // node's property return value; // Anything } if(propName != "href" && (typeof value == "boolean" || lang.isFunction(value))){ // node's property return value; // Anything } // node's attribute // we need _hasAttr() here to guard against IE returning a default value var attrName = attrNames[lc] || name; return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything }; exports.set = function setAttr(/*DOMNode|String*/ node, /*String|Object*/ name, /*String?*/ value){ // summary: // Sets an attribute on an HTML element. // description: // Handles normalized setting of attributes on DOM Nodes. // // 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: DOMNode|String // id or reference to the element to set the attribute on // name: String|Object // the name of the attribute to set, or a hash of key-value pairs to set. // value: String? // the value to set for the attribute, if the name is a string. // returns: // the DOM node // // example: // | // use attr() to set the tab index // | dojo.setAttr("nodeId", "tabIndex", 3); // // example: // Set multiple values at once, including event handlers: // | dojo.setAttr("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.setAttr("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.setAttr("someNode", "style", obj); // | // | // though shorter to use `dojo.style()` in this case: // | dojo.setStyle("someNode", obj); node = dom.byId(node); if(arguments.length == 2){ // inline'd type check // the object form of setter: the 2nd argument is a dictionary for(var x in name){ exports.set(node, x, name[x]); } return node; // DomNode } var lc = name.toLowerCase(), propName = prop.names[lc] || name, forceProp = forcePropNames[propName]; if(propName == "style" && typeof value != "string"){ // inline'd type check // special case: setting a style style.set(node, value); return node; // DomNode } if(forceProp || typeof value == "boolean" || lang.isFunction(value)){ return prop.set(node, name, value); } // node's attribute node.setAttribute(attrNames[lc] || name, value); return node; // DomNode }; exports.remove = function removeAttr(/*DOMNode|String*/ node, /*String*/ name){ // summary: // Removes an attribute from an HTML element. // node: DOMNode|String // id or reference to the element to remove the attribute from // name: String // the name of the attribute to remove dom.byId(node).removeAttribute(attrNames[name.toLowerCase()] || name); }; exports.getNodeProp = function getNodeProp(/*DomNode|String*/ node, /*String*/ name){ // summary: // Returns an effective value of a property or an attribute. // node: DOMNode|String // id or reference to the element to remove the attribute from // name: String // the name of the attribute // returns: // the value of the attribute node = dom.byId(node); var lc = name.toLowerCase(), propName = prop.names[lc] || name; if((propName in node) && propName != "href"){ // node's property return node[propName]; // Anything } // node's attribute var attrName = attrNames[lc] || name; return _hasAttr(node, attrName) ? node.getAttribute(attrName) : null; // Anything }; }); }, 'dojo/dom-construct':function(){ define(["exports", "./_base/kernel", "./sniff", "./_base/window", "./dom", "./dom-attr", "./on"], function(exports, dojo, has, win, dom, attr, on){ // module: // dojo/dom-construct // summary: // This module defines the core dojo DOM construction API. // TODOC: summary not showing up in output, see https://github.com/csnover/js-doc-parse/issues/42 // support stuff for 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 = "__" + dojo._scopeName + "ToDomId"; // generate start/end tag strings to use // for the injection for each special tag wrap case. for(var param in tagWrap){ if(tagWrap.hasOwnProperty(param)){ var tw = tagWrap[param]; tw.pre = param == "option" ? '