/* Copyright (c) 2004-2006, The Dojo Foundation All Rights Reserved. Licensed under the Academic Free License version 2.1 or above OR the modified BSD license. For more information on Dojo licensing, see: http://dojotoolkit.org/community/licensing.shtml */ if(typeof dojo == "undefined"){ // TODOC: HOW TO DOC THE BELOW? // @global: djConfig // summary: // Application code can set the global 'djConfig' prior to loading // the library to override certain global settings for how dojo works. // description: The variables that can be set are as follows: // - isDebug: false // - allowQueryConfig: false // - baseScriptUri: "" // - baseRelativePath: "" // - libraryScriptUri: "" // - iePreventClobber: false // - ieClobberMinimal: true // - locale: undefined // - extraLocale: undefined // - preventBackButtonFix: true // - searchIds: [] // - parseWidgets: true // TODOC: HOW TO DOC THESE VARIABLES? // TODOC: IS THIS A COMPLETE LIST? // note: // 'djConfig' does not exist under 'dojo.*' so that it can be set before the // 'dojo' variable exists. // note: // Setting any of these variables *after* the library has loaded does nothing at all. // TODOC: is this still true? Release notes for 0.3 indicated they could be set after load. // //TODOC: HOW TO DOC THIS? // @global: dj_global // summary: // an alias for the top-level global object in the host environment // (e.g., the window object in a browser). // description: // Refer to 'dj_global' rather than referring to window to ensure your // code runs correctly in contexts other than web browsers (eg: Rhino on a server). var dj_global = this; //TODOC: HOW TO DOC THIS? // @global: dj_currentContext // summary: // Private global context object. Where 'dj_global' always refers to the boot-time // global context, 'dj_currentContext' can be modified for temporary context shifting. // dojo.global() returns dj_currentContext. // description: // Refer to dojo.global() rather than referring to dj_global to ensure your // code runs correctly in managed contexts. var dj_currentContext = this; // **************************************************************** // global public utils // TODOC: DO WE WANT TO NOTE THAT THESE ARE GLOBAL PUBLIC UTILS? // **************************************************************** function dj_undef(/*String*/ name, /*Object?*/ object){ //summary: Returns true if 'name' is defined on 'object' (or globally if 'object' is null). //description: Note that 'defined' and 'exists' are not the same concept. return (typeof (object || dj_currentContext)[name] == "undefined"); // Boolean } // make sure djConfig is defined if(dj_undef("djConfig", this)){ var djConfig = {}; } //TODOC: HOW TO DOC THIS? // dojo is the root variable of (almost all) our public symbols -- make sure it is defined. if(dj_undef("dojo", this)){ var dojo = {}; } dojo.global = function(){ // summary: // return the current global context object // (e.g., the window object in a browser). // description: // Refer to 'dojo.global()' rather than referring to window to ensure your // code runs correctly in contexts other than web browsers (eg: Rhino on a server). return dj_currentContext; } // Override locale setting, if specified dojo.locale = djConfig.locale; //TODOC: HOW TO DOC THIS? dojo.version = { // summary: version number of this instance of dojo. major: 0, minor: 0, patch: 0, flag: "dev", revision: Number("$Rev$".match(/[0-9]+/)[0]), toString: function(){ with(dojo.version){ return major + "." + minor + "." + patch + flag + " (" + revision + ")"; // String } } } dojo.evalProp = function(/*String*/ name, /*Object*/ object, /*Boolean?*/ create){ // summary: Returns 'object[name]'. If not defined and 'create' is true, will return a new Object. // description: // Returns null if 'object[name]' is not defined and 'create' is not true. // Note: 'defined' and 'exists' are not the same concept. if((!object)||(!name)) return undefined; // undefined if(!dj_undef(name, object)) return object[name]; // mixed return (create ? (object[name]={}) : undefined); // mixed } dojo.parseObjPath = function(/*String*/ path, /*Object?*/ context, /*Boolean?*/ create){ // summary: Parse string path to an object, and return corresponding object reference and property name. // description: // Returns an object with two properties, 'obj' and 'prop'. // 'obj[prop]' is the reference indicated by 'path'. // path: Path to an object, in the form "A.B.C". // context: Object to use as root of path. Defaults to 'dojo.global()'. // create: If true, Objects will be created at any point along the 'path' that is undefined. var object = (context || dojo.global()); var names = path.split('.'); var prop = names.pop(); for (var i=0,l=names.length;i 1) { dh.modulesLoadedListeners.push(function() { obj[functionName](); }); } //Added for xdomain loading. dojo.addOnLoad is used to //indicate callbacks after doing some dojo.require() statements. //In the xdomain case, if all the requires are loaded (after initial //page load), then immediately call any listeners. if(dh.post_load_ && dh.inFlightCount == 0 && !dh.loadNotifying){ dh.callLoaded(); } } dojo.addOnUnload = function(/*Object?*/obj, /*String|Function?*/functionName){ // summary: registers a function to be triggered when the page unloads // // usage: // dojo.addOnLoad(functionPointer) // dojo.addOnLoad(object, "functionName") var dh = dojo.hostenv; if(arguments.length == 1){ dh.unloadListeners.push(obj); } else if(arguments.length > 1) { dh.unloadListeners.push(function() { obj[functionName](); }); } } dojo.hostenv.modulesLoaded = function(){ if(this.post_load_){ return; } if(this.loadUriStack.length==0 && this.getTextStack.length==0){ if(this.inFlightCount > 0){ dojo.debug("files still in flight!"); return; } dojo.hostenv.callLoaded(); } } dojo.hostenv.callLoaded = function(){ //The "object" check is for IE, and the other opera check fixes an issue //in Opera where it could not find the body element in some widget test cases. //For 0.9, maybe route all browsers through the setTimeout (need protection //still for non-browser environments though). This might also help the issue with //FF 2.0 and freezing issues where we try to do sync xhr while background css images //are being loaded (trac #2572)? Consider for 0.9. if(typeof setTimeout == "object" || (djConfig["useXDomain"] && dojo.render.html.opera)){ setTimeout("dojo.hostenv.loaded();", 0); }else{ dojo.hostenv.loaded(); } } dojo.hostenv.getModuleSymbols = function(/*String*/modulename){ // summary: // Converts a module name in dotted JS notation to an array representing the path in the source tree var syms = modulename.split("."); for(var i = syms.length; i>0; i--){ var parentModule = syms.slice(0, i).join("."); if((i==1) && !this.moduleHasPrefix(parentModule)){ // Support default module directory (sibling of dojo) for top-level modules syms[0] = "../" + syms[0]; }else{ var parentModulePath = this.getModulePrefix(parentModule); if(parentModulePath != parentModule){ syms.splice(0, i, parentModulePath); break; } } } return syms; // Array } dojo.hostenv._global_omit_module_check = false; dojo.hostenv.loadModule = function(/*String*/moduleName, /*Boolean?*/exactOnly, /*Boolean?*/omitModuleCheck){ // summary: // loads a Javascript module from the appropriate URI // // description: // loadModule("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, // followed by "A.js". // // It throws 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. // // This does nothing about importing symbols into the current package. // It is presumed that the caller will take care of that. For example, to import // all symbols: // // with (dojo.hostenv.loadModule("A.B")) { // ... // } // // And to import just the leaf symbol: // // var B = dojo.hostenv.loadModule("A.B"); // ... // // dj_load is an alias for dojo.hostenv.loadModule if(!moduleName){ return; } omitModuleCheck = this._global_omit_module_check || omitModuleCheck; var module = this.findModule(moduleName, false); if(module){ return module; } // protect against infinite recursion from mutual dependencies if(dj_undef(moduleName, this.loading_modules_)){ this.addedToLoadingCount.push(moduleName); } this.loading_modules_[moduleName] = 1; // convert periods to slashes var relpath = moduleName.replace(/\./g, '/') + '.js'; var nsyms = moduleName.split("."); // this line allowed loading of a module manifest as if it were a namespace // it's an interesting idea, but shouldn't be combined with 'namespaces' proper // and leads to unwanted dependencies // the effect can be achieved in other (albeit less-flexible) ways now, so I am // removing this pending further design work // perhaps we can explicitly define this idea of a 'module manifest', and subclass // 'namespace manifest' from that //dojo.getNamespace(nsyms[0]); var syms = this.getModuleSymbols(moduleName); var startedRelative = ((syms[0].charAt(0) != '/') && !syms[0].match(/^\w+:/)); var last = syms[syms.length - 1]; var ok; // figure out if we're looking for a full package, if so, we want to do // things slightly diffrently if(last=="*"){ moduleName = nsyms.slice(0, -1).join('.'); while(syms.length){ syms.pop(); syms.push(this.pkgFileName); relpath = syms.join("/") + '.js'; if(startedRelative && relpath.charAt(0)=="/"){ relpath = relpath.slice(1); } ok = this.loadPath(relpath, !omitModuleCheck ? moduleName : null); if(ok){ break; } syms.pop(); } }else{ relpath = syms.join("/") + '.js'; moduleName = nsyms.join('.'); var modArg = !omitModuleCheck ? moduleName : null; ok = this.loadPath(relpath, modArg); if(!ok && !exactOnly){ syms.pop(); while(syms.length){ relpath = syms.join('/') + '.js'; ok = this.loadPath(relpath, modArg); if(ok){ break; } syms.pop(); relpath = syms.join('/') + '/'+this.pkgFileName+'.js'; if(startedRelative && relpath.charAt(0)=="/"){ relpath = relpath.slice(1); } ok = this.loadPath(relpath, modArg); if(ok){ break; } } } if(!ok && !omitModuleCheck){ dojo.raise("Could not load '" + moduleName + "'; last tried '" + relpath + "'"); } } // check that the symbol was defined //Don't bother if we're doing xdomain (asynchronous) loading. if(!omitModuleCheck && !this["isXDomain"]){ // pass in false so we can give better error module = this.findModule(moduleName, false); if(!module){ dojo.raise("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'"); } } return module; } dojo.hostenv.startPackage = function(/*String*/packageName){ // summary: // Creates a JavaScript package // // description: // startPackage("A.B") follows the path, and at each level creates a new empty // object or uses what already exists. It returns the result. // // packageName: the package to be created as a String in dot notation //Make sure we have a string. var fullPkgName = String(packageName); var strippedPkgName = fullPkgName; var syms = packageName.split(/\./); if(syms[syms.length-1]=="*"){ syms.pop(); strippedPkgName = syms.join("."); } var evaledPkg = dojo.evalObjPath(strippedPkgName, true); this.loaded_modules_[fullPkgName] = evaledPkg; this.loaded_modules_[strippedPkgName] = evaledPkg; return evaledPkg; // Object } dojo.hostenv.findModule = function(/*String*/moduleName, /*Boolean?*/mustExist){ // summary: // Returns the Object representing the module, if it exists, otherwise null. // // moduleName A fully qualified module including package name, like 'A.B'. // mustExist Optional, default false. throw instead of returning null // if the module does not currently exist. var lmn = String(moduleName); if(this.loaded_modules_[lmn]){ return this.loaded_modules_[lmn]; // Object } if(mustExist){ dojo.raise("no loaded module named '" + moduleName + "'"); } return null; // null } //Start of old bootstrap2: dojo.kwCompoundRequire = function(/*Object containing Arrays*/modMap){ // description: // This method taks a "map" of arrays which one can use to optionally load dojo // modules. The map is indexed by the possible dojo.hostenv.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 // the hostenv.name_ item. The items in the "common" array will _always_ be // loaded, regardless of which list is chosen. Here's how it's normally // called: // // dojo.kwCompoundRequire({ // browser: [ // ["foo.bar.baz", true, true], // an example that passes multiple args to loadModule() // "foo.sample.*", // "foo.test, // ], // default: [ "foo.sample.*" ], // common: [ "really.important.module.*" ] // }); var common = modMap["common"]||[]; var result = modMap[dojo.hostenv.name_] ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]); for(var x=0; x, // relative to Dojo root. For example, module acme is mapped to ../acme. // If you want to use a different module name, use dojo.registerModulePath. return dojo.hostenv.setModulePrefix(module, prefix); } if(djConfig["modulePaths"]){ for(var param in djConfig["modulePaths"]){ dojo.registerModulePath(param, djConfig["modulePaths"][param]); } } dojo.setModulePrefix = function(/*String*/module, /*String*/prefix){ // summary: maps a module name to a path dojo.deprecated('dojo.setModulePrefix("' + module + '", "' + prefix + '")', "replaced by dojo.registerModulePath", "0.5"); return dojo.registerModulePath(module, prefix); } dojo.exists = function(/*Object*/obj, /*String*/name){ // summary: determine if an object supports a given method // description: useful for longer api chains where you have to test each object in the chain var p = name.split("."); for(var i = 0; i < p.length; i++){ if(!obj[p[i]]){ return false; } // Boolean obj = obj[p[i]]; } return true; // Boolean } // Localization routines dojo.hostenv.normalizeLocale = function(/*String?*/locale){ // summary: // Returns canonical form of locale, as used by Dojo. All variants are case-insensitive and are separated by '-' // as specified in RFC 3066. If no locale is specified, the user agent's default is returned. var result = locale ? locale.toLowerCase() : dojo.locale; if(result == "root"){ result = "ROOT"; } return result;// String }; dojo.hostenv.searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){ // summary: // A helper method to assist in searching for locale-based resources. Will iterate through // the variants of a particular locale, either up or down, executing a callback function. // For example, "en-us" and true will try "en-us" followed by "en" and finally "ROOT". locale = dojo.hostenv.normalizeLocale(locale); var elements = locale.split('-'); var searchlist = []; for(var i = elements.length; i > 0; i--){ searchlist.push(elements.slice(0, i).join('-')); } searchlist.push(false); if(down){searchlist.reverse();} for(var j = searchlist.length - 1; j >= 0; j--){ var loc = searchlist[j] || "ROOT"; var stop = searchFunc(loc); if(stop){ break; } } } //These two functions are placed outside of preloadLocalizations //So that the xd loading can use/override them. dojo.hostenv.localesGenerated =["ROOT","es-es","es","it-it","pt-br","de","fr-fr","zh-cn","pt","en-us","zh","fr","zh-tw","it","en-gb","xx","de-de","ko-kr","ja-jp","ko","en","ja"]; // value will be inserted here at build time, if necessary dojo.hostenv.registerNlsPrefix = function(){ // summary: // Register module "nls" to point where Dojo can find pre-built localization files dojo.registerModulePath("nls","nls"); } dojo.hostenv.preloadLocalizations = function(){ // summary: // Load built, flattened resource bundles, if available for all locales used in the page. // Execute only once. Note that this is a no-op unless there is a build. if(dojo.hostenv.localesGenerated){ dojo.hostenv.registerNlsPrefix(); function preload(locale){ locale = dojo.hostenv.normalizeLocale(locale); dojo.hostenv.searchLocalePath(locale, true, function(loc){ for(var i=0; i bestLocale.length){ bestLocale = flatLocales[i]; } } } if(!bestLocale){ bestLocale = "ROOT"; } } //See if the desired locale is already loaded. var tempLocale = availableFlatLocales ? bestLocale : targetLocale; var bundle = dojo.hostenv.findModule(bundlePackage); var localizedBundle = null; if(bundle){ if(djConfig.localizationComplete && bundle._built){return;} var jsLoc = tempLocale.replace('-', '_'); var translationPackage = bundlePackage+"."+jsLoc; localizedBundle = dojo.hostenv.findModule(translationPackage); } if(!localizedBundle){ bundle = dojo.hostenv.startPackage(bundlePackage); var syms = dojo.hostenv.getModuleSymbols(moduleName); var modpath = syms.concat("nls").join("/"); var parent; dojo.hostenv.searchLocalePath(tempLocale, availableFlatLocales, function(loc){ var jsLoc = loc.replace('-', '_'); var translationPackage = bundlePackage + "." + jsLoc; var loaded = false; if(!dojo.hostenv.findModule(translationPackage)){ // Mark loaded whether it's found or not, so that further load attempts will not be made dojo.hostenv.startPackage(translationPackage); var module = [modpath]; if(loc != "ROOT"){module.push(loc);} module.push(bundleName); var filespec = module.join("/") + '.js'; loaded = dojo.hostenv.loadPath(filespec, null, function(hash){ // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath var clazz = function(){}; clazz.prototype = parent; bundle[jsLoc] = new clazz(); for(var j in hash){ bundle[jsLoc][j] = hash[j]; } }); }else{ loaded = true; } if(loaded && bundle[jsLoc]){ parent = bundle[jsLoc]; }else{ bundle[jsLoc] = parent; } if(availableFlatLocales){ //Stop the locale path searching if we know the availableFlatLocales, since //the first call to this function will load the only bundle that is needed. return true; } }); } //Save the best locale bundle as the target locale bundle when we know the //the available bundles. if(availableFlatLocales && targetLocale != bestLocale){ bundle[targetLocale.replace('-', '_')] = bundle[bestLocale.replace('-', '_')]; } }; (function(){ // If other locales are used, dojo.requireLocalization should load them as well, by default. // Override dojo.requireLocalization to do load the default bundle, then iterate through the // extraLocale list and load those translations as well, unless a particular locale was requested. var extra = djConfig.extraLocale; if(extra){ if(!extra instanceof Array){ extra = [extra]; } var req = dojo.requireLocalization; dojo.requireLocalization = function(m, b, locale, availableFlatLocales){ req(m,b,locale, availableFlatLocales); if(locale){return;} for(var i=0; i 1){ var paramStr = params[1]; var pairs = paramStr.split("&"); for(var x in pairs){ var sp = pairs[x].split("="); // FIXME: is this eval dangerous? if((sp[0].length > 9)&&(sp[0].substr(0, 9) == "djConfig.")){ var opt = sp[0].substr(9); try{ djConfig[opt]=eval(sp[1]); }catch(e){ djConfig[opt]=sp[1]; } } } } } if( ((djConfig["baseScriptUri"] == "")||(djConfig["baseRelativePath"] == "")) && (document && document.getElementsByTagName) ){ var scripts = document.getElementsByTagName("script"); var rePkg = /(__package__|dojo|bootstrap1)\.js([\?\.]|$)/i; for(var i = 0; i < scripts.length; i++) { var src = scripts[i].getAttribute("src"); if(!src) { continue; } var m = src.match(rePkg); if(m) { var root = src.substring(0, m.index); if(src.indexOf("bootstrap1") > -1) { root += "../"; } if(!this["djConfig"]) { djConfig = {}; } if(djConfig["baseScriptUri"] == "") { djConfig["baseScriptUri"] = root; } if(djConfig["baseRelativePath"] == "") { djConfig["baseRelativePath"] = root; } break; } } } // fill in the rendering support information in dojo.render.* var dr = dojo.render; var drh = dojo.render.html; var drs = dojo.render.svg; var dua = (drh.UA = navigator.userAgent); var dav = (drh.AV = navigator.appVersion); var t = true; var f = false; drh.capable = t; drh.support.builtin = t; dr.ver = parseFloat(drh.AV); dr.os.mac = dav.indexOf("Macintosh") >= 0; dr.os.win = dav.indexOf("Windows") >= 0; // could also be Solaris or something, but it's the same browser dr.os.linux = dav.indexOf("X11") >= 0; drh.opera = dua.indexOf("Opera") >= 0; drh.khtml = (dav.indexOf("Konqueror") >= 0)||(dav.indexOf("Safari") >= 0); drh.safari = dav.indexOf("Safari") >= 0; var geckoPos = dua.indexOf("Gecko"); drh.mozilla = drh.moz = (geckoPos >= 0)&&(!drh.khtml); if (drh.mozilla) { // gecko version is YYYYMMDD drh.geckoVersion = dua.substring(geckoPos + 6, geckoPos + 14); } drh.ie = (document.all)&&(!drh.opera); drh.ie50 = drh.ie && dav.indexOf("MSIE 5.0")>=0; drh.ie55 = drh.ie && dav.indexOf("MSIE 5.5")>=0; drh.ie60 = drh.ie && dav.indexOf("MSIE 6.0")>=0; drh.ie70 = drh.ie && dav.indexOf("MSIE 7.0")>=0; var cm = document["compatMode"]; drh.quirks = (cm == "BackCompat")||(cm == "QuirksMode")||drh.ie55||drh.ie50; // TODO: is the HTML LANG attribute relevant? dojo.locale = dojo.locale || (drh.ie ? navigator.userLanguage : navigator.language).toLowerCase(); dr.vml.capable=drh.ie; drs.capable = f; drs.support.plugin = f; drs.support.builtin = f; var tdoc = window["document"]; var tdi = tdoc["implementation"]; if((tdi)&&(tdi["hasFeature"])&&(tdi.hasFeature("org.w3c.dom.svg", "1.0"))){ drs.capable = t; drs.support.builtin = t; drs.support.plugin = f; } // webkits after 420 support SVG natively. The test string is "AppleWebKit/420+" if(drh.safari){ var tmp = dua.split("AppleWebKit/")[1]; var ver = parseFloat(tmp.split(" ")[0]); if(ver >= 420){ drs.capable = t; drs.support.builtin = t; drs.support.plugin = f; } }else{ } })(); dojo.hostenv.startPackage("dojo.hostenv"); dojo.render.name = dojo.hostenv.name_ = 'browser'; dojo.hostenv.searchIds = []; // These are in order of decreasing likelihood; this will change in time. dojo.hostenv._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; dojo.hostenv.getXmlhttpObject = function(){ // summary: does the work of portably generating a new XMLHTTPRequest object. var http = null; var last_e = null; try{ http = new XMLHttpRequest(); }catch(e){} if(!http){ for(var i=0; i<3; ++i){ var progid = dojo.hostenv._XMLHTTP_PROGIDS[i]; try{ http = new ActiveXObject(progid); }catch(e){ last_e = e; } if(http){ dojo.hostenv._XMLHTTP_PROGIDS = [progid]; // so faster next time break; } } /*if(http && !http.toString) { http.toString = function() { "[object XMLHttpRequest]"; } }*/ } if(!http){ return dojo.raise("XMLHTTP not available", last_e); } return http; // XMLHTTPRequest instance } dojo.hostenv._blockAsync = false; dojo.hostenv.getText = function(uri, async_cb, fail_ok){ // summary: Read the contents of the specified uri and return those contents. // uri: // A relative or absolute uri. If absolute, it still must be in // the same "domain" as we are. // async_cb: // If not specified, load synchronously. If specified, load // asynchronously, and use async_cb as the progress handler which // takes the xmlhttp object as its argument. If async_cb, this // function returns null. // fail_ok: // Default false. If fail_ok and !async_cb and loading fails, // return null instead of throwing. // need to block async callbacks from snatching this thread as the result // of an async callback might call another sync XHR, this hangs khtml forever // hostenv._blockAsync must also be checked in BrowserIO's watchInFlight() // NOTE: must be declared before scope switches ie. this.getXmlhttpObject() if(!async_cb){ this._blockAsync = true; } var http = this.getXmlhttpObject(); function isDocumentOk(http){ var stat = http["status"]; // allow a 304 use cache, needed in konq (is this compliant with the http spec?) return Boolean((!stat)||((200 <= stat)&&(300 > stat))||(stat==304)); } if(async_cb){ var _this = this, timer = null, gbl = dojo.global(); var xhr = dojo.evalObjPath("dojo.io.XMLHTTPTransport"); http.onreadystatechange = function(){ if(timer){ gbl.clearTimeout(timer); timer = null; } if(_this._blockAsync || (xhr && xhr._blockAsync)){ timer = gbl.setTimeout(function () { http.onreadystatechange.apply(this); }, 10); }else{ if(4==http.readyState){ if(isDocumentOk(http)){ // dojo.debug("LOADED URI: "+uri); async_cb(http.responseText); } } } } } http.open('GET', uri, async_cb ? true : false); try{ http.send(null); if(async_cb){ return null; } if(!isDocumentOk(http)){ var err = Error("Unable to load "+uri+" status:"+ http.status); err.status = http.status; err.responseText = http.responseText; throw err; } }catch(e){ this._blockAsync = false; if((fail_ok)&&(!async_cb)){ return null; }else{ throw e; } } this._blockAsync = false; return http.responseText; // String } dojo.hostenv.defaultDebugContainerId = 'dojoDebug'; dojo.hostenv._println_buffer = []; dojo.hostenv._println_safe = false; dojo.hostenv.println = function(/*String*/line){ // summary: // prints the provided line to whatever logging container is // available. If the page isn't loaded yet, the line may be added // to a buffer for printing later. if(!dojo.hostenv._println_safe){ dojo.hostenv._println_buffer.push(line); }else{ try { var console = document.getElementById(djConfig.debugContainerId ? djConfig.debugContainerId : dojo.hostenv.defaultDebugContainerId); if(!console) { console = dojo.body(); } var div = document.createElement("div"); div.appendChild(document.createTextNode(line)); console.appendChild(div); } catch (e) { try{ // safari needs the output wrapped in an element for some reason document.write("
" + line + "
"); }catch(e2){ window.status = line; } } } } dojo.addOnLoad(function(){ dojo.hostenv._println_safe = true; while(dojo.hostenv._println_buffer.length > 0){ dojo.hostenv.println(dojo.hostenv._println_buffer.shift()); } }); function dj_addNodeEvtHdlr(/*DomNode*/node, /*String*/evtName, /*Function*/fp){ // summary: // non-destructively adds the specified function to the node's // evtName handler. // node: the DomNode to add the handler to // evtName: should be in the form "click" for "onclick" handlers var oldHandler = node["on"+evtName] || function(){}; node["on"+evtName] = function(){ fp.apply(node, arguments); oldHandler.apply(node, arguments); } return true; } dojo.hostenv._djInitFired = false; // BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/) function dj_load_init(e){ dojo.hostenv._djInitFired = true; // allow multiple calls, only first one will take effect // A bug in khtml calls events callbacks for document for event which isnt supported // for example a created contextmenu event calls DOMContentLoaded, workaround var type = (e && e.type) ? e.type.toLowerCase() : "load"; if(arguments.callee.initialized || (type!="domcontentloaded" && type!="load")){ return; } arguments.callee.initialized = true; if(typeof(_timer) != 'undefined'){ clearInterval(_timer); delete _timer; } var initFunc = function(){ //perform initialization if(dojo.render.html.ie){ dojo.hostenv.makeWidgets(); } }; if(dojo.hostenv.inFlightCount == 0){ initFunc(); dojo.hostenv.modulesLoaded(); }else{ //This else case should be xdomain loading. //Make sure this is the first thing in the load listener array. //Part of the dojo.addOnLoad guarantee is that when the listeners are notified, //It means the DOM (or page) has loaded and that widgets have been parsed. dojo.hostenv.modulesLoadedListeners.unshift(initFunc); } } // START DOMContentLoaded // Mozilla and Opera 9 expose the event we could use if(document.addEventListener){ // NOTE: // due to a threading issue in Firefox 2.0, we can't enable // DOMContentLoaded on that platform. For more information, see: // http://trac.dojotoolkit.org/ticket/1704 if(dojo.render.html.opera || (dojo.render.html.moz && (djConfig["enableMozDomContentLoaded"] === true))){ document.addEventListener("DOMContentLoaded", dj_load_init, null); } // mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already. // also used for Mozilla because of trac #1640 window.addEventListener("load", dj_load_init, null); } // for Internet Explorer. readyState will not be achieved on init call, but dojo doesn't need it // however, we'll include it because we don't know if there are other functions added that might. // Note that this has changed because the build process strips all comments--including conditional // ones. if(dojo.render.html.ie && dojo.render.os.win){ document.attachEvent("onreadystatechange", function(e){ if(document.readyState == "complete"){ dj_load_init(); } }); } if (/(WebKit|khtml)/i.test(navigator.userAgent)) { // sniff var _timer = setInterval(function() { if (/loaded|complete/.test(document.readyState)) { dj_load_init(); // call the onload handler } }, 10); } // END DOMContentLoaded // IE WebControl hosted in an application can fire "beforeunload" and "unload" // events when control visibility changes, causing Dojo to unload too soon. The // following code fixes the problem // Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;199155 if(dojo.render.html.ie){ dj_addNodeEvtHdlr(window, "beforeunload", function(){ dojo.hostenv._unloading = true; window.setTimeout(function() { dojo.hostenv._unloading = false; }, 0); }); } dj_addNodeEvtHdlr(window, "unload", function(){ dojo.hostenv.unloaded(); if((!dojo.render.html.ie)||(dojo.render.html.ie && dojo.hostenv._unloading)){ dojo.hostenv.unloaded(); } }); dojo.hostenv.makeWidgets = function(){ // you can put searchIds in djConfig and dojo.hostenv at the moment // we should probably eventually move to one or the other var sids = []; if(djConfig.searchIds && djConfig.searchIds.length > 0) { sids = sids.concat(djConfig.searchIds); } if(dojo.hostenv.searchIds && dojo.hostenv.searchIds.length > 0) { sids = sids.concat(dojo.hostenv.searchIds); } if((djConfig.parseWidgets)||(sids.length > 0)){ if(dojo.evalObjPath("dojo.widget.Parse")){ // we must do this on a delay to avoid: // http://www.shaftek.org/blog/archives/000212.html // (IE bug) var parser = new dojo.xml.Parse(); if(sids.length > 0){ for(var x=0; x -1; // boolean } /** * Partial implmentation of is* functions from * http://www.crockford.com/javascript/recommend.html * NOTE: some of these may not be the best thing to use in all situations * as they aren't part of core JS and therefore can't work in every case. * See WARNING messages inline for tips. * * The following is* functions are fairly "safe" */ dojo.lang.isObject = function(/*anything*/ it){ // summary: Return true if it is an Object, Array or Function. if(typeof it == "undefined"){ return false; } return (typeof it == "object" || it === null || dojo.lang.isArray(it) || dojo.lang.isFunction(it)); // Boolean } dojo.lang.isArray = function(/*anything*/ it){ // summary: Return true if it is an Array. return (it && it instanceof Array || typeof it == "array"); // Boolean } dojo.lang.isArrayLike = function(/*anything*/ it){ // summary: // Return true if it can be used as an array (i.e. is an object with // an integer length property). if((!it)||(dojo.lang.isUndefined(it))){ return false; } if(dojo.lang.isString(it)){ return false; } if(dojo.lang.isFunction(it)){ return false; } // keeps out built-in constructors (Number, String, ...) which have length properties if(dojo.lang.isArray(it)){ return true; } // form node itself is ArrayLike, but not always iterable. Use form.elements instead. if((it.tagName)&&(it.tagName.toLowerCase()=='form')){ return false; } if(dojo.lang.isNumber(it.length) && isFinite(it.length)){ return true; } return false; // Boolean } dojo.lang.isFunction = function(/*anything*/ it){ // summary: Return true if it is a Function. return (it instanceof Function || typeof it == "function"); // Boolean }; (function(){ // webkit treats NodeList as a function, which is bad if((dojo.render.html.capable)&&(dojo.render.html["safari"])){ dojo.lang.isFunction = function(/*anything*/ it){ if((typeof(it) == "function") && (it == "[object NodeList]")) { return false; } return (it instanceof Function || typeof it == "function"); // Boolean } } })(); dojo.lang.isString = function(/*anything*/ it){ // summary: Return true if it is a String. return (typeof it == "string" || it instanceof String); } dojo.lang.isAlien = function(/*anything*/ it){ // summary: Return true if it is not a built-in function. False if not. if(!it){ return false; } return !dojo.lang.isFunction(it) && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean } dojo.lang.isBoolean = function(/*anything*/ it){ // summary: Return true if it is a Boolean. return (it instanceof Boolean || typeof it == "boolean"); // Boolean } /** * The following is***() functions are somewhat "unsafe". Fortunately, * there are workarounds the the language provides and are mentioned * in the WARNING messages. * */ dojo.lang.isNumber = function(/*anything*/ it){ // summary: Return true if it is a number. // description: // WARNING - In most cases, isNaN(it) is sufficient to determine whether or not // something is a number or can be used as such. For example, a number or string // can be used interchangably when accessing array items (array["1"] is the same as // array[1]) and isNaN will return false for both values ("1" and 1). However, // isNumber("1") will return false, which is generally not too useful. // Also, isNumber(NaN) returns true, again, this isn't generally useful, but there // are corner cases (like when you want to make sure that two things are really // the same type of thing). That is really where isNumber "shines". // // Recommendation - Use isNaN(it) when possible return (it instanceof Number || typeof it == "number"); // Boolean } /* * FIXME: Should isUndefined go away since it is error prone? */ dojo.lang.isUndefined = function(/*anything*/ it){ // summary: Return true if it is not defined. // description: // WARNING - In some cases, isUndefined will not behave as you // might expect. If you do isUndefined(foo) and there is no earlier // reference to foo, an error will be thrown before isUndefined is // called. It behaves correctly if you scope yor object first, i.e. // isUndefined(foo.bar) where foo is an object and bar isn't a // property of the object. // // Recommendation - Use typeof foo == "undefined" when possible return ((typeof(it) == "undefined")&&(it == undefined)); // Boolean } // end Crockford functions dojo.provide("dojo.lang.array"); // FIXME: Is this worthless since you can do: if(name in obj) // is this the right place for this? dojo.lang.mixin(dojo.lang, { has: function(/*Object*/obj, /*String*/name){ // summary: is there a property with the passed name in obj? try{ return typeof obj[name] != "undefined"; // Boolean }catch(e){ return false; } // Boolean }, isEmpty: function(/*Object*/obj){ // summary: // can be used to determine if the passed object is "empty". In // the case of array-like objects, the length, property is // examined, but for other types of objects iteration is used to // examine the iterable "surface area" to determine if any // non-prototypal properties have been assigned. This iteration is // prototype-extension safe. if(dojo.lang.isObject(obj)){ var tmp = {}; var count = 0; for(var x in obj){ if(obj[x] && (!tmp[x])){ count++; break; } } return count == 0; // boolean }else if(dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)){ return obj.length == 0; // boolean } }, map: function(/*Array*/arr, /*Object|Function*/obj, /*Function?*/unary_func){ // summary: // returns a new array constituded from the return values of // passing each element of arr into unary_func. The obj parameter // may be passed to enable the passed function to be called in // that scope. In environments that support JavaScript 1.6, this // function is a passthrough to the built-in map() function // provided by Array instances. For details on this, see: // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:map // examples: // dojo.lang.map([1, 2, 3, 4], function(item){ return item+1 }); // // returns [2, 3, 4, 5] var isString = dojo.lang.isString(arr); if(isString){ // arr: String arr = arr.split(""); } if(dojo.lang.isFunction(obj)&&(!unary_func)){ unary_func = obj; obj = dj_global; }else if(dojo.lang.isFunction(obj) && unary_func){ // ff 1.5 compat var tmpObj = obj; obj = unary_func; unary_func = tmpObj; } if(Array.map){ var outArr = Array.map(arr, unary_func, obj); }else{ var outArr = []; for(var i=0;i1; }); // // returns false // dojo.lang.every([1, 2, 3, 4], function(item){ return item>0; }); // // returns true return this._everyOrSome(true, arr, callback, thisObject); // Boolean }, some: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){ // summary: // determines whether or not any item in the array satisfies the // condition implemented by callback. thisObject may be used to // scope the call to callback. The function signature is derived // from the JavaScript 1.6 Array.some() function. More // information on this can be found here: // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:some // examples: // dojo.lang.some([1, 2, 3, 4], function(item){ return item>1; }); // // returns true // dojo.lang.some([1, 2, 3, 4], function(item){ return item<1; }); // // returns false return this._everyOrSome(false, arr, callback, thisObject); // Boolean }, filter: function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){ // summary: // returns a new Array with those items from arr that match the // condition implemented by callback.thisObject may be used to // scope the call to callback. The function signature is derived // from the JavaScript 1.6 Array.filter() function, although // special accomidation is made in our implementation for strings. // More information on the JS 1.6 API can be found here: // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:filter // examples: // dojo.lang.some([1, 2, 3, 4], function(item){ return item>1; }); // // returns [2, 3, 4] var isString = dojo.lang.isString(arr); if(isString){ /*arr: String*/arr = arr.split(""); } var outArr; if(Array.filter){ outArr = Array.filter(arr, callback, thisObject); }else{ if(!thisObject){ if(arguments.length >= 3){ dojo.raise("thisObject doesn't exist!"); } thisObject = dj_global; } outArr = []; for(var i = 0; i < arr.length; i++){ if(callback.call(thisObject, arr[i], i, arr)){ outArr.push(arr[i]); } } } if(isString){ return outArr.join(""); // String } else { return outArr; // Array } }, unnest: function(/* ... */){ // summary: // Creates a 1-D array out of all the arguments passed, // unravelling any array-like objects in the process // usage: // unnest(1, 2, 3) ==> [1, 2, 3] // unnest(1, [2, [3], [[[4]]]]) ==> [1, 2, 3, 4] var out = []; for(var i = 0; i < arguments.length; i++){ if(dojo.lang.isArrayLike(arguments[i])){ var add = dojo.lang.unnest.apply(this, arguments[i]); out = out.concat(add); }else{ out.push(arguments[i]); } } return out; // Array }, toArray: function(/*Object*/arrayLike, /*Number*/startOffset){ // summary: // Converts an array-like object (i.e. arguments, DOMCollection) // to an array. Returns a new Array object. var array = []; for(var i = startOffset||0; i < arrayLike.length; i++){ array.push(arrayLike[i]); } return array; // Array } }); dojo.provide("dojo.lang.extras"); dojo.lang.setTimeout = function(/*Function*/func, /*int*/delay /*, ...*/){ // summary: // Sets a timeout in milliseconds to execute a function in a given // context with optional arguments. // usage: // dojo.lang.setTimeout(Object context, function func, number delay[, arg1[, ...]]); // dojo.lang.setTimeout(function func, number delay[, arg1[, ...]]); var context = window, argsStart = 2; if(!dojo.lang.isFunction(func)){ context = func; func = delay; delay = arguments[2]; argsStart++; } if(dojo.lang.isString(func)){ func = context[func]; } var args = []; for (var i = argsStart; i < arguments.length; i++){ args.push(arguments[i]); } return dojo.global().setTimeout(function(){ func.apply(context, args); }, delay); // int } dojo.lang.clearTimeout = function(/*int*/timer){ // summary: clears timer by number from the execution queue // FIXME: // why do we have this function? It's not portable outside of browser // environments and it's a stupid wrapper on something that browsers // provide anyway. dojo.global().clearTimeout(timer); } dojo.lang.getNameInObj = function(/*Object*/ns, /*unknown*/item){ // summary: // looks for a value in the object ns with a value matching item and // returns the property name // ns: if null, dj_global is used // item: value to return a name for if(!ns){ ns = dj_global; } for(var x in ns){ if(ns[x] === item){ return new String(x); // String } } return null; // null } dojo.lang.shallowCopy = function(/*Object*/obj, /*Boolean?*/deep){ // summary: // copies object obj one level deep, or full depth if deep is true var i, ret; if(obj === null){ /*obj: null*/ return null; } // null if(dojo.lang.isObject(obj)){ // obj: Object ret = new obj.constructor(); for(i in obj){ if(dojo.lang.isUndefined(ret[i])){ ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i]; } } }else if(dojo.lang.isArray(obj)){ // obj: Array ret = []; for(i=0; i2) ? args[0] : "after", precedence: "last", once: false, delay: null, rate: 0, adviceMsg: false, maxCalls: -1 }; switch(args.length){ case 0: return; case 1: return; case 2: ao.srcFunc = args[0]; ao.adviceFunc = args[1]; break; case 3: if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){ ao.adviceType = "after"; ao.srcObj = args[0]; ao.srcFunc = args[1]; ao.adviceFunc = args[2]; }else if((dl.isString(args[1]))&&(dl.isString(args[2]))){ ao.srcFunc = args[1]; ao.adviceFunc = args[2]; }else if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){ ao.adviceType = "after"; ao.srcObj = args[0]; ao.srcFunc = args[1]; var tmpName = dl.nameAnonFunc(args[2], ao.adviceObj, searchForNames); ao.adviceFunc = tmpName; }else if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){ ao.adviceType = "after"; ao.srcObj = dj_global; var tmpName = dl.nameAnonFunc(args[0], ao.srcObj, searchForNames); ao.srcFunc = tmpName; ao.adviceObj = args[1]; ao.adviceFunc = args[2]; } break; case 4: if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){ // we can assume that we've got an old-style "connect" from // the sigslot school of event attachment. We therefore // assume after-advice. ao.adviceType = "after"; ao.srcObj = args[0]; ao.srcFunc = args[1]; ao.adviceObj = args[2]; ao.adviceFunc = args[3]; }else if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){ ao.adviceType = args[0]; ao.srcObj = dj_global; ao.srcFunc = args[1]; ao.adviceObj = args[2]; ao.adviceFunc = args[3]; }else if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){ ao.adviceType = args[0]; ao.srcObj = dj_global; var tmpName = dl.nameAnonFunc(args[1], dj_global, searchForNames); ao.srcFunc = tmpName; ao.adviceObj = args[2]; ao.adviceFunc = args[3]; }else if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){ ao.srcObj = args[1]; ao.srcFunc = args[2]; var tmpName = dl.nameAnonFunc(args[3], dj_global, searchForNames); ao.adviceObj = dj_global; ao.adviceFunc = tmpName; }else if(dl.isObject(args[1])){ ao.srcObj = args[1]; ao.srcFunc = args[2]; ao.adviceObj = dj_global; ao.adviceFunc = args[3]; }else if(dl.isObject(args[2])){ ao.srcObj = dj_global; ao.srcFunc = args[1]; ao.adviceObj = args[2]; ao.adviceFunc = args[3]; }else{ ao.srcObj = ao.adviceObj = ao.aroundObj = dj_global; ao.srcFunc = args[1]; ao.adviceFunc = args[2]; ao.aroundFunc = args[3]; } break; case 6: ao.srcObj = args[1]; ao.srcFunc = args[2]; ao.adviceObj = args[3] ao.adviceFunc = args[4]; ao.aroundFunc = args[5]; ao.aroundObj = dj_global; break; default: ao.srcObj = args[1]; ao.srcFunc = args[2]; ao.adviceObj = args[3] ao.adviceFunc = args[4]; ao.aroundObj = args[5]; ao.aroundFunc = args[6]; ao.once = args[7]; ao.delay = args[8]; ao.rate = args[9]; ao.adviceMsg = args[10]; ao.maxCalls = (!isNaN(parseInt(args[11]))) ? args[11] : -1; break; } if(dl.isFunction(ao.aroundFunc)){ var tmpName = dl.nameAnonFunc(ao.aroundFunc, ao.aroundObj, searchForNames); ao.aroundFunc = tmpName; } if(dl.isFunction(ao.srcFunc)){ ao.srcFunc = dl.getNameInObj(ao.srcObj, ao.srcFunc); } if(dl.isFunction(ao.adviceFunc)){ ao.adviceFunc = dl.getNameInObj(ao.adviceObj, ao.adviceFunc); } if((ao.aroundObj)&&(dl.isFunction(ao.aroundFunc))){ ao.aroundFunc = dl.getNameInObj(ao.aroundObj, ao.aroundFunc); } if(!ao.srcObj){ dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc); } if(!ao.adviceObj){ dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc); } if(!ao.adviceFunc){ dojo.debug("bad adviceFunc for srcFunc: "+ao.srcFunc); dojo.debugShallow(ao); } return ao; } this.connect = function(/*...*/){ // summary: // dojo.event.connect is the glue that holds most Dojo-based // applications together. Most combinations of arguments are // supported, with the connect() method attempting to disambiguate // the implied types of positional parameters. The following will // all work: // dojo.event.connect("globalFunctionName1", "globalFunctionName2"); // dojo.event.connect(functionReference1, functionReference2); // dojo.event.connect("globalFunctionName1", functionReference2); // dojo.event.connect(functionReference1, "globalFunctionName2"); // dojo.event.connect(scope1, "functionName1", "globalFunctionName2"); // dojo.event.connect("globalFunctionName1", scope2, "functionName2"); // dojo.event.connect(scope1, "functionName1", scope2, "functionName2"); // dojo.event.connect("after", scope1, "functionName1", scope2, "functionName2"); // dojo.event.connect("before", scope1, "functionName1", scope2, "functionName2"); // dojo.event.connect("around", scope1, "functionName1", // scope2, "functionName2", // aroundFunctionReference); // dojo.event.connect("around", scope1, "functionName1", // scope2, "functionName2", // scope3, "aroundFunctionName"); // dojo.event.connect("before-around", scope1, "functionName1", // scope2, "functionName2", // aroundFunctionReference); // dojo.event.connect("after-around", scope1, "functionName1", // scope2, "functionName2", // aroundFunctionReference); // dojo.event.connect("after-around", scope1, "functionName1", // scope2, "functionName2", // scope3, "aroundFunctionName"); // dojo.event.connect("around", scope1, "functionName1", // scope2, "functionName2", // scope3, "aroundFunctionName", true, 30); // dojo.event.connect("around", scope1, "functionName1", // scope2, "functionName2", // scope3, "aroundFunctionName", null, null, 10); // adviceType: // Optional. String. One of "before", "after", "around", // "before-around", or "after-around". FIXME // srcObj: // the scope in which to locate/execute the named srcFunc. Along // with srcFunc, this creates a way to dereference the function to // call. So if the function in question is "foo.bar", the // srcObj/srcFunc pair would be foo and "bar", where "bar" is a // string and foo is an object reference. // srcFunc: // the name of the function to connect to. When it is executed, // the listener being registered with this call will be called. // The adviceType defines the call order between the source and // the target functions. // adviceObj: // the scope in which to locate/execute the named adviceFunc. // adviceFunc: // the name of the function being conected to srcObj.srcFunc // aroundObj: // the scope in which to locate/execute the named aroundFunc. // aroundFunc: // the name of, or a reference to, the function that will be used // to mediate the advice call. Around advice requires a special // unary function that will be passed a "MethodInvocation" object. // These objects have several important properties, namely: // - args // a mutable array of arguments to be passed into the // wrapped function // - proceed // a function that "continues" the invocation. The result // of this function is the return of the wrapped function. // You can then manipulate this return before passing it // back out (or take further action based on it). // once: // boolean that determines whether or not this connect() will // create a new connection if an identical connect() has already // been made. Defaults to "false". // delay: // an optional delay (in ms), as an integer, for dispatch of a // listener after the source has been fired. // rate: // an optional rate throttling parameter (integer, in ms). When // specified, this particular connection will not fire more than // once in the interval specified by the rate // adviceMsg: // boolean. Should the listener have all the parameters passed in // as a single argument? /* ao.adviceType = args[0]; ao.srcObj = args[1]; ao.srcFunc = args[2]; ao.adviceObj = args[3] ao.adviceFunc = args[4]; ao.aroundObj = args[5]; ao.aroundFunc = args[6]; ao.once = args[7]; ao.delay = args[8]; ao.rate = args[9]; ao.adviceMsg = args[10]; ao.maxCalls = args[11]; */ if(arguments.length == 1){ var ao = arguments[0]; }else{ var ao = interpolateArgs(arguments, true); } if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){ if(dojo.render.html.ie){ ao.srcFunc = "onkeydown"; this.connect(ao); } ao.srcFunc = "onkeypress"; } if(dojo.lang.isArray(ao.srcObj) && ao.srcObj!=""){ var tmpAO = {}; for(var x in ao){ tmpAO[x] = ao[x]; } var mjps = []; dojo.lang.forEach(ao.srcObj, function(src){ if((dojo.render.html.capable)&&(dojo.lang.isString(src))){ src = dojo.byId(src); // dojo.debug(src); } tmpAO.srcObj = src; // dojo.debug(tmpAO.srcObj, tmpAO.srcFunc); // dojo.debug(tmpAO.adviceObj, tmpAO.adviceFunc); mjps.push(dojo.event.connect.call(dojo.event, tmpAO)); }); return mjps; } // FIXME: just doing a "getForMethod()" seems to be enough to put this into infinite recursion!! var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc); if(ao.adviceFunc){ var mjp2 = dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc); } mjp.kwAddAdvice(ao); // advanced users might want to fsck w/ the join point manually return mjp; // a MethodJoinPoint object } this.log = function(/*object or funcName*/ a1, /*funcName*/ a2){ // summary: // a function that will wrap and log all calls to the specified // a1.a2() function. If only a1 is passed, it'll be used as a // function or function name on the global context. Logging will // be sent to dojo.debug // a1: // if a2 is passed, this should be an object. If not, it can be a // function or function name. // a2: // a function name var kwArgs; if((arguments.length == 1)&&(typeof a1 == "object")){ kwArgs = a1; }else{ kwArgs = { srcObj: a1, srcFunc: a2 }; } kwArgs.adviceFunc = function(){ var argsStr = []; for(var x=0; x= this.jp_.around.length){ return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args); // return this.jp_.run_before_after(this.object, this.args); }else{ var ti = this.jp_.around[this.around_index]; var mobj = ti[0]||dj_global; var meth = ti[1]; return mobj[meth].call(mobj, this); } } dojo.event.MethodJoinPoint = function(/*Object*/obj, /*String*/funcName){ this.object = obj||dj_global; this.methodname = funcName; this.methodfunc = this.object[funcName]; this.squelch = false; // this.before = []; // this.after = []; // this.around = []; } dojo.event.MethodJoinPoint.getForMethod = function(/*Object*/obj, /*String*/funcName){ // summary: // "static" class function for returning a MethodJoinPoint from a // scoped function. If one doesn't exist, one is created. // obj: // the scope to search for the function in // funcName: // the name of the function to return a MethodJoinPoint for if(!obj){ obj = dj_global; } var ofn = obj[funcName]; if(!ofn){ // supply a do-nothing method implementation ofn = obj[funcName] = function(){}; if(!obj[funcName]){ // e.g. cannot add to inbuilt objects in IE6 dojo.raise("Cannot set do-nothing method on that object "+funcName); } }else if((typeof ofn != "function")&&(!dojo.lang.isFunction(ofn))&&(!dojo.lang.isAlien(ofn))){ // FIXME: should we throw an exception here instead? return null; } // we hide our joinpoint instance in obj[funcName + '$joinpoint'] var jpname = funcName + "$joinpoint"; var jpfuncname = funcName + "$joinpoint$method"; var joinpoint = obj[jpname]; if(!joinpoint){ var isNode = false; if(dojo.event["browser"]){ if( (obj["attachEvent"])|| (obj["nodeType"])|| (obj["addEventListener"]) ){ isNode = true; dojo.event.browser.addClobberNodeAttrs(obj, [jpname, jpfuncname, funcName]); } } var origArity = ofn.length; obj[jpfuncname] = ofn; // joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, funcName); joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, jpfuncname); if(!isNode){ obj[funcName] = function(){ // var args = []; // for(var x=0; x -1){ if(maxCount == 0){ return; } marr[7]--; } var undef; var to = { args: [], jp_: this, object: obj, proceed: function(){ return callObj[callFunc].apply(callObj, to.args); } }; to.args = aargs; var delay = parseInt(marr[4]); var hasDelay = ((!isNaN(delay))&&(marr[4]!==null)&&(typeof marr[4] != "undefined")); if(marr[5]){ var rate = parseInt(marr[5]); var cur = new Date(); var timerSet = false; if((marr["last"])&&((cur-marr.last)<=rate)){ if(dojo.event._canTimeout){ if(marr["delayTimer"]){ clearTimeout(marr.delayTimer); } var tod = parseInt(rate*2); // is rate*2 naive? var mcpy = dojo.lang.shallowCopy(marr); marr.delayTimer = setTimeout(function(){ // FIXME: on IE at least, event objects from the // browser can go out of scope. How (or should?) we // deal with it? mcpy[5] = 0; unrollAdvice(mcpy); }, tod); } return; }else{ marr.last = cur; } } // FIXME: need to enforce rates for a connection here! if(aroundFunc){ // NOTE: around advice can't delay since we might otherwise depend // on execution order! aroundObj[aroundFunc].call(aroundObj, to); }else{ // var tmjp = dojo.event.MethodJoinPoint.getForMethod(obj, methname); if((hasDelay)&&((dojo.render.html)||(dojo.render.svg))){ // FIXME: the render checks are grotty! dj_global["setTimeout"](function(){ if(msg){ callObj[callFunc].call(callObj, to); }else{ callObj[callFunc].apply(callObj, args); } }, delay); }else{ // many environments can't support delay! if(msg){ callObj[callFunc].call(callObj, to); }else{ callObj[callFunc].apply(callObj, args); } } } }; var unRollSquelch = function(){ if(this.squelch){ try{ return unrollAdvice.apply(this, arguments); }catch(e){ dojo.debug(e); } }else{ return unrollAdvice.apply(this, arguments); } }; if((this["before"])&&(this.before.length>0)){ // pass a cloned array, if this event disconnects this event forEach on this.before wont work dojo.lang.forEach(this.before.concat(new Array()), unRollSquelch); } var result; try{ if((this["around"])&&(this.around.length>0)){ var mi = new dojo.event.MethodInvocation(this, obj, args); result = mi.proceed(); }else if(this.methodfunc){ result = this.object[this.methodname].apply(this.object, args); } }catch(e){ if(!this.squelch){ dojo.debug(e,"when calling",this.methodname,"on",this.object,"with arguments",args); dojo.raise(e); } } if((this["after"])&&(this.after.length>0)){ // see comment on this.before above dojo.lang.forEach(this.after.concat(new Array()), unRollSquelch); } return (this.methodfunc) ? result : null; }, getArr: function(/*String*/kind){ // summary: return a list of listeners of the past "kind" // kind: // can be one of: "before", "after", "around", "before-around", or // "after-around" var type = "after"; // FIXME: we should be able to do this through props or Array.in() if((typeof kind == "string")&&(kind.indexOf("before")!=-1)){ type = "before"; }else if(kind=="around"){ type = "around"; } if(!this[type]){ this[type] = []; } return this[type]; // Array }, kwAddAdvice: function(/*Object*/args){ // summary: // adds advice to the joinpoint with arguments in a map // args: // An object that can have the following properties: // - adviceType // - adviceObj // - adviceFunc // - aroundObj // - aroundFunc // - once // - delay // - rate // - adviceMsg // - maxCalls this.addAdvice( args["adviceObj"], args["adviceFunc"], args["aroundObj"], args["aroundFunc"], args["adviceType"], args["precedence"], args["once"], args["delay"], args["rate"], args["adviceMsg"], args["maxCalls"]); }, addAdvice: function( thisAdviceObj, thisAdvice, thisAroundObj, thisAround, adviceType, precedence, once, delay, rate, asMessage, maxCalls){ // summary: // add advice to this joinpoint using positional parameters // thisAdviceObj: // the scope in which to locate/execute the named adviceFunc. // thisAdviceFunc: // the name of the function being conected // thisAroundObj: // the scope in which to locate/execute the named aroundFunc. // thisAroundFunc: // the name of the function that will be used to mediate the // advice call. // adviceType: // Optional. String. One of "before", "after", "around", // "before-around", or "after-around". FIXME // once: // boolean that determines whether or not this advice will create // a new connection if an identical advice set has already been // provided. Defaults to "false". // delay: // an optional delay (in ms), as an integer, for dispatch of a // listener after the source has been fired. // rate: // an optional rate throttling parameter (integer, in ms). When // specified, this particular connection will not fire more than // once in the interval specified by the rate // adviceMsg: // boolean. Should the listener have all the parameters passed in // as a single argument? // maxCalls: // Integer. The maximum number of times this connection can be // used before being auto-disconnected. -1 signals that the // connection should never be disconnected. var arr = this.getArr(adviceType); if(!arr){ dojo.raise("bad this: " + this); } var ao = [thisAdviceObj, thisAdvice, thisAroundObj, thisAround, delay, rate, asMessage, maxCalls]; if(once){ if(this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr) >= 0){ return; } } if(precedence == "first"){ arr.unshift(ao); }else{ arr.push(ao); } }, hasAdvice: function(thisAdviceObj, thisAdvice, adviceType, arr){ // summary: // returns the array index of the first existing connection // betweened the passed advice and this joinpoint. Will be -1 if // none exists. // thisAdviceObj: // the scope in which to locate/execute the named adviceFunc. // thisAdviceFunc: // the name of the function being conected // adviceType: // Optional. String. One of "before", "after", "around", // "before-around", or "after-around". FIXME // arr: // Optional. The list of advices to search. Will be found via // adviceType if not passed if(!arr){ arr = this.getArr(adviceType); } var ind = -1; for(var x=0; x=0; i=i-1){ var el = na[i]; try{ if(el && el["__clobberAttrs__"]){ for(var j=0; j= 65 && unifiedCharCode <= 90 && evt.shiftKey == false){ unifiedCharCode += 32; } if(unifiedCharCode >= 1 && unifiedCharCode <= 26 && evt.ctrlKey){ unifiedCharCode += 96; // 001-032 = ctrl+[a-z] } evt.key = String.fromCharCode(unifiedCharCode); } } } else if(evt["type"] == "keypress"){ if(dojo.render.html.opera){ if(evt.which == 0){ evt.key = evt.keyCode; }else if(evt.which > 0){ switch(evt.which){ case evt.KEY_SHIFT: case evt.KEY_CTRL: case evt.KEY_ALT: case evt.KEY_CAPS_LOCK: case evt.KEY_NUM_LOCK: case evt.KEY_SCROLL_LOCK: break; case evt.KEY_PAUSE: case evt.KEY_TAB: case evt.KEY_BACKSPACE: case evt.KEY_ENTER: case evt.KEY_ESCAPE: evt.key = evt.which; break; default: var unifiedCharCode = evt.which; if((evt.ctrlKey || evt.altKey || evt.metaKey) && (evt.which >= 65 && evt.which <= 90 && evt.shiftKey == false)){ unifiedCharCode += 32; } evt.key = String.fromCharCode(unifiedCharCode); } } }else if(dojo.render.html.ie){ // catch some IE keys that are hard to get in keyDown // key combinations were handled in onKeyDown if(!evt.ctrlKey && !evt.altKey && evt.keyCode >= evt.KEY_SPACE){ evt.key = String.fromCharCode(evt.keyCode); } }else if(dojo.render.html.safari){ switch(evt.keyCode){ case 25: evt.key = evt.KEY_TAB; evt.shift = true;break; case 63232: evt.key = evt.KEY_UP_ARROW; break; case 63233: evt.key = evt.KEY_DOWN_ARROW; break; case 63234: evt.key = evt.KEY_LEFT_ARROW; break; case 63235: evt.key = evt.KEY_RIGHT_ARROW; break; case 63236: evt.key = evt.KEY_F1; break; case 63237: evt.key = evt.KEY_F2; break; case 63238: evt.key = evt.KEY_F3; break; case 63239: evt.key = evt.KEY_F4; break; case 63240: evt.key = evt.KEY_F5; break; case 63241: evt.key = evt.KEY_F6; break; case 63242: evt.key = evt.KEY_F7; break; case 63243: evt.key = evt.KEY_F8; break; case 63244: evt.key = evt.KEY_F9; break; case 63245: evt.key = evt.KEY_F10; break; case 63246: evt.key = evt.KEY_F11; break; case 63247: evt.key = evt.KEY_F12; break; case 63250: evt.key = evt.KEY_PAUSE; break; case 63272: evt.key = evt.KEY_DELETE; break; case 63273: evt.key = evt.KEY_HOME; break; case 63275: evt.key = evt.KEY_END; break; case 63276: evt.key = evt.KEY_PAGE_UP; break; case 63277: evt.key = evt.KEY_PAGE_DOWN; break; case 63302: evt.key = evt.KEY_INSERT; break; case 63248://prtscr case 63249://scrolllock case 63289://numlock break; default: evt.key = evt.charCode >= evt.KEY_SPACE ? String.fromCharCode(evt.charCode) : evt.keyCode; } }else{ evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode; } } } if(dojo.render.html.ie){ if(!evt.target){ evt.target = evt.srcElement; } if(!evt.currentTarget){ evt.currentTarget = (sender ? sender : evt.srcElement); } if(!evt.layerX){ evt.layerX = evt.offsetX; } if(!evt.layerY){ evt.layerY = evt.offsetY; } // FIXME: scroll position query is duped from dojo.html to avoid dependency on that entire module // DONOT replace the following to use dojo.body(), in IE, document.documentElement should be used // here rather than document.body var doc = (evt.srcElement && evt.srcElement.ownerDocument) ? evt.srcElement.ownerDocument : document; var docBody = ((dojo.render.html.ie55)||(doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement; if(!evt.pageX){ evt.pageX = evt.clientX + (docBody.scrollLeft || 0) } if(!evt.pageY){ evt.pageY = evt.clientY + (docBody.scrollTop || 0) } // mouseover if(evt.type == "mouseover"){ evt.relatedTarget = evt.fromElement; } // mouseout if(evt.type == "mouseout"){ evt.relatedTarget = evt.toElement; } this.currentEvent = evt; evt.callListener = this.callListener; evt.stopPropagation = this._stopPropagation; evt.preventDefault = this._preventDefault; } return evt; // Event } this.stopEvent = function(/*Event*/evt){ // summary: // prevents propigation and clobbers the default action of the // passed event // evt: Optional for IE. The native event object. if(window.event){ evt.cancelBubble = true; evt.returnValue = false; }else{ evt.preventDefault(); evt.stopPropagation(); } } } dojo.provide("dojo.string.common"); dojo.string.trim = function(/* string */str, /* integer? */wh){ // summary // Trim whitespace from str. If wh > 0, trim from start, if wh < 0, trim from end, else both if(!str.replace){ return str; } if(!str.length){ return str; } var re = (wh > 0) ? (/^\s+/) : (wh < 0) ? (/\s+$/) : (/^\s+|\s+$/g); return str.replace(re, ""); // string } dojo.string.trimStart = function(/* string */str) { // summary // Trim whitespace at the beginning of 'str' return dojo.string.trim(str, 1); // string } dojo.string.trimEnd = function(/* string */str) { // summary // Trim whitespace at the end of 'str' return dojo.string.trim(str, -1); } dojo.string.repeat = function(/* string */str, /* integer */count, /* string? */separator) { // summary // Return 'str' repeated 'count' times, optionally placing 'separator' between each rep var out = ""; for(var i = 0; i < count; i++) { out += str; if(separator && i < count - 1) { out += separator; } } return out; // string } dojo.string.pad = function(/* string */str, /* integer */len/*=2*/, /* string */ c/*='0'*/, /* integer */dir/*=1*/) { // summary // Pad 'str' to guarantee that it is at least 'len' length with the character 'c' at either the // start (dir=1) or end (dir=-1) of the string var out = String(str); if(!c) { c = '0'; } if(!dir) { dir = 1; } while(out.length < len) { if(dir > 0) { out = c + out; } else { out += c; } } return out; // string } dojo.string.padLeft = function(/* string */str, /* integer */len, /* string */c) { // summary // same as dojo.string.pad(str, len, c, 1) return dojo.string.pad(str, len, c, 1); // string } dojo.string.padRight = function(/* string */str, /* integer */len, /* string */c) { // summary // same as dojo.string.pad(str, len, c, -1) return dojo.string.pad(str, len, c, -1); // string } dojo.provide("dojo.string"); dojo.provide("dojo.io.common"); dojo.io.transports = []; dojo.io.hdlrFuncNames = [ "load", "error", "timeout" ]; dojo.io.Request = function(/*String*/ url, /*String*/ mimetype, /*String*/ transport, /*String or Boolean*/ changeUrl){ if((arguments.length == 1)&&(arguments[0].constructor == Object)){ this.fromKwArgs(arguments[0]); }else{ this.url = url; if(mimetype){ this.mimetype = mimetype; } if(transport){ this.transport = transport; } if(arguments.length >= 4){ this.changeUrl = changeUrl; } } } dojo.lang.extend(dojo.io.Request, { /** The URL to hit */ url: "", /** The mime type used to interrpret the response body */ mimetype: "text/plain", /** The HTTP method to use */ method: "GET", /** An Object containing key-value pairs to be included with the request */ content: undefined, // Object /** The transport medium to use */ transport: undefined, // String /** If defined the URL of the page is physically changed */ changeUrl: undefined, // String /** A form node to use in the request */ formNode: undefined, // HTMLFormElement /** Whether the request should be made synchronously */ sync: false, bindSuccess: false, /** Cache/look for the request in the cache before attempting to request? * NOTE: this isn't a browser cache, this is internal and would only cache in-page */ useCache: false, /** Prevent the browser from caching this by adding a query string argument to the URL */ preventCache: false, jsonFilter: function(value){ if( (this.mimetype == "text/json-comment-filtered")|| (this.mimetype == "application/json-comment-filtered") ){ var cStartIdx = value.indexOf("\/*"); var cEndIdx = value.lastIndexOf("*\/"); if((cStartIdx == -1)||(cEndIdx == -1)){ dojo.debug("your JSON wasn't comment filtered!"); return ""; } return value.substring(cStartIdx+2, cEndIdx); } dojo.debug("please consider using a mimetype of text/json-comment-filtered to avoid potential security issues with JSON endpoints"); return value; }, // events stuff load: function(/*String*/type, /*Object*/data, /*Object*/transportImplementation, /*Object*/kwArgs){ }, error: function(/*String*/type, /*Object*/error, /*Object*/transportImplementation, /*Object*/kwArgs){ }, timeout: function(/*String*/type, /*Object*/empty, /*Object*/transportImplementation, /*Object*/kwArgs){ }, handle: function(/*String*/type, /*Object*/data, /*Object*/transportImplementation, /*Object*/kwArgs){ }, timeoutSeconds: 0, // the abort method needs to be filled in by the transport that accepts the // bind() request abort: function(){ }, fromKwArgs: function(/*Object*/ kwArgs){ // summary: // Creates a dojo.io.Request from a simple object (kwArgs object). // normalize args if(kwArgs["url"]){ kwArgs.url = kwArgs.url.toString(); } if(kwArgs["formNode"]) { kwArgs.formNode = dojo.byId(kwArgs.formNode); } if(!kwArgs["method"] && kwArgs["formNode"] && kwArgs["formNode"].method) { kwArgs.method = kwArgs["formNode"].method; } // backwards compatibility if(!kwArgs["handle"] && kwArgs["handler"]){ kwArgs.handle = kwArgs.handler; } if(!kwArgs["load"] && kwArgs["loaded"]){ kwArgs.load = kwArgs.loaded; } if(!kwArgs["changeUrl"] && kwArgs["changeURL"]) { kwArgs.changeUrl = kwArgs.changeURL; } // encoding fun! kwArgs.encoding = dojo.lang.firstValued(kwArgs["encoding"], djConfig["bindEncoding"], ""); kwArgs.sendTransport = dojo.lang.firstValued(kwArgs["sendTransport"], djConfig["ioSendTransport"], false); var isFunction = dojo.lang.isFunction; for(var x=0; x 0){ dojo.io.bind(dojo.io._bindQueue.shift()); }else{ dojo.io._queueBindInFlight = false; } } } dojo.io._bindQueue = []; dojo.io._queueBindInFlight = false; dojo.io.argsFromMap = function(/*Object*/map, /*String?*/encoding, /*String?*/last){ // summary: // Converts name/values pairs in the map object to an URL-encoded string // with format of name1=value1&name2=value2... // map: Object // Object that has the contains the names and values. // encoding: String? // String to specify how to encode the name and value. If the encoding string // contains "utf" (case-insensitive), then encodeURIComponent is used. Otherwise // dojo.string.encodeAscii is used. // last: String? // The last parameter in the list. Helps with final string formatting? var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii; var mapped = []; var control = new Object(); for(var name in map){ var domap = function(elt){ var val = enc(name)+"="+enc(elt); mapped[(last == name) ? "push" : "unshift"](val); } if(!control[name]){ var value = map[name]; // FIXME: should be isArrayLike? if (dojo.lang.isArray(value)){ dojo.lang.forEach(value, domap); }else{ domap(value); } } } return mapped.join("&"); //String } dojo.io.setIFrameSrc = function(/*DOMNode*/ iframe, /*String*/ src, /*Boolean*/ replace){ //summary: // Sets the URL that is loaded in an IFrame. The replace parameter indicates whether // location.replace() should be used when changing the location of the iframe. try{ var r = dojo.render.html; // dojo.debug(iframe); if(!replace){ if(r.safari){ iframe.location = src; }else{ frames[iframe.name].location = src; } }else{ // Fun with DOM 0 incompatibilities! var idoc; if(r.ie){ idoc = iframe.contentWindow.document; }else if(r.safari){ idoc = iframe.document; }else{ // if(r.moz){ idoc = iframe.contentWindow; } //For Safari (at least 2.0.3) and Opera, if the iframe //has just been created but it doesn't have content //yet, then iframe.document may be null. In that case, //use iframe.location and return. if(!idoc){ iframe.location = src; return; }else{ idoc.location.replace(src); } } }catch(e){ dojo.debug(e); dojo.debug("setIFrameSrc: "+e); } } dojo.provide("dojo.string.extras"); //TODO: should we use ${} substitution syntax instead, like widgets do? dojo.string.substituteParams = function(/*string*/template, /* object - optional or ... */hash){ // summary: // Performs parameterized substitutions on a string. Throws an exception if any parameter is unmatched. // // description: // For example, // dojo.string.substituteParams("File '%{0}' is not found in directory '%{1}'.","foo.html","/temp"); // returns // "File 'foo.html' is not found in directory '/temp'." // // template: the original string template with %{values} to be replaced // hash: name/value pairs (type object) to provide substitutions. Alternatively, substitutions may be // included as arguments 1..n to this function, corresponding to template parameters 0..n-1 var map = (typeof hash == 'object') ? hash : dojo.lang.toArray(arguments, 1); return template.replace(/\%\{(\w+)\}/g, function(match, key){ if(typeof(map[key]) != "undefined" && map[key] != null){ return map[key]; } dojo.raise("Substitution not found: " + key); }); // string }; dojo.string.capitalize = function(/*string*/str){ // summary: // Uppercases the first letter of each word if(!dojo.lang.isString(str)){ return ""; } if(arguments.length == 0){ str = this; } var words = str.split(' '); for(var i=0; i"' // Optionally skips escapes for single quotes str = str.replace(/&/gm, "&").replace(//gm, ">").replace(/"/gm, """); if(!noSingleQuotes){ str = str.replace(/'/gm, "'"); } return str; // string } dojo.string.escapeSql = function(/*string*/str){ //summary: // Adds escape sequences for single quotes in SQL expressions return str.replace(/'/gm, "''"); //string } dojo.string.escapeRegExp = function(/*string*/str){ //summary: // Adds escape sequences for special characters in regular expressions return str.replace(/\\/gm, "\\\\").replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, "\\$1"); // string } //FIXME: should this one also escape backslash? dojo.string.escapeJavaScript = function(/*string*/str){ //summary: // Adds escape sequences for single and double quotes as well // as non-visible characters in JavaScript string literal expressions return str.replace(/(["'\f\b\n\t\r])/gm, "\\$1"); // string } //FIXME: looks a lot like escapeJavaScript, just adds quotes? deprecate one? dojo.string.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 } // TODO: make an HTML version dojo.string.summary = function(/*string*/str, /*number*/len){ // summary: // Truncates 'str' after 'len' characters and appends periods as necessary so that it ends with "..." if(!len || str.length <= len){ return str; // string } return str.substring(0, len).replace(/\.+$/, "") + "..."; // string } dojo.string.endsWith = function(/*string*/str, /*string*/end, /*boolean*/ignoreCase){ // summary: // Returns true if 'str' ends with 'end' if(ignoreCase){ str = str.toLowerCase(); end = end.toLowerCase(); } if((str.length - end.length) < 0){ return false; // boolean } return str.lastIndexOf(end) == str.length - end.length; // boolean } dojo.string.endsWithAny = function(/*string*/str /* , ... */){ // summary: // Returns true if 'str' ends with any of the arguments[2 -> n] for(var i = 1; i < arguments.length; i++) { if(dojo.string.endsWith(str, arguments[i])) { return true; // boolean } } return false; // boolean } dojo.string.startsWith = function(/*string*/str, /*string*/start, /*boolean*/ignoreCase){ // summary: // Returns true if 'str' starts with 'start' if(ignoreCase) { str = str.toLowerCase(); start = start.toLowerCase(); } return str.indexOf(start) == 0; // boolean } dojo.string.startsWithAny = function(/*string*/str /* , ... */){ // summary: // Returns true if 'str' starts with any of the arguments[2 -> n] for(var i = 1; i < arguments.length; i++) { if(dojo.string.startsWith(str, arguments[i])) { return true; // boolean } } return false; // boolean } dojo.string.has = function(/*string*/str /* , ... */) { // summary: // Returns true if 'str' contains any of the arguments 2 -> n for(var i = 1; i < arguments.length; i++) { if(str.indexOf(arguments[i]) > -1){ return true; // boolean } } return false; // boolean } dojo.string.normalizeNewlines = function(/*string*/text, /*string? (\n or \r)*/newlineChar){ // summary: // Changes occurences of CR and LF in text to CRLF, or if newlineChar is provided as '\n' or '\r', // substitutes newlineChar for occurrences of CR/LF and CRLF if (newlineChar == "\n"){ text = text.replace(/\r\n/g, "\n"); text = text.replace(/\r/g, "\n"); } else if (newlineChar == "\r"){ text = text.replace(/\r\n/g, "\r"); text = text.replace(/\n/g, "\r"); }else{ text = text.replace(/([^\r])\n/g, "$1\r\n").replace(/\r([^\n])/g, "\r\n$1"); } return text; // string } dojo.string.splitEscaped = function(/*string*/str, /*string of length=1*/charac){ // summary: // Splits 'str' into an array separated by 'charac', but skips characters escaped with a backslash var components = []; for (var i = 0, prevcomma = 0; i < str.length; i++){ if (str.charAt(i) == '\\'){ i++; continue; } if (str.charAt(i) == charac){ components.push(str.substring(prevcomma, i)); prevcomma = i + 1; } } components.push(str.substr(prevcomma)); return components; // array } dojo.provide("dojo.dom"); dojo.dom.ELEMENT_NODE = 1; dojo.dom.ATTRIBUTE_NODE = 2; dojo.dom.TEXT_NODE = 3; dojo.dom.CDATA_SECTION_NODE = 4; dojo.dom.ENTITY_REFERENCE_NODE = 5; dojo.dom.ENTITY_NODE = 6; dojo.dom.PROCESSING_INSTRUCTION_NODE = 7; dojo.dom.COMMENT_NODE = 8; dojo.dom.DOCUMENT_NODE = 9; dojo.dom.DOCUMENT_TYPE_NODE = 10; dojo.dom.DOCUMENT_FRAGMENT_NODE = 11; dojo.dom.NOTATION_NODE = 12; dojo.dom.dojoml = "http://www.dojotoolkit.org/2004/dojoml"; /** * comprehensive list of XML namespaces **/ dojo.dom.xmlns = { // summary // aliases for various common XML namespaces svg : "http://www.w3.org/2000/svg", smil : "http://www.w3.org/2001/SMIL20/", mml : "http://www.w3.org/1998/Math/MathML", cml : "http://www.xml-cml.org", xlink : "http://www.w3.org/1999/xlink", xhtml : "http://www.w3.org/1999/xhtml", xul : "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", xbl : "http://www.mozilla.org/xbl", fo : "http://www.w3.org/1999/XSL/Format", xsl : "http://www.w3.org/1999/XSL/Transform", xslt : "http://www.w3.org/1999/XSL/Transform", xi : "http://www.w3.org/2001/XInclude", xforms : "http://www.w3.org/2002/01/xforms", saxon : "http://icl.com/saxon", xalan : "http://xml.apache.org/xslt", xsd : "http://www.w3.org/2001/XMLSchema", dt: "http://www.w3.org/2001/XMLSchema-datatypes", xsi : "http://www.w3.org/2001/XMLSchema-instance", rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", rdfs : "http://www.w3.org/2000/01/rdf-schema#", dc : "http://purl.org/dc/elements/1.1/", dcq: "http://purl.org/dc/qualifiers/1.0", "soap-env" : "http://schemas.xmlsoap.org/soap/envelope/", wsdl : "http://schemas.xmlsoap.org/wsdl/", AdobeExtensions : "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/" }; dojo.dom.isNode = function(/* object */wh){ // summary: // checks to see if wh is actually a node. if(typeof Element == "function") { try { return wh instanceof Element; // boolean } catch(e) {} } else { // best-guess return wh && !isNaN(wh.nodeType); // boolean } } dojo.dom.getUniqueId = function(){ // summary: // returns a unique string for use with any DOM element var _document = dojo.doc(); do { var id = "dj_unique_" + (++arguments.callee._idIncrement); }while(_document.getElementById(id)); return id; // string } dojo.dom.getUniqueId._idIncrement = 0; dojo.dom.firstElement = dojo.dom.getFirstChildElement = function(/* Element */parentNode, /* string? */tagName){ // summary: // returns the first child element matching tagName var node = parentNode.firstChild; while(node && node.nodeType != dojo.dom.ELEMENT_NODE){ node = node.nextSibling; } if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) { node = dojo.dom.nextElement(node, tagName); } return node; // Element } dojo.dom.lastElement = dojo.dom.getLastChildElement = function(/* Element */parentNode, /* string? */tagName){ // summary: // returns the last child element matching tagName var node = parentNode.lastChild; while(node && node.nodeType != dojo.dom.ELEMENT_NODE) { node = node.previousSibling; } if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) { node = dojo.dom.prevElement(node, tagName); } return node; // Element } dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function(/* Node */node, /* string? */tagName){ // summary: // returns the next sibling element matching tagName if(!node) { return null; } do { node = node.nextSibling; } while(node && node.nodeType != dojo.dom.ELEMENT_NODE); if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) { return dojo.dom.nextElement(node, tagName); } return node; // Element } dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function(/* Node */node, /* string? */tagName){ // summary: // returns the previous sibling element matching tagName if(!node) { return null; } if(tagName) { tagName = tagName.toLowerCase(); } do { node = node.previousSibling; } while(node && node.nodeType != dojo.dom.ELEMENT_NODE); if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) { return dojo.dom.prevElement(node, tagName); } return node; // Element } // TODO: hmph /*this.forEachChildTag = function(node, unaryFunc) { var child = this.getFirstChildTag(node); while(child) { if(unaryFunc(child) == "break") { break; } child = this.getNextSiblingTag(child); } }*/ dojo.dom.moveChildren = function(/*Element*/srcNode, /*Element*/destNode, /*boolean?*/trim){ // summary: // Moves children from srcNode to destNode and returns the count of // children moved; will trim off text nodes if trim == true var count = 0; if(trim) { while(srcNode.hasChildNodes() && srcNode.firstChild.nodeType == dojo.dom.TEXT_NODE) { srcNode.removeChild(srcNode.firstChild); } while(srcNode.hasChildNodes() && srcNode.lastChild.nodeType == dojo.dom.TEXT_NODE) { srcNode.removeChild(srcNode.lastChild); } } while(srcNode.hasChildNodes()){ destNode.appendChild(srcNode.firstChild); count++; } return count; // number } dojo.dom.copyChildren = function(/*Element*/srcNode, /*Element*/destNode, /*boolean?*/trim){ // summary: // Copies children from srcNde to destNode and returns the count of // children copied; will trim off text nodes if trim == true var clonedNode = srcNode.cloneNode(true); return this.moveChildren(clonedNode, destNode, trim); // number } dojo.dom.replaceChildren = function(/*Element*/node, /*Node*/newChild){ // summary: // Removes all children of node and appends newChild. All the existing // children will be destroyed. // FIXME: what if newChild is an array-like object? var nodes = []; if(dojo.render.html.ie){ for(var i=0;i 0){ return ancestors[0]; // Node } node = node.parentNode; } if(returnFirstHit){ return null; } return ancestors; // array } dojo.dom.getAncestorsByTag = function(/*Node*/node, /*String*/tag, /*boolean?*/returnFirstHit){ // summary: // returns all ancestors matching tag (as tagName), will only return // first one if returnFirstHit tag = tag.toLowerCase(); return dojo.dom.getAncestors(node, function(el){ return ((el.tagName)&&(el.tagName.toLowerCase() == tag)); }, returnFirstHit); // Node || array } dojo.dom.getFirstAncestorByTag = function(/*Node*/node, /*string*/tag){ // summary: // Returns first ancestor of node with tag tagName return dojo.dom.getAncestorsByTag(node, tag, true); // Node } dojo.dom.isDescendantOf = function(/* Node */node, /* Node */ancestor, /* boolean? */guaranteeDescendant){ // summary // Returns boolean if node is a descendant of ancestor // guaranteeDescendant allows us to be a "true" isDescendantOf function if(guaranteeDescendant && node) { node = node.parentNode; } while(node) { if(node == ancestor){ return true; // boolean } node = node.parentNode; } return false; // boolean } dojo.dom.innerXML = function(/*Node*/node){ // summary: // Implementation of MS's innerXML function. if(node.innerXML){ return node.innerXML; // string }else if (node.xml){ return node.xml; // string }else if(typeof XMLSerializer != "undefined"){ return (new XMLSerializer()).serializeToString(node); // string } } dojo.dom.createDocument = function(){ // summary: // cross-browser implementation of creating an XML document object. var doc = null; var _document = dojo.doc(); if(!dj_undef("ActiveXObject")){ var prefixes = [ "MSXML2", "Microsoft", "MSXML", "MSXML3" ]; for(var i = 0; i1) { var _document = dojo.doc(); dojo.dom.replaceChildren(node, _document.createTextNode(text)); return text; // string } else { if(node.textContent != undefined){ //FF 1.5 return node.textContent; // string } var _result = ""; if (node == null) { return _result; } for (var i = 0; i < node.childNodes.length; i++) { switch (node.childNodes[i].nodeType) { case 1: // ELEMENT_NODE case 5: // ENTITY_REFERENCE_NODE _result += dojo.dom.textContent(node.childNodes[i]); break; case 3: // TEXT_NODE case 2: // ATTRIBUTE_NODE case 4: // CDATA_SECTION_NODE _result += node.childNodes[i].nodeValue; break; default: break; } } return _result; // string } } dojo.dom.hasParent = function(/*Node*/node){ // summary: // returns whether or not node is a child of another node. return Boolean(node && node.parentNode && dojo.dom.isNode(node.parentNode)); // boolean } /** * Examples: * * myFooNode = * isTag(myFooNode, "foo"); // returns "foo" * isTag(myFooNode, "bar"); // returns "" * isTag(myFooNode, "FOO"); // returns "" * isTag(myFooNode, "hey", "foo", "bar"); // returns "foo" **/ dojo.dom.isTag = function(/* Node */node /* ... */){ // summary: // determines if node has any of the provided tag names and returns // the tag name that matches, empty string otherwise. if(node && node.tagName) { for(var i=1; i"); } }catch(e){/* squelch */} if(dojo.render.html.opera){ dojo.debug("Opera is not supported with dojo.undo.browser, so back/forward detection will not work."); } dojo.undo.browser = { initialHref: (!dj_undef("window")) ? window.location.href : "", initialHash: (!dj_undef("window")) ? window.location.hash : "", moveForward: false, historyStack: [], forwardStack: [], historyIframe: null, bookmarkAnchor: null, locationTimer: null, /** * */ setInitialState: function(/*Object*/args){ //summary: Sets the state object and back callback for the very first page that is loaded. //description: It is recommended that you call this method as part of an event listener that is registered via //dojo.addOnLoad(). //args: Object // See the addToHistory() function for the list of valid args properties. this.initialState = this._createState(this.initialHref, args, this.initialHash); }, //FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things. //FIXME: is there a slight race condition in moz using change URL with the timer check and when // the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent. addToHistory: function(args){ //summary: adds a state object (args) to the history list. You must set //djConfig.preventBackButtonFix = false to use dojo.undo.browser. //args: Object // args can have the following properties: // To support getting back button notifications, the object argument should implement a // function called either "back", "backButton", or "handle". The string "back" will be // passed as the first and only argument to this callback. // - To support getting forward button notifications, the object argument should implement a // function called either "forward", "forwardButton", or "handle". The string "forward" will be // passed as the first and only argument to this callback. // - If you want the browser location string to change, define "changeUrl" on the object. If the // value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment // identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does // not evaluate to false, that value will be used as the fragment identifier. For example, // if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1 // Full example: // dojo.undo.browser.addToHistory({ // back: function() { alert('back pressed'); }, // forward: function() { alert('forward pressed'); }, // changeUrl: true // }); // // BROWSER NOTES: // Safari 1.2: // back button "works" fine, however it's not possible to actually // DETECT that you've moved backwards by inspecting window.location. // Unless there is some other means of locating. // FIXME: perhaps we can poll on history.length? // Safari 2.0.3+ (and probably 1.3.2+): // works fine, except when changeUrl is used. When changeUrl is used, // Safari jumps all the way back to whatever page was shown before // the page that uses dojo.undo.browser support. // IE 5.5 SP2: // back button behavior is macro. It does not move back to the // previous hash value, but to the last full page load. This suggests // that the iframe is the correct way to capture the back button in // these cases. // Don't test this page using local disk for MSIE. MSIE will not create // a history list for iframe_history.html if served from a file: URL. // The XML served back from the XHR tests will also not be properly // created if served from local disk. Serve the test pages from a web // server to test in that browser. // IE 6.0: // same behavior as IE 5.5 SP2 // Firefox 1.0+: // the back button will return us to the previous hash on the same // page, thereby not requiring an iframe hack, although we do then // need to run a timer to detect inter-page movement. //If addToHistory is called, then that means we prune the //forward stack -- the user went back, then wanted to //start a new forward path. this.forwardStack = []; var hash = null; var url = null; if(!this.historyIframe){ if(djConfig["useXDomain"] && !djConfig["dojoIframeHistoryUrl"]){ dojo.debug("dojo.undo.browser: When using cross-domain Dojo builds," + " please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl" + " to the path on your domain to iframe_history.html"); } this.historyIframe = window.frames["djhistory"]; } if(!this.bookmarkAnchor){ this.bookmarkAnchor = document.createElement("a"); dojo.body().appendChild(this.bookmarkAnchor); this.bookmarkAnchor.style.display = "none"; } if(args["changeUrl"]){ hash = "#"+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime()); //If the current hash matches the new one, just replace the history object with //this new one. It doesn't make sense to track different state objects for the same //logical URL. This matches the browser behavior of only putting in one history //item no matter how many times you click on the same #hash link, at least in Firefox //and Safari, and there is no reliable way in those browsers to know if a #hash link //has been clicked on multiple times. So making this the standard behavior in all browsers //so that dojo.undo.browser's behavior is the same in all browsers. if(this.historyStack.length == 0 && this.initialState.urlHash == hash){ this.initialState = this._createState(url, args, hash); return; }else if(this.historyStack.length > 0 && this.historyStack[this.historyStack.length - 1].urlHash == hash){ this.historyStack[this.historyStack.length - 1] = this._createState(url, args, hash); return; } this.changingUrl = true; setTimeout("window.location.href = '"+hash+"'; dojo.undo.browser.changingUrl = false;", 1); this.bookmarkAnchor.href = hash; if(dojo.render.html.ie){ url = this._loadIframeHistory(); var oldCB = args["back"]||args["backButton"]||args["handle"]; //The function takes handleName as a parameter, in case the //callback we are overriding was "handle". In that case, //we will need to pass the handle name to handle. var tcb = function(handleName){ if(window.location.hash != ""){ setTimeout("window.location.href = '"+hash+"';", 1); } //Use apply to set "this" to args, and to try to avoid memory leaks. oldCB.apply(this, [handleName]); } //Set interceptor function in the right place. if(args["back"]){ args.back = tcb; }else if(args["backButton"]){ args.backButton = tcb; }else if(args["handle"]){ args.handle = tcb; } var oldFW = args["forward"]||args["forwardButton"]||args["handle"]; //The function takes handleName as a parameter, in case the //callback we are overriding was "handle". In that case, //we will need to pass the handle name to handle. var tfw = function(handleName){ if(window.location.hash != ""){ window.location.href = hash; } if(oldFW){ // we might not actually have one //Use apply to set "this" to args, and to try to avoid memory leaks. oldFW.apply(this, [handleName]); } } //Set interceptor function in the right place. if(args["forward"]){ args.forward = tfw; }else if(args["forwardButton"]){ args.forwardButton = tfw; }else if(args["handle"]){ args.handle = tfw; } }else if(dojo.render.html.moz){ // start the timer if(!this.locationTimer){ this.locationTimer = setInterval("dojo.undo.browser.checkLocation();", 200); } } }else{ url = this._loadIframeHistory(); } this.historyStack.push(this._createState(url, args, hash)); }, checkLocation: function(){ //summary: private method. Do not call this directly. if (!this.changingUrl){ var hsl = this.historyStack.length; if((window.location.hash == this.initialHash||window.location.href == this.initialHref)&&(hsl == 1)){ // FIXME: could this ever be a forward button? // we can't clear it because we still need to check for forwards. Ugg. // clearInterval(this.locationTimer); this.handleBackButton(); return; } // first check to see if we could have gone forward. We always halt on // a no-hash item. if(this.forwardStack.length > 0){ if(this.forwardStack[this.forwardStack.length-1].urlHash == window.location.hash){ this.handleForwardButton(); return; } } // ok, that didn't work, try someplace back in the history stack if((hsl >= 2)&&(this.historyStack[hsl-2])){ if(this.historyStack[hsl-2].urlHash==window.location.hash){ this.handleBackButton(); return; } } } }, iframeLoaded: function(evt, ifrLoc){ //summary: private method. Do not call this directly. if(!dojo.render.html.opera){ var query = this._getUrlQuery(ifrLoc.href); if(query == null){ // alert("iframeLoaded"); // we hit the end of the history, so we should go back if(this.historyStack.length == 1){ this.handleBackButton(); } return; } if(this.moveForward){ // we were expecting it, so it's not either a forward or backward movement this.moveForward = false; return; } //Check the back stack first, since it is more likely. //Note that only one step back or forward is supported. if(this.historyStack.length >= 2 && query == this._getUrlQuery(this.historyStack[this.historyStack.length-2].url)){ this.handleBackButton(); } else if(this.forwardStack.length > 0 && query == this._getUrlQuery(this.forwardStack[this.forwardStack.length-1].url)){ this.handleForwardButton(); } } }, handleBackButton: function(){ //summary: private method. Do not call this directly. //The "current" page is always at the top of the history stack. var current = this.historyStack.pop(); if(!current){ return; } var last = this.historyStack[this.historyStack.length-1]; if(!last && this.historyStack.length == 0){ last = this.initialState; } if (last){ if(last.kwArgs["back"]){ last.kwArgs["back"](); }else if(last.kwArgs["backButton"]){ last.kwArgs["backButton"](); }else if(last.kwArgs["handle"]){ last.kwArgs.handle("back"); } } this.forwardStack.push(current); }, handleForwardButton: function(){ //summary: private method. Do not call this directly. var last = this.forwardStack.pop(); if(!last){ return; } if(last.kwArgs["forward"]){ last.kwArgs.forward(); }else if(last.kwArgs["forwardButton"]){ last.kwArgs.forwardButton(); }else if(last.kwArgs["handle"]){ last.kwArgs.handle("forward"); } this.historyStack.push(last); }, _createState: function(url, args, hash){ //summary: private method. Do not call this directly. return {"url": url, "kwArgs": args, "urlHash": hash}; //Object }, _getUrlQuery: function(url){ //summary: private method. Do not call this directly. var segments = url.split("?"); if (segments.length < 2){ return null; //null } else{ return segments[1]; //String } }, _loadIframeHistory: function(){ //summary: private method. Do not call this directly. var url = (djConfig["dojoIframeHistoryUrl"] || dojo.hostenv.getBaseScriptUri()+'iframe_history.html') + "?" + (new Date()).getTime(); this.moveForward = true; dojo.io.setIFrameSrc(this.historyIframe, url, false); return url; //String } } dojo.provide("dojo.io.BrowserIO"); if(!dj_undef("window")) { dojo.io.checkChildrenForFile = function(/*DOMNode*/node){ //summary: Checks any child nodes of node for an input type="file" element. var hasFile = false; var inputs = node.getElementsByTagName("input"); dojo.lang.forEach(inputs, function(input){ if(hasFile){ return; } if(input.getAttribute("type")=="file"){ hasFile = true; } }); return hasFile; //boolean } dojo.io.formHasFile = function(/*DOMNode*/formNode){ //summary: Just calls dojo.io.checkChildrenForFile(). return dojo.io.checkChildrenForFile(formNode); //boolean } dojo.io.updateNode = function(/*DOMNode*/node, /*String or Object*/urlOrArgs){ //summary: Updates a DOMnode with the result of a dojo.io.bind() call. //node: DOMNode //urlOrArgs: String or Object // Either a String that has an URL, or an object containing dojo.io.bind() // arguments. node = dojo.byId(node); var args = urlOrArgs; if(dojo.lang.isString(urlOrArgs)){ args = { url: urlOrArgs }; } args.mimetype = "text/html"; args.load = function(t, d, e){ while(node.firstChild){ dojo.dom.destroyNode(node.firstChild); } node.innerHTML = d; }; dojo.io.bind(args); } dojo.io.formFilter = function(/*DOMNode*/node) { //summary: Returns true if the node is an input element that is enabled, has //a name, and whose type is one of the following values: ["file", "submit", "image", "reset", "button"] var type = (node.type||"").toLowerCase(); return !node.disabled && node.name && !dojo.lang.inArray(["file", "submit", "image", "reset", "button"], type); //boolean } // TODO: Move to htmlUtils dojo.io.encodeForm = function(/*DOMNode*/formNode, /*String?*/encoding, /*Function?*/formFilter){ //summary: Converts the names and values of form elements into an URL-encoded //string (name=value&name=value...). //formNode: DOMNode //encoding: String? // The encoding to use for the values. Specify a string that starts with // "utf" (for instance, "utf8"), to use encodeURIComponent() as the encoding // function. Otherwise, dojo.string.encodeAscii will be used. //formFilter: Function? // A function used to filter out form elements. The element node will be passed // to the formFilter function, and a boolean result is expected (true indicating // indicating that the element should have its name/value included in the output). // If no formFilter is specified, then dojo.io.formFilter() will be used. if((!formNode)||(!formNode.tagName)||(!formNode.tagName.toLowerCase() == "form")){ dojo.raise("Attempted to encode a non-form element."); } if(!formFilter) { formFilter = dojo.io.formFilter; } var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii; var values = []; for(var i = 0; i < formNode.elements.length; i++){ var elm = formNode.elements[i]; if(!elm || elm.tagName.toLowerCase() == "fieldset" || !formFilter(elm)) { continue; } var name = enc(elm.name); var type = elm.type.toLowerCase(); if(type == "select-multiple"){ for(var j = 0; j < elm.options.length; j++){ if(elm.options[j].selected) { values.push(name + "=" + enc(elm.options[j].value)); } } }else if(dojo.lang.inArray(["radio", "checkbox"], type)){ if(elm.checked){ values.push(name + "=" + enc(elm.value)); } }else{ values.push(name + "=" + enc(elm.value)); } } // now collect input type="image", which doesn't show up in the elements array var inputs = formNode.getElementsByTagName("input"); for(var i = 0; i < inputs.length; i++) { var input = inputs[i]; if(input.type.toLowerCase() == "image" && input.form == formNode && formFilter(input)) { var name = enc(input.name); values.push(name + "=" + enc(input.value)); values.push(name + ".x=0"); values.push(name + ".y=0"); } } return values.join("&") + "&"; //String } dojo.io.FormBind = function(/*DOMNode or Object*/args) { //summary: constructor for a dojo.io.FormBind object. See the Dojo Book for //some information on usage: http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book23 //args: DOMNode or Object // args can either be the DOMNode for a form element, or an object containing // dojo.io.bind() arguments, one of which should be formNode with the value of // a form element DOMNode. this.bindArgs = {}; if(args && args.formNode) { this.init(args); } else if(args) { this.init({formNode: args}); } } dojo.lang.extend(dojo.io.FormBind, { form: null, bindArgs: null, clickedButton: null, init: function(/*DOMNode or Object*/args) { //summary: Internal function called by the dojo.io.FormBind() constructor //do not call this method directly. var form = dojo.byId(args.formNode); if(!form || !form.tagName || form.tagName.toLowerCase() != "form") { throw new Error("FormBind: Couldn't apply, invalid form"); } else if(this.form == form) { return; } else if(this.form) { throw new Error("FormBind: Already applied to a form"); } dojo.lang.mixin(this.bindArgs, args); this.form = form; this.connect(form, "onsubmit", "submit"); for(var i = 0; i < form.elements.length; i++) { var node = form.elements[i]; if(node && node.type && dojo.lang.inArray(["submit", "button"], node.type.toLowerCase())) { this.connect(node, "onclick", "click"); } } var inputs = form.getElementsByTagName("input"); for(var i = 0; i < inputs.length; i++) { var input = inputs[i]; if(input.type.toLowerCase() == "image" && input.form == form) { this.connect(input, "onclick", "click"); } } }, onSubmit: function(/*DOMNode*/form) { //summary: Function used to verify that the form is OK to submit. //Override this function if you want specific form validation done. return true; //boolean }, submit: function(/*Event*/e) { //summary: internal function that is connected as a listener to the //form's onsubmit event. e.preventDefault(); if(this.onSubmit(this.form)) { dojo.io.bind(dojo.lang.mixin(this.bindArgs, { formFilter: dojo.lang.hitch(this, "formFilter") })); } }, click: function(/*Event*/e) { //summary: internal method that is connected as a listener to the //form's elements whose click event can submit a form. var node = e.currentTarget; if(node.disabled) { return; } this.clickedButton = node; }, formFilter: function(/*DOMNode*/node) { //summary: internal function used to know which form element values to include // in the dojo.io.bind() request. var type = (node.type||"").toLowerCase(); var accept = false; if(node.disabled || !node.name) { accept = false; } else if(dojo.lang.inArray(["submit", "button", "image"], type)) { if(!this.clickedButton) { this.clickedButton = node; } accept = node == this.clickedButton; } else { accept = !dojo.lang.inArray(["file", "submit", "reset", "button"], type); } return accept; //boolean }, // in case you don't have dojo.event.* pulled in connect: function(/*Object*/srcObj, /*Function*/srcFcn, /*Function*/targetFcn) { //summary: internal function used to connect event listeners to form elements //that trigger events. Used in case dojo.event is not loaded. if(dojo.evalObjPath("dojo.event.connect")) { dojo.event.connect(srcObj, srcFcn, this, targetFcn); } else { var fcn = dojo.lang.hitch(this, targetFcn); srcObj[srcFcn] = function(e) { if(!e) { e = window.event; } if(!e.currentTarget) { e.currentTarget = e.srcElement; } if(!e.preventDefault) { e.preventDefault = function() { window.event.returnValue = false; } } fcn(e); } } } }); dojo.io.XMLHTTPTransport = new function(){ //summary: The object that implements the dojo.io.bind transport for XMLHttpRequest. var _this = this; var _cache = {}; // FIXME: make this public? do we even need to? this.useCache = false; // if this is true, we'll cache unless kwArgs.useCache = false this.preventCache = false; // if this is true, we'll always force GET requests to cache // FIXME: Should this even be a function? or do we just hard code it in the next 2 functions? function getCacheKey(url, query, method) { return url + "|" + query + "|" + method.toLowerCase(); } function addToCache(url, query, method, http) { _cache[getCacheKey(url, query, method)] = http; } function getFromCache(url, query, method) { return _cache[getCacheKey(url, query, method)]; } this.clearCache = function() { _cache = {}; } // moved successful load stuff here function doLoad(kwArgs, http, url, query, useCache) { if( ((http.status>=200)&&(http.status<300))|| // allow any 2XX response code (http.status==304)|| // get it out of the cache (http.status==1223)|| // Internet Explorer mangled the status code (location.protocol=="file:" && (http.status==0 || http.status==undefined))|| (location.protocol=="chrome:" && (http.status==0 || http.status==undefined)) ){ var ret; if(kwArgs.method.toLowerCase() == "head"){ var headers = http.getAllResponseHeaders(); ret = {}; ret.toString = function(){ return headers; } var values = headers.split(/[\r\n]+/g); for(var i = 0; i < values.length; i++) { var pair = values[i].match(/^([^:]+)\s*:\s*(.+)$/i); if(pair) { ret[pair[1]] = pair[2]; } } }else if(kwArgs.mimetype == "text/javascript"){ try{ ret = dj_eval(http.responseText); }catch(e){ dojo.debug(e); dojo.debug(http.responseText); ret = null; } }else if(kwArgs.mimetype.substr(0, 9) == "text/json" || kwArgs.mimetype.substr(0, 16) == "application/json"){ try{ ret = dj_eval("("+kwArgs.jsonFilter(http.responseText)+")"); }catch(e){ dojo.debug(e); dojo.debug(http.responseText); ret = false; } }else if((kwArgs.mimetype == "application/xml")|| (kwArgs.mimetype == "text/xml")){ ret = http.responseXML; if(!ret || typeof ret == "string" || !http.getResponseHeader("Content-Type")) { ret = dojo.dom.createDocumentFromText(http.responseText); } }else{ ret = http.responseText; } if(useCache){ // only cache successful responses addToCache(url, query, kwArgs.method, http); } kwArgs[(typeof kwArgs.load == "function") ? "load" : "handle"]("load", ret, http, kwArgs); }else{ var errObj = new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText); kwArgs[(typeof kwArgs.error == "function") ? "error" : "handle"]("error", errObj, http, kwArgs); } } // set headers (note: Content-Type will get overriden if kwArgs.contentType is set) function setHeaders(http, kwArgs){ if(kwArgs["headers"]) { for(var header in kwArgs["headers"]) { if(header.toLowerCase() == "content-type" && !kwArgs["contentType"]) { kwArgs["contentType"] = kwArgs["headers"][header]; } else { http.setRequestHeader(header, kwArgs["headers"][header]); } } } } this.inFlight = []; this.inFlightTimer = null; this.startWatchingInFlight = function(){ //summary: internal method used to trigger a timer to watch all inflight //XMLHttpRequests. if(!this.inFlightTimer){ // setInterval broken in mozilla x86_64 in some circumstances, see // https://bugzilla.mozilla.org/show_bug.cgi?id=344439 // using setTimeout instead this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10); } } this.watchInFlight = function(){ //summary: internal method that checks each inflight XMLHttpRequest to see //if it has completed or if the timeout situation applies. var now = null; // make sure sync calls stay thread safe, if this callback is called during a sync call // and this results in another sync call before the first sync call ends the browser hangs if(!dojo.hostenv._blockAsync && !_this._blockAsync){ for(var x=this.inFlight.length-1; x>=0; x--){ try{ var tif = this.inFlight[x]; if(!tif || tif.http._aborted || !tif.http.readyState){ this.inFlight.splice(x, 1); continue; } if(4==tif.http.readyState){ // remove it so we can clean refs this.inFlight.splice(x, 1); doLoad(tif.req, tif.http, tif.url, tif.query, tif.useCache); }else if (tif.startTime){ //See if this is a timeout case. if(!now){ now = (new Date()).getTime(); } if(tif.startTime + (tif.req.timeoutSeconds * 1000) < now){ //Stop the request. if(typeof tif.http.abort == "function"){ tif.http.abort(); } // remove it so we can clean refs this.inFlight.splice(x, 1); tif.req[(typeof tif.req.timeout == "function") ? "timeout" : "handle"]("timeout", null, tif.http, tif.req); } } }catch(e){ try{ var errObj = new dojo.io.Error("XMLHttpTransport.watchInFlight Error: " + e); tif.req[(typeof tif.req.error == "function") ? "error" : "handle"]("error", errObj, tif.http, tif.req); }catch(e2){ dojo.debug("XMLHttpTransport error callback failed: " + e2); } } } } clearTimeout(this.inFlightTimer); if(this.inFlight.length == 0){ this.inFlightTimer = null; return; } this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10); } var hasXmlHttp = dojo.hostenv.getXmlhttpObject() ? true : false; this.canHandle = function(/*dojo.io.Request*/kwArgs){ //summary: Tells dojo.io.bind() if this is a good transport to //use for the particular type of request. This type of transport cannot //handle forms that have an input type="file" element. // FIXME: we need to determine when form values need to be // multi-part mime encoded and avoid using this transport for those // requests. var mlc = kwArgs["mimetype"].toLowerCase()||""; return hasXmlHttp && ( ( dojo.lang.inArray([ "text/plain", "text/html", "application/xml", "text/xml", "text/javascript" ], mlc ) ) || ( mlc.substr(0, 9) == "text/json" || mlc.substr(0, 16) == "application/json" ) ) && !( kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]) ); //boolean } this.multipartBoundary = "45309FFF-BD65-4d50-99C9-36986896A96F"; // unique guid as a boundary value for multipart posts this.bind = function(/*dojo.io.Request*/kwArgs){ //summary: function that sends the request to the server. //This function will attach an abort() function to the kwArgs dojo.io.Request object, //so if you need to abort the request, you can call that method on the request object. //The following are acceptable properties in kwArgs (in addition to the //normal dojo.io.Request object properties). //url: String: URL the server URL to use for the request. //method: String: the HTTP method to use (GET, POST, etc...). //mimetype: Specifies what format the result data should be given to the load/handle callback. Valid values are: // text/javascript, text/json, application/json, application/xml, text/xml. Any other mimetype will give back a text // string. //transport: String: specify "XMLHTTPTransport" to force the use of this XMLHttpRequest transport. //headers: Object: The object property names and values will be sent as HTTP request header // names and values. //sendTransport: boolean: If true, then dojo.transport=xmlhttp will be added to the request. //encoding: String: The type of encoding to use when dealing with the content kwArgs property. //content: Object: The content object is converted into a name=value&name=value string, by // using dojo.io.argsFromMap(). The encoding kwArgs property is passed to dojo.io.argsFromMap() // for use in encoding the names and values. The resulting string is added to the request. //formNode: DOMNode: a form element node. This should not normally be used. Use new dojo.io.FormBind() instead. // If formNode is used, then the names and values of the form elements will be converted // to a name=value&name=value string and added to the request. The encoding kwArgs property is used // to encode the names and values. //postContent: String: Raw name=value&name=value string to be included as part of the request. //back or backButton: Function: A function to be called if the back button is pressed. If this kwArgs property // is used, then back button support via dojo.undo.browser will be used. See notes for dojo.undo.browser on usage. // You need to set djConfig.preventBackButtonFix = false to enable back button support. //changeUrl: boolean or String: Used as part of back button support. See notes for dojo.undo.browser on usage. //user: String: The user name. Used in conjuction with password. Passed to XMLHttpRequest.open(). //password: String: The user's password. Used in conjuction with user. Passed to XMLHttpRequest.open(). //file: Object or Array of Objects: an object simulating a file to be uploaded. file objects should have the following properties: // name or fileName: the name of the file // contentType: the MIME content type for the file. // content: the actual content of the file. //multipart: boolean: indicates whether this should be a multipart mime request. If kwArgs.file exists, then this // property is set to true automatically. //sync: boolean: if true, then a synchronous XMLHttpRequest call is done, // if false (the default), then an asynchronous call is used. //preventCache: boolean: If true, then a cache busting parameter is added to the request URL. // default value is false. //useCache: boolean: If true, then XMLHttpTransport will keep an internal cache of the server // response and use that response if a similar request is done again. // A similar request is one that has the same URL, query string and HTTP method value. // default is false. if(!kwArgs["url"]){ // are we performing a history action? if( !kwArgs["formNode"] && (kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"] || kwArgs["watchForURL"]) && (!djConfig.preventBackButtonFix)) { dojo.deprecated("Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request", "Use dojo.undo.browser.addToHistory() instead.", "0.4"); dojo.undo.browser.addToHistory(kwArgs); return true; } } // build this first for cache purposes var url = kwArgs.url; var query = ""; if(kwArgs["formNode"]){ var ta = kwArgs.formNode.getAttribute("action"); if((ta)&&(!kwArgs["url"])){ url = ta; } var tp = kwArgs.formNode.getAttribute("method"); if((tp)&&(!kwArgs["method"])){ kwArgs.method = tp; } query += dojo.io.encodeForm(kwArgs.formNode, kwArgs.encoding, kwArgs["formFilter"]); } if(url.indexOf("#") > -1) { dojo.debug("Warning: dojo.io.bind: stripping hash values from url:", url); url = url.split("#")[0]; } if(kwArgs["file"]){ // force post for file transfer kwArgs.method = "post"; } if(!kwArgs["method"]){ kwArgs.method = "get"; } // guess the multipart value if(kwArgs.method.toLowerCase() == "get"){ // GET cannot use multipart kwArgs.multipart = false; }else{ if(kwArgs["file"]){ // enforce multipart when sending files kwArgs.multipart = true; }else if(!kwArgs["multipart"]){ // default kwArgs.multipart = false; } } if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){ dojo.undo.browser.addToHistory(kwArgs); } var content = kwArgs["content"] || {}; if(kwArgs.sendTransport) { content["dojo.transport"] = "xmlhttp"; } do { // break-block if(kwArgs.postContent){ query = kwArgs.postContent; break; } if(content) { query += dojo.io.argsFromMap(content, kwArgs.encoding); } if(kwArgs.method.toLowerCase() == "get" || !kwArgs.multipart){ break; } var t = []; if(query.length){ var q = query.split("&"); for(var i = 0; i < q.length; ++i){ if(q[i].length){ var p = q[i].split("="); t.push( "--" + this.multipartBoundary, "Content-Disposition: form-data; name=\"" + p[0] + "\"", "", p[1]); } } } if(kwArgs.file){ if(dojo.lang.isArray(kwArgs.file)){ for(var i = 0; i < kwArgs.file.length; ++i){ var o = kwArgs.file[i]; t.push( "--" + this.multipartBoundary, "Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"", "Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"), "", o.content); } }else{ var o = kwArgs.file; t.push( "--" + this.multipartBoundary, "Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"", "Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"), "", o.content); } } if(t.length){ t.push("--"+this.multipartBoundary+"--", ""); query = t.join("\r\n"); } }while(false); // kwArgs.Connection = "close"; var async = kwArgs["sync"] ? false : true; var preventCache = kwArgs["preventCache"] || (this.preventCache == true && kwArgs["preventCache"] != false); var useCache = kwArgs["useCache"] == true || (this.useCache == true && kwArgs["useCache"] != false ); // preventCache is browser-level (add query string junk), useCache // is for the local cache. If we say preventCache, then don't attempt // to look in the cache, but if useCache is true, we still want to cache // the response if(!preventCache && useCache){ var cachedHttp = getFromCache(url, query, kwArgs.method); if(cachedHttp){ doLoad(kwArgs, cachedHttp, url, query, false); return; } } // much of this is from getText, but reproduced here because we need // more flexibility var http = dojo.hostenv.getXmlhttpObject(kwArgs); var received = false; // build a handler function that calls back to the handler obj if(async){ var startTime = // FIXME: setting up this callback handler leaks on IE!!! this.inFlight.push({ "req": kwArgs, "http": http, "url": url, "query": query, "useCache": useCache, "startTime": kwArgs.timeoutSeconds ? (new Date()).getTime() : 0 }); this.startWatchingInFlight(); }else{ // block async callbacks until sync is in, needed in khtml, others? _this._blockAsync = true; } if(kwArgs.method.toLowerCase() == "post"){ // FIXME: need to hack in more flexible Content-Type setting here! if (!kwArgs.user) { http.open("POST", url, async); }else{ http.open("POST", url, async, kwArgs.user, kwArgs.password); } setHeaders(http, kwArgs); http.setRequestHeader("Content-Type", kwArgs.multipart ? ("multipart/form-data; boundary=" + this.multipartBoundary) : (kwArgs.contentType || "application/x-www-form-urlencoded")); try{ http.send(query); }catch(e){ if(typeof http.abort == "function"){ http.abort(); } doLoad(kwArgs, {status: 404}, url, query, useCache); } }else{ var tmpUrl = url; if(query != "") { tmpUrl += (tmpUrl.indexOf("?") > -1 ? "&" : "?") + query; } if(preventCache) { tmpUrl += (dojo.string.endsWithAny(tmpUrl, "?", "&") ? "" : (tmpUrl.indexOf("?") > -1 ? "&" : "?")) + "dojo.preventCache=" + new Date().valueOf(); } if (!kwArgs.user) { http.open(kwArgs.method.toUpperCase(), tmpUrl, async); }else{ http.open(kwArgs.method.toUpperCase(), tmpUrl, async, kwArgs.user, kwArgs.password); } setHeaders(http, kwArgs); try { http.send(null); }catch(e) { if(typeof http.abort == "function"){ http.abort(); } doLoad(kwArgs, {status: 404}, url, query, useCache); } } if( !async ) { doLoad(kwArgs, http, url, query, useCache); _this._blockAsync = false; } kwArgs.abort = function(){ try{// khtml doesent reset readyState on abort, need this workaround http._aborted = true; }catch(e){/*squelsh*/} return http.abort(); } return; } dojo.io.transports.addTransport("XMLHTTPTransport"); } } dojo.provide("dojo.io.cookie"); dojo.io.cookie.setCookie = function(/*String*/name, /*String*/value, /*Number?*/days, /*String?*/path, /*String?*/domain, /*boolean?*/secure){ //summary: sets a cookie. var expires = -1; if((typeof days == "number")&&(days >= 0)){ var d = new Date(); d.setTime(d.getTime()+(days*24*60*60*1000)); expires = d.toGMTString(); } value = escape(value); document.cookie = name + "=" + value + ";" + (expires != -1 ? " expires=" + expires + ";" : "") + (path ? "path=" + path : "") + (domain ? "; domain=" + domain : "") + (secure ? "; secure" : ""); } dojo.io.cookie.set = dojo.io.cookie.setCookie; dojo.io.cookie.getCookie = function(/*String*/name){ //summary: Gets a cookie with the given name. // FIXME: Which cookie should we return? // If there are cookies set for different sub domains in the current // scope there could be more than one cookie with the same name. // I think taking the last one in the list takes the one from the // deepest subdomain, which is what we're doing here. var idx = document.cookie.lastIndexOf(name+'='); if(idx == -1) { return null; } var value = document.cookie.substring(idx+name.length+1); var end = value.indexOf(';'); if(end == -1) { end = value.length; } value = value.substring(0, end); value = unescape(value); return value; //String } dojo.io.cookie.get = dojo.io.cookie.getCookie; dojo.io.cookie.deleteCookie = function(/*String*/name){ //summary: Deletes a cookie with the given name. dojo.io.cookie.setCookie(name, "-", 0); } dojo.io.cookie.setObjectCookie = function( /*String*/name, /*Object*/obj, /*Number?*/days, /*String?*/path, /*String?*/domain, /*boolean?*/secure, /*boolean?*/clearCurrent){ //summary: Takes an object, serializes it to a cookie value, and either //sets a cookie with the serialized value. //description: If clearCurrent is true, then any current cookie value //for this object will be replaced with the the new serialized object value. //If clearCurrent is false, then the existing cookie value will be modified //with any changes from the new object value. //Objects must be simple name/value pairs where the value is either a string //or a number. Any other value will be ignored. if(arguments.length == 5){ // for backwards compat clearCurrent = domain; domain = null; secure = null; } var pairs = [], cookie, value = ""; if(!clearCurrent){ cookie = dojo.io.cookie.getObjectCookie(name); } if(days >= 0){ if(!cookie){ cookie = {}; } for(var prop in obj){ if(obj[prop] == null){ delete cookie[prop]; }else if((typeof obj[prop] == "string")||(typeof obj[prop] == "number")){ cookie[prop] = obj[prop]; } } prop = null; for(var prop in cookie){ pairs.push(escape(prop) + "=" + escape(cookie[prop])); } value = pairs.join("&"); } dojo.io.cookie.setCookie(name, value, days, path, domain, secure); } dojo.io.cookie.getObjectCookie = function(/*String*/name){ //summary: Gets an object value for the given cookie name. The complement of //dojo.io.cookie.setObjectCookie(). var values = null, cookie = dojo.io.cookie.getCookie(name); if(cookie){ values = {}; var pairs = cookie.split("&"); for(var i = 0; i < pairs.length; i++){ var pair = pairs[i].split("="); var value = pair[1]; if( isNaN(value) ){ value = unescape(pair[1]); } values[ unescape(pair[0]) ] = value; } } return values; } dojo.io.cookie.isSupported = function(){ //summary: Tests the browser to see if cookies are enabled. if(typeof navigator.cookieEnabled != "boolean"){ dojo.io.cookie.setCookie("__TestingYourBrowserForCookieSupport__", "CookiesAllowed", 90, null); var cookieVal = dojo.io.cookie.getCookie("__TestingYourBrowserForCookieSupport__"); navigator.cookieEnabled = (cookieVal == "CookiesAllowed"); if(navigator.cookieEnabled){ // FIXME: should we leave this around? this.deleteCookie("__TestingYourBrowserForCookieSupport__"); } } return navigator.cookieEnabled; //boolean } // need to leave this in for backwards-compat from 0.1 for when it gets pulled in by dojo.io.* if(!dojo.io.cookies){ dojo.io.cookies = dojo.io.cookie; } dojo.provide("dojo.lang.declare"); dojo.lang.declare = function( /*String*/ className, /*Function|Array*/ superclass, /*Function?*/ init, /*Object|Array*/ props){ /* * summary: Create a feature-rich constructor with a compact notation * className: the name of the constructor (loosely, a "class") * superclass: * may be a Function, or an Array of Functions. If "superclass" is an * array, the first element is used as the prototypical ancestor and * any following Functions become mixin ancestors. * init: an initializer function * props: * an object (or array of objects) whose properties are copied to the * created prototype * description: * Create a constructor using a compact notation for inheritance and * prototype extension. "superclass" argument may be a Function, or an * array of Functions. * * If "superclass" is an array, the first element is used as the * prototypical ancestor and any following Functions become mixin * ancestors. * * All "superclass(es)" must be Functions (not mere Objects). * * Using mixin ancestors provides a type of multiple inheritance. * Mixin ancestors prototypical properties are copied to the subclass, * and any inializater/constructor is invoked. * * Properties of object "props" are copied to the constructor * prototype. If "props" is an array, properties of each object in the * array are copied to the constructor prototype. * * name of the class ("className" argument) is stored in * "declaredClass" property * * Initializer functions are called when an object is instantiated * from this constructor. * * Aliased as "dojo.declare" * * Usage: * * dojo.declare("my.classes.bar", my.classes.foo, * function(){ * // initialization function * this.myComplicatedObject = new ReallyComplicatedObject(); * }, * { // properties to be added to the class prototype * someValue: 2, * someMethod: function(){ * doStuff(); * } * } * ); * */ if((dojo.lang.isFunction(props))||((!props)&&(!dojo.lang.isFunction(init)))){ // parameter juggling to support omitting init param (also allows // reordering init and props arguments) var temp = props; props = init; init = temp; } var mixins = [ ]; if(dojo.lang.isArray(superclass)){ mixins = superclass; superclass = mixins.shift(); } if(!init){ init = dojo.evalObjPath(className, false); if((init)&&(!dojo.lang.isFunction(init))){ init = null }; } var ctor = dojo.lang.declare._makeConstructor(); var scp = (superclass ? superclass.prototype : null); if(scp){ scp.prototyping = true; ctor.prototype = new superclass(); scp.prototyping = false; } ctor.superclass = scp; ctor.mixins = mixins; for(var i=0,l=mixins.length; i, //which will be treated as an external javascript file in IE var xscript = dojo.doc().createElement('script'); xscript.src = "javascript:'dojo.html.createExternalElement=function(doc, tag){ return doc.createElement(tag); }'"; dojo.doc().getElementsByTagName("head")[0].appendChild(xscript); })(); } }else{ //for other browsers, simply use document.createElement //is enough dojo.html.createExternalElement = function(/* HTMLDocument */doc, /* string */tag){ // summary // Creates an element in the HTML document, here for ActiveX activation workaround. return doc.createElement(tag); // HTMLElement } } dojo.html._callDeprecated = function(inFunc, replFunc, args, argName, retValue){ dojo.deprecated("dojo.html." + inFunc, "replaced by dojo.html." + replFunc + "(" + (argName ? "node, {"+ argName + ": " + argName + "}" : "" ) + ")" + (retValue ? "." + retValue : ""), "0.5"); var newArgs = []; if(argName){ var argsIn = {}; argsIn[argName] = args[1]; newArgs.push(args[0]); newArgs.push(argsIn); } else { newArgs = args } var ret = dojo.html[replFunc].apply(dojo.html, args); if(retValue){ return ret[retValue]; } else { return ret; } } dojo.html.getViewportWidth = function(){ return dojo.html._callDeprecated("getViewportWidth", "getViewport", arguments, null, "width"); } dojo.html.getViewportHeight = function(){ return dojo.html._callDeprecated("getViewportHeight", "getViewport", arguments, null, "height"); } dojo.html.getViewportSize = function(){ return dojo.html._callDeprecated("getViewportSize", "getViewport", arguments); } dojo.html.getScrollTop = function(){ return dojo.html._callDeprecated("getScrollTop", "getScroll", arguments, null, "top"); } dojo.html.getScrollLeft = function(){ return dojo.html._callDeprecated("getScrollLeft", "getScroll", arguments, null, "left"); } dojo.html.getScrollOffset = function(){ return dojo.html._callDeprecated("getScrollOffset", "getScroll", arguments, null, "offset"); } dojo.provide("dojo.uri.Uri"); dojo.uri = new function() { var authorityPattern = new RegExp("^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$"); var uriPattern = new RegExp("(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"); var cssPattern = new RegExp("/(\\w+.css)"); this.dojoUri = function (/*dojo.uri.Uri||String*/uri) { // summary: returns a Uri object resolved relative to the dojo root return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri(), uri); } this.moduleUri = function(/*String*/module, /*dojo.uri.Uri||String*/uri){ // summary: returns a Uri object relative to a module // description: Examples: dojo.uri.moduleUri("dojo.widget","templates/template.html"), or dojo.uri.moduleUri("acme","images/small.png") var loc = dojo.hostenv.getModuleSymbols(module).join('/'); if(!loc){ return null; } if(loc.lastIndexOf("/") != loc.length-1){ loc += "/"; } //If the path is an absolute path (starts with a / or is on another domain/xdomain) //then don't add the baseScriptUri. var colonIndex = loc.indexOf(":"); var slashIndex = loc.indexOf("/"); if(loc.charAt(0) != "/" && (colonIndex == -1 || colonIndex > slashIndex)){ loc = dojo.hostenv.getBaseScriptUri() + loc; } return new dojo.uri.Uri(loc,uri); } this.Uri = function (/*dojo.uri.Uri||String...*/) { // summary: Constructor to create an object representing a URI. // description: // Each argument is evaluated in order relative to the next until // a canonical uri is produced. To get an absolute Uri relative // to the current document use // new dojo.uri.Uri(document.baseURI, uri) // TODO: support for IPv6, see RFC 2732 // resolve uri components relative to each other var uri = arguments[0]; if (uri && arguments.length > 1) { var cssMatch = cssPattern.exec(uri); if (cssMatch){ uri = uri.toString().replace(cssMatch[1], ""); } } for (var i = 1; i < arguments.length; i++) { if(!arguments[i]) { continue; } // Safari doesn't support this.constructor so we have to be explicit var relobj = new dojo.uri.Uri(arguments[i].toString()); var uriobj = new dojo.uri.Uri(uri.toString()); if ((relobj.path=="")&&(relobj.scheme==null)&&(relobj.authority==null)&&(relobj.query==null)) { if (relobj.fragment != null) { uriobj.fragment = relobj.fragment; } relobj = uriobj; } if (relobj.scheme != null && relobj.authority != null) uri = ""; if (relobj.scheme != null) { uri += relobj.scheme + ":"; } if (relobj.authority != null) { uri += "//" + relobj.authority; } uri += relobj.path; if (relobj.query != null) { uri += "?" + relobj.query; } if (relobj.fragment != null) { uri += "#" + relobj.fragment; } } this.uri = uri.toString(); // break the uri into its main components var r = this.uri.match(uriPattern); this.scheme = r[2] || (r[1] ? "" : null); this.authority = r[4] || (r[3] ? "" : null); this.path = r[5]; // can never be undefined this.query = r[7] || (r[6] ? "" : null); this.fragment = r[9] || (r[8] ? "" : null); if (this.authority != null) { // server based naming authority r = this.authority.match(authorityPattern); this.user = r[3] || null; this.password = r[4] || null; this.host = r[5]; this.port = r[7] || null; } this.toString = function(){ return this.uri; } } }; dojo.provide("dojo.html.style"); dojo.html.getClass = function(/* HTMLElement */node){ // summary // Returns the string value of the list of CSS classes currently assigned directly // to the node in question. Returns an empty string if no class attribute is found; node = dojo.byId(node); if(!node){ return ""; } var cs = ""; if(node.className){ cs = node.className; }else if(dojo.html.hasAttribute(node, "class")){ cs = dojo.html.getAttribute(node, "class"); } return cs.replace(/^\s+|\s+$/g, ""); // string } dojo.html.getClasses = function(/* HTMLElement */node) { // summary // Returns an array of CSS classes currently assigned directly to the node in question. // Returns an empty array if no classes are found; var c = dojo.html.getClass(node); return (c == "") ? [] : c.split(/\s+/g); // array } dojo.html.hasClass = function(/* HTMLElement */node, /* string */classname){ // summary // Returns whether or not the specified classname is a portion of the // class list currently applied to the node. Does not cover cascaded // styles, only classes directly applied to the node. return (new RegExp('(^|\\s+)'+classname+'(\\s+|$)')).test(dojo.html.getClass(node)) // boolean } dojo.html.prependClass = function(/* HTMLElement */node, /* string */classStr){ // summary // Adds the specified class to the beginning of the class list on the // passed node. This gives the specified class the highest precidence // when style cascading is calculated for the node. Returns true or // false; indicating success or failure of the operation, respectively. classStr += " " + dojo.html.getClass(node); return dojo.html.setClass(node, classStr); // boolean } dojo.html.addClass = function(/* HTMLElement */node, /* string */classStr){ // summary // Adds the specified class to the end of the class list on the // passed &node;. Returns &true; or &false; indicating success or failure. if (dojo.html.hasClass(node, classStr)) { return false; } classStr = (dojo.html.getClass(node) + " " + classStr).replace(/^\s+|\s+$/g,""); return dojo.html.setClass(node, classStr); // boolean } dojo.html.setClass = function(/* HTMLElement */node, /* string */classStr){ // summary // Clobbers the existing list of classes for the node, replacing it with // the list given in the 2nd argument. Returns true or false // indicating success or failure. node = dojo.byId(node); var cs = new String(classStr); try{ if(typeof node.className == "string"){ node.className = cs; }else if(node.setAttribute){ node.setAttribute("class", classStr); node.className = cs; }else{ return false; } }catch(e){ dojo.debug("dojo.html.setClass() failed", e); } return true; } dojo.html.removeClass = function(/* HTMLElement */node, /* string */classStr, /* boolean? */allowPartialMatches){ // summary // Removes the className from the node;. Returns true or false indicating success or failure. try{ if (!allowPartialMatches) { var newcs = dojo.html.getClass(node).replace(new RegExp('(^|\\s+)'+classStr+'(\\s+|$)'), "$1$2"); } else { var newcs = dojo.html.getClass(node).replace(classStr,''); } dojo.html.setClass(node, newcs); }catch(e){ dojo.debug("dojo.html.removeClass() failed", e); } return true; // boolean } dojo.html.replaceClass = function(/* HTMLElement */node, /* string */newClass, /* string */oldClass) { // summary // Replaces 'oldClass' and adds 'newClass' to node dojo.html.removeClass(node, oldClass); dojo.html.addClass(node, newClass); } // Enum type for getElementsByClass classMatchType arg: dojo.html.classMatchType = { ContainsAll : 0, // all of the classes are part of the node's class (default) ContainsAny : 1, // any of the classes are part of the node's class IsOnly : 2 // only all of the classes are part of the node's class } dojo.html.getElementsByClass = function( /* string */classStr, /* HTMLElement? */parent, /* string? */nodeType, /* integer? */classMatchType, /* boolean? */useNonXpath ){ // summary // Returns an array of nodes for the given classStr, children of a // parent, and optionally of a certain nodeType // FIXME: temporarily set to false because of several dojo tickets related // to the xpath version not working consistently in firefox. useNonXpath = false; var _document = dojo.doc(); parent = dojo.byId(parent) || _document; var classes = classStr.split(/\s+/g); var nodes = []; if( classMatchType != 1 && classMatchType != 2 ) classMatchType = 0; // make it enum var reClass = new RegExp("(\\s|^)((" + classes.join(")|(") + "))(\\s|$)"); var srtLength = classes.join(" ").length; var candidateNodes = []; if(!useNonXpath && _document.evaluate) { // supports dom 3 xpath var xpath = ".//" + (nodeType || "*") + "[contains("; if(classMatchType != dojo.html.classMatchType.ContainsAny){ xpath += "concat(' ',@class,' '), ' " + classes.join(" ') and contains(concat(' ',@class,' '), ' ") + " ')"; if (classMatchType == 2) { xpath += " and string-length(@class)="+srtLength+"]"; }else{ xpath += "]"; } }else{ xpath += "concat(' ',@class,' '), ' " + classes.join(" ') or contains(concat(' ',@class,' '), ' ") + " ')]"; } var xpathResult = _document.evaluate(xpath, parent, null, XPathResult.ANY_TYPE, null); var result = xpathResult.iterateNext(); while(result){ try{ candidateNodes.push(result); result = xpathResult.iterateNext(); }catch(e){ break; } } return candidateNodes; // NodeList }else{ if(!nodeType){ nodeType = "*"; } candidateNodes = parent.getElementsByTagName(nodeType); var node, i = 0; outer: while(node = candidateNodes[i++]){ var nodeClasses = dojo.html.getClasses(node); if(nodeClasses.length == 0){ continue outer; } var matches = 0; for(var j = 0; j < nodeClasses.length; j++){ if(reClass.test(nodeClasses[j])){ if(classMatchType == dojo.html.classMatchType.ContainsAny){ nodes.push(node); continue outer; }else{ matches++; } }else{ if(classMatchType == dojo.html.classMatchType.IsOnly){ continue outer; } } } if(matches == classes.length){ if( (classMatchType == dojo.html.classMatchType.IsOnly)&& (matches == nodeClasses.length)){ nodes.push(node); }else if(classMatchType == dojo.html.classMatchType.ContainsAll){ nodes.push(node); } } } return nodes; // NodeList } } dojo.html.getElementsByClassName = dojo.html.getElementsByClass; dojo.html.toCamelCase = function(/* string */selector){ // summary // Translates a CSS selector string to a camel-cased one. var arr = selector.split('-'), cc = arr[0]; for(var i = 1; i < arr.length; i++) { cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1); } return cc; // string } dojo.html.toSelectorCase = function(/* string */selector){ // summary // Translates a camel cased string to a selector cased one. return selector.replace(/([A-Z])/g, "-$1" ).toLowerCase(); // string } if (dojo.render.html.ie) { // IE branch dojo.html.getComputedStyle = function(/*HTMLElement|String*/node, /*String*/property, /*String*/value) { // summary // Get the computed style value for style "property" on "node" (IE). node = dojo.byId(node); // FIXME: remove ability to access nodes by id for this time-critical function if(!node || !node.currentStyle){return value;} // FIXME: standardize on camel-case input to improve speed return node.currentStyle[dojo.html.toCamelCase(property)]; // String } // SJM: getComputedStyle should be abandoned and replaced with the below function. // All our supported browsers can return CSS2 compliant CssStyleDeclaration objects // which can be queried directly for multiple styles. dojo.html.getComputedStyles = function(/*HTMLElement*/node) { // summary // Get a style object containing computed styles for HTML Element node (IE). return node.currentStyle; // CSSStyleDeclaration } } else { // non-IE branch dojo.html.getComputedStyle = function(/*HTMLElement|String*/node, /*String*/property, /*Any*/value) { // summary // Get the computed style value for style "property" on "node" (non-IE). node = dojo.byId(node); if(!node || !node.style){return value;} var s = document.defaultView.getComputedStyle(node, null); // s may be null on Safari return (s&&s[dojo.html.toCamelCase(property)])||''; // String } // SJM: getComputedStyle should be abandoned and replaced with the below function. // All our supported browsers can return CSS2 compliant CssStyleDeclaration objects // which can be queried directly for multiple styles. dojo.html.getComputedStyles = function(node) { // summary // Get a style object containing computed styles for HTML Element node (non-IE). return document.defaultView.getComputedStyle(node, null); // CSSStyleDeclaration } } dojo.html.getStyleProperty = function(/* HTMLElement */node, /* string */cssSelector){ // summary // Returns the value of the passed style node = dojo.byId(node); return (node && node.style ? node.style[dojo.html.toCamelCase(cssSelector)] : undefined); // string } dojo.html.getStyle = function(/* HTMLElement */node, /* string */cssSelector){ // summary // Returns the computed value of the passed style var value = dojo.html.getStyleProperty(node, cssSelector); return (value ? value : dojo.html.getComputedStyle(node, cssSelector)); // string || integer } dojo.html.setStyle = function(/* HTMLElement */node, /* string */cssSelector, /* string */value){ // summary // Set the value of passed style on node node = dojo.byId(node); if(node && node.style){ var camelCased = dojo.html.toCamelCase(cssSelector); node.style[camelCased] = value; } } dojo.html.setStyleText = function (/* HTMLElement */target, /* string */text) { // summary // Try to set the entire cssText property of the passed target; equiv of setting style attribute. try { target.style.cssText = text; } catch (e) { target.setAttribute("style", text); } } dojo.html.copyStyle = function(/* HTMLElement */target, /* HTMLElement */source){ // summary // work around for opera which doesn't have cssText, and for IE which fails on setAttribute if(!source.style.cssText){ target.setAttribute("style", source.getAttribute("style")); }else{ target.style.cssText = source.style.cssText; } dojo.html.addClass(target, dojo.html.getClass(source)); } dojo.html.getUnitValue = function(/* HTMLElement */node, /* string */cssSelector, /* boolean? */autoIsZero){ // summary // Get the value of passed selector, with the specific units used var s = dojo.html.getComputedStyle(node, cssSelector); if((!s)||((s == 'auto')&&(autoIsZero))){ return { value: 0, units: 'px' }; // object } // FIXME: is regex inefficient vs. parseInt or some manual test? var match = s.match(/(\-?[\d.]+)([a-z%]*)/i); if (!match){return dojo.html.getUnitValue.bad;} return { value: Number(match[1]), units: match[2].toLowerCase() }; // object } dojo.html.getUnitValue.bad = { value: NaN, units: '' }; if (dojo.render.html.ie) { // IE branch dojo.html.toPixelValue = function(/* HTMLElement */element, /* String */styleValue){ // summary // Extract value in pixels from styleValue (IE version). // If a value cannot be extracted, zero is returned. if(!styleValue){return 0;} if(styleValue.slice(-2) == 'px'){return parseFloat(styleValue);} var pixelValue = 0; with(element){ var sLeft = style.left; var rsLeft = runtimeStyle.left; runtimeStyle.left = currentStyle.left; try { style.left = styleValue || 0; pixelValue = style.pixelLeft; style.left = sLeft; runtimeStyle.left = rsLeft; }catch(e){ // FIXME: it's possible for styleValue to be incompatible with // style.left. In particular, border width values of // "thick", "medium", or "thin" will provoke an exception. } } return pixelValue; // Number } } else { // non-IE branch dojo.html.toPixelValue = function(/* HTMLElement */element, /* String */styleValue){ // summary // Extract value in pixels from styleValue (non-IE version). // If a value cannot be extracted, zero is returned. return (styleValue && (styleValue.slice(-2)=='px') ? parseFloat(styleValue) : 0); // Number } } dojo.html.getPixelValue = function(/* HTMLElement */node, /* string */styleProperty, /* boolean? */autoIsZero){ // summary // Get a computed style value, in pixels. // node: HTMLElement // Node to interrogate // styleProperty: String // Style property to query, in either css-selector or camelCase (property) format. // autoIsZero: Boolean // Deprecated. Any value that cannot be converted to pixels is returned as zero. // // summary // Get the value of passed selector in pixels. // return dojo.html.toPixelValue(node, dojo.html.getComputedStyle(node, styleProperty)); } dojo.html.setPositivePixelValue = function(/* HTMLElement */node, /* string */selector, /* integer */value){ // summary // Attempt to set the value of selector on node as a positive pixel value. if(isNaN(value)){return false;} node.style[selector] = Math.max(0, value) + 'px'; return true; // boolean } dojo.html.styleSheet = null; // FIXME: this is a really basic stub for adding and removing cssRules, but // it assumes that you know the index of the cssRule that you want to add // or remove, making it less than useful. So we need something that can // search for the selector that you you want to remove. dojo.html.insertCssRule = function(/* string */selector, /* string */declaration, /* integer? */index) { // summary // Attempt to insert declaration as selector on the internal stylesheet; if index try to set it there. if (!dojo.html.styleSheet) { if (document.createStyleSheet) { // IE dojo.html.styleSheet = document.createStyleSheet(); } else if (document.styleSheets[0]) { // rest // FIXME: should create a new style sheet here // fall back on an exsiting style sheet dojo.html.styleSheet = document.styleSheets[0]; } else { return null; // integer } // fail } if (arguments.length < 3) { // index may == 0 if (dojo.html.styleSheet.cssRules) { // W3 index = dojo.html.styleSheet.cssRules.length; } else if (dojo.html.styleSheet.rules) { // IE index = dojo.html.styleSheet.rules.length; } else { return null; // integer } // fail } if (dojo.html.styleSheet.insertRule) { // W3 var rule = selector + " { " + declaration + " }"; return dojo.html.styleSheet.insertRule(rule, index); // integer } else if (dojo.html.styleSheet.addRule) { // IE return dojo.html.styleSheet.addRule(selector, declaration, index); // integer } else { return null; // integer } // fail } dojo.html.removeCssRule = function(/* integer? */index){ // summary // Attempt to remove the rule at index. if(!dojo.html.styleSheet){ dojo.debug("no stylesheet defined for removing rules"); return false; } if(dojo.render.html.ie){ if(!index){ index = dojo.html.styleSheet.rules.length; dojo.html.styleSheet.removeRule(index); } }else if(document.styleSheets[0]){ if(!index){ index = dojo.html.styleSheet.cssRules.length; } dojo.html.styleSheet.deleteRule(index); } return true; // boolean } dojo.html._insertedCssFiles = []; // cache container needed because IE reformats cssText when added to DOM dojo.html.insertCssFile = function(/* string */URI, /* HTMLDocument? */doc, /* boolean? */checkDuplicates, /* boolean */fail_ok){ // summary // calls css by XmlHTTP and inserts it into DOM as if(!URI){ return; } if(!doc){ doc = document; } var cssStr = dojo.hostenv.getText(URI, false, fail_ok); if(cssStr===null){ return; } cssStr = dojo.html.fixPathsInCssText(cssStr, URI); if(checkDuplicates){ var idx = -1, node, ent = dojo.html._insertedCssFiles; for(var i = 0; i < ent.length; i++){ if((ent[i].doc == doc) && (ent[i].cssText == cssStr)){ idx = i; node = ent[i].nodeRef; break; } } // make sure we havent deleted our node if(node){ var styles = doc.getElementsByTagName("style"); for(var i = 0; i < styles.length; i++){ if(styles[i] == node){ return; } } // delete this entry dojo.html._insertedCssFiles.shift(idx, 1); } } var style = dojo.html.insertCssText(cssStr, doc); dojo.html._insertedCssFiles.push({'doc': doc, 'cssText': cssStr, 'nodeRef': style}); // insert custom attribute ex dbgHref="../foo.css" usefull when debugging in DOM inspectors, no? if(style && djConfig.isDebug){ style.setAttribute("dbgHref", URI); } return style; // HTMLStyleElement } dojo.html.insertCssText = function(/* string */cssStr, /* HTMLDocument? */doc, /* string? */URI){ // summary // Attempt to insert CSS rules into the document through inserting a style element // DomNode Style = insertCssText(String ".dojoMenu {color: green;}"[, DomDoc document, dojo.uri.Uri Url ]) if(!cssStr){ return; // HTMLStyleElement } if(!doc){ doc = document; } if(URI){// fix paths in cssStr cssStr = dojo.html.fixPathsInCssText(cssStr, URI); } var style = doc.createElement("style"); style.setAttribute("type", "text/css"); // IE is b0rken enough to require that we add the element to the doc // before changing it's properties var head = doc.getElementsByTagName("head")[0]; if(!head){ // must have a head tag dojo.debug("No head tag in document, aborting styles"); return; // HTMLStyleElement }else{ head.appendChild(style); } if(style.styleSheet){// IE var setFunc = function(){ try{ style.styleSheet.cssText = cssStr; }catch(e){ dojo.debug(e); } }; if(style.styleSheet.disabled){ setTimeout(setFunc, 10); }else{ setFunc(); } }else{ // w3c var cssText = doc.createTextNode(cssStr); style.appendChild(cssText); } return style; // HTMLStyleElement } dojo.html.fixPathsInCssText = function(/* string */cssStr, /* string */URI){ // summary // usage: cssText comes from dojoroot/src/widget/templates/Foobar.css // it has .dojoFoo { background-image: url(images/bar.png);} then uri should point to dojoroot/src/widget/templates/ if(!cssStr || !URI){ return; } var match, str = "", url = "", urlChrs = "[\\t\\s\\w\\(\\)\\/\\.\\\\'\"-:#=&?~]+"; var regex = new RegExp('url\\(\\s*('+urlChrs+')\\s*\\)'); var regexProtocol = /(file|https?|ftps?):\/\//; regexTrim = new RegExp("^[\\s]*(['\"]?)("+urlChrs+")\\1[\\s]*?$"); if(dojo.render.html.ie55 || dojo.render.html.ie60){ var regexIe = new RegExp("AlphaImageLoader\\((.*)src\=['\"]("+urlChrs+")['\"]"); // TODO: need to decide how to handle relative paths and AlphaImageLoader see #1441 // current implementation breaks on build with intern_strings while(match = regexIe.exec(cssStr)){ url = match[2].replace(regexTrim, "$2"); if(!regexProtocol.exec(url)){ url = (new dojo.uri.Uri(URI, url).toString()); } str += cssStr.substring(0, match.index) + "AlphaImageLoader(" + match[1] + "src='" + url + "'"; cssStr = cssStr.substr(match.index + match[0].length); } cssStr = str + cssStr; str = ""; } while(match = regex.exec(cssStr)){ url = match[1].replace(regexTrim, "$2"); if(!regexProtocol.exec(url)){ url = (new dojo.uri.Uri(URI, url).toString()); } str += cssStr.substring(0, match.index) + "url(" + url + ")"; cssStr = cssStr.substr(match.index + match[0].length); } return str + cssStr; // string } dojo.html.setActiveStyleSheet = function(/* string */title){ // summary // Activate style sheet with specified title. var i = 0, a, els = dojo.doc().getElementsByTagName("link"); while (a = els[i++]) { if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")){ a.disabled = true; if (a.getAttribute("title") == title) { a.disabled = false; } } } } dojo.html.getActiveStyleSheet = function(){ // summary // return the title of the currently active stylesheet var i = 0, a, els = dojo.doc().getElementsByTagName("link"); while (a = els[i++]) { if (a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title") && !a.disabled ){ return a.getAttribute("title"); // string } } return null; // string } dojo.html.getPreferredStyleSheet = function(){ // summary // Return the preferred stylesheet title (i.e. link without alt attribute) var i = 0, a, els = dojo.doc().getElementsByTagName("link"); while (a = els[i++]) { if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("rel").indexOf("alt") == -1 && a.getAttribute("title") ){ return a.getAttribute("title"); // string } } return null; // string } dojo.html.applyBrowserClass = function(/* HTMLElement */node){ // summary // Applies pre-set class names based on browser & version to the passed node. // Modified version of Morris' CSS hack. var drh=dojo.render.html; var classes = { dj_ie: drh.ie, dj_ie55: drh.ie55, dj_ie6: drh.ie60, dj_ie7: drh.ie70, dj_iequirks: drh.ie && drh.quirks, dj_opera: drh.opera, dj_opera8: drh.opera && (Math.floor(dojo.render.version)==8), dj_opera9: drh.opera && (Math.floor(dojo.render.version)==9), dj_khtml: drh.khtml, dj_safari: drh.safari, dj_gecko: drh.mozilla }; // no dojo unsupported browsers for(var p in classes){ if(classes[p]){ dojo.html.addClass(node, p); } } }; dojo.kwCompoundRequire({ common: [ "dojo.html.common", "dojo.html.style" ] }); dojo.provide("dojo.html.*"); dojo.provide("dojo.html.display"); dojo.html._toggle = function(node, tester, setter){ node = dojo.byId(node); setter(node, !tester(node)); return tester(node); } dojo.html.show = function(/* HTMLElement */node){ // summary // Show the passed element by reverting display property set by dojo.html.hide node = dojo.byId(node); if(dojo.html.getStyleProperty(node, 'display')=='none'){ dojo.html.setStyle(node, 'display', (node.dojoDisplayCache||'')); node.dojoDisplayCache = undefined; // cannot use delete on a node in IE6 } } dojo.html.hide = function(/* HTMLElement */node){ // summary // Hide the passed element by setting display:none node = dojo.byId(node); if(typeof node["dojoDisplayCache"] == "undefined"){ // it could == '', so we cannot say !node.dojoDisplayCount var d = dojo.html.getStyleProperty(node, 'display') if(d!='none'){ node.dojoDisplayCache = d; } } dojo.html.setStyle(node, 'display', 'none'); } dojo.html.setShowing = function(/* HTMLElement */node, /* boolean? */showing){ // summary // Calls show() if showing is true, hide() otherwise dojo.html[(showing ? 'show' : 'hide')](node); } dojo.html.isShowing = function(/* HTMLElement */node){ // summary // Returns whether the element is displayed or not. // FIXME: returns true if node is bad, isHidden would be easier to make correct return (dojo.html.getStyleProperty(node, 'display') != 'none'); // boolean } dojo.html.toggleShowing = function(/* HTMLElement */node){ // summary // Call setShowing() on node with the complement of isShowing(), then return the new value of isShowing() return dojo.html._toggle(node, dojo.html.isShowing, dojo.html.setShowing); // boolean } // Simple mapping of tag names to display values // FIXME: simplistic dojo.html.displayMap = { tr: '', td: '', th: '', img: 'inline', span: 'inline', input: 'inline', button: 'inline' }; dojo.html.suggestDisplayByTagName = function(/* HTMLElement */node){ // summary // Suggest a value for the display property that will show 'node' based on it's tag node = dojo.byId(node); if(node && node.tagName){ var tag = node.tagName.toLowerCase(); return (tag in dojo.html.displayMap ? dojo.html.displayMap[tag] : 'block'); // string } } dojo.html.setDisplay = function(/* HTMLElement */node, /* string */display){ // summary // Sets the value of style.display to value of 'display' parameter if it is a string. // Otherwise, if 'display' is false, set style.display to 'none'. // Finally, set 'display' to a suggested display value based on the node's tag dojo.html.setStyle(node, 'display', ((display instanceof String || typeof display == "string") ? display : (display ? dojo.html.suggestDisplayByTagName(node) : 'none'))); } dojo.html.isDisplayed = function(/* HTMLElement */node){ // summary // Is true if the the computed display style for node is not 'none' // FIXME: returns true if node is bad, isNotDisplayed would be easier to make correct return (dojo.html.getComputedStyle(node, 'display') != 'none'); // boolean } dojo.html.toggleDisplay = function(/* HTMLElement */node){ // summary // Call setDisplay() on node with the complement of isDisplayed(), then // return the new value of isDisplayed() return dojo.html._toggle(node, dojo.html.isDisplayed, dojo.html.setDisplay); // boolean } dojo.html.setVisibility = function(/* HTMLElement */node, /* string */visibility){ // summary // Sets the value of style.visibility to value of 'visibility' parameter if it is a string. // Otherwise, if 'visibility' is false, set style.visibility to 'hidden'. Finally, set style.visibility to 'visible'. dojo.html.setStyle(node, 'visibility', ((visibility instanceof String || typeof visibility == "string") ? visibility : (visibility ? 'visible' : 'hidden'))); } dojo.html.isVisible = function(/* HTMLElement */node){ // summary // Returns true if the the computed visibility style for node is not 'hidden' // FIXME: returns true if node is bad, isInvisible would be easier to make correct return (dojo.html.getComputedStyle(node, 'visibility') != 'hidden'); // boolean } dojo.html.toggleVisibility = function(node){ // summary // Call setVisibility() on node with the complement of isVisible(), then return the new value of isVisible() return dojo.html._toggle(node, dojo.html.isVisible, dojo.html.setVisibility); // boolean } dojo.html.setOpacity = function(/* HTMLElement */node, /* float */opacity, /* boolean? */dontFixOpacity){ // summary // Sets the opacity of node in a cross-browser way. // float between 0.0 (transparent) and 1.0 (opaque) node = dojo.byId(node); var h = dojo.render.html; if(!dontFixOpacity){ if( opacity >= 1.0){ if(h.ie){ dojo.html.clearOpacity(node); return; }else{ opacity = 0.999999; } }else if( opacity < 0.0){ opacity = 0; } } if(h.ie){ if(node.nodeName.toLowerCase() == "tr"){ // FIXME: is this too naive? will we get more than we want? var tds = node.getElementsByTagName("td"); for(var x=0; x= 0.999999 ? 1.0 : Number(opac); // float } dojo.provide("dojo.ns"); dojo.ns = { // summary: private object that implements widget namespace management namespaces: {}, failed: {}, loading: {}, loaded: {}, register: function(/*String*/name, /*String*/module, /*Function?*/resolver, /*Boolean?*/noOverride){ // summary: creates and registers a dojo.ns.Ns object if(!noOverride || !this.namespaces[name]){ this.namespaces[name] = new dojo.ns.Ns(name, module, resolver); } }, allow: function(/*String*/name){ // summary: Returns false if 'name' is filtered by configuration or has failed to load, true otherwise if(this.failed[name]){return false;} // Boolean if((djConfig.excludeNamespace)&&(dojo.lang.inArray(djConfig.excludeNamespace, name))){return false;} // Boolean // If the namespace is "dojo", or the user has not specified allowed namespaces return true. // Otherwise, if the user has specifically allowed this namespace, return true, otherwise false. return((name==this.dojo)||(!djConfig.includeNamespace)||(dojo.lang.inArray(djConfig.includeNamespace, name))); // Boolean }, get: function(/*String*/name){ // summary // Return Ns object registered to 'name', if any return this.namespaces[name]; // Ns }, require: function(/*String*/name){ // summary // Try to ensure that 'name' is registered, loading a namespace manifest if necessary var ns = this.namespaces[name]; if((ns)&&(this.loaded[name])){return ns;} // Ns if(!this.allow(name)){return false;} // Boolean if(this.loading[name]){ // FIXME: do we really ever have re-entrancy situation? this would appear to be really bad // original code did not throw an exception, although that seems the only course // adding debug output here to track if this occurs. dojo.debug('dojo.namespace.require: re-entrant request to load namespace "' + name + '" must fail.'); return false; // Boolean } // workaround so we don't break the build system var req = dojo.require; this.loading[name] = true; try { //dojo namespace file is always in the Dojo namespaces folder, not any custom folder if(name=="dojo"){ req("dojo.namespaces.dojo"); }else{ // if no registered module prefix, use ../ by convention if(!dojo.hostenv.moduleHasPrefix(name)){ dojo.registerModulePath(name, "../" + name); } req([name, 'manifest'].join('.'), false, true); } if(!this.namespaces[name]){ this.failed[name] = true; //only look for a namespace once } }finally{ this.loading[name]=false; } return this.namespaces[name]; // Ns } } dojo.ns.Ns = function(/*String*/name, /*String*/module, /*Function?*/resolver){ // summary: this object simply encapsulates namespace data this.name = name; this.module = module; this.resolver = resolver; this._loaded = [ ]; this._failed = [ ]; } dojo.ns.Ns.prototype.resolve = function(/*String*/name, /*String*/domain, /*Boolean?*/omitModuleCheck){ //summary: map component with 'name' and 'domain' to a module via namespace resolver, if specified if(!this.resolver || djConfig["skipAutoRequire"]){return false;} // Boolean var fullName = this.resolver(name, domain); //only load a widget once. This is a quicker check than dojo.require does if((fullName)&&(!this._loaded[fullName])&&(!this._failed[fullName])){ //workaround so we don't break the build system var req = dojo.require; req(fullName, false, true); //omit the module check, we'll do it ourselves. if(dojo.hostenv.findModule(fullName, false)){ this._loaded[fullName] = true; }else{ if(!omitModuleCheck){dojo.raise("dojo.ns.Ns.resolve: module '" + fullName + "' not found after loading via namespace '" + this.name + "'");} this._failed[fullName] = true; } } return Boolean(this._loaded[fullName]); // Boolean } dojo.registerNamespace = function(/*String*/name, /*String*/module, /*Function?*/resolver){ // summary: maps a module name to a namespace for widgets, and optionally maps widget names to modules for auto-loading // description: An unregistered namespace is mapped to an eponymous module. // For example, namespace acme is mapped to module acme, and widgets are // assumed to belong to acme.widget. If you want to use a different widget // module, use dojo.registerNamespace. dojo.ns.register.apply(dojo.ns, arguments); } dojo.registerNamespaceResolver = function(/*String*/name, /*Function*/resolver){ // summary: a resolver function maps widget names to modules, so the // widget manager can auto-load needed widget implementations // // description: The resolver provides information to allow Dojo // to load widget modules on demand. When a widget is created, // a namespace resolver can tell Dojo what module to require // to ensure that the widget implementation code is loaded. // // name: will always be lower-case. // // example: // dojo.registerNamespaceResolver("acme", // function(name){ // return "acme.widget."+dojo.string.capitalize(name); // } // ); var n = dojo.ns.namespaces[name]; if(n){ n.resolver = resolver; } } dojo.registerNamespaceManifest = function(/*String*/module, /*String*/path, /*String*/name, /*String*/widgetModule, /*Function?*/resolver){ // summary: convenience function to register a module path, a namespace, and optionally a resolver all at once. dojo.registerModulePath(name, path); dojo.registerNamespace(name, widgetModule, resolver); } // NOTE: rather put this in dojo.widget.Widget, but that fubars debugAtAllCosts dojo.registerNamespace("dojo", "dojo.widget"); dojo.provide("dojo.event.topic"); dojo.event.topic = new function(){ this.topics = {}; this.getTopic = function(/*String*/topic){ // summary: // returns a topic implementation object of type // dojo.event.topic.TopicImpl // topic: // a unique, opaque string that names the topic if(!this.topics[topic]){ this.topics[topic] = new this.TopicImpl(topic); } return this.topics[topic]; // a dojo.event.topic.TopicImpl object } this.registerPublisher = function(/*String*/topic, /*Object*/obj, /*String*/funcName){ // summary: // registers a function as a publisher on a topic. Subsequent // calls to the function will cause a publish event on the topic // with the arguments passed to the function passed to registered // listeners. // topic: // a unique, opaque string that names the topic // obj: // the scope to locate the function in // funcName: // the name of the function to register var topic = this.getTopic(topic); topic.registerPublisher(obj, funcName); } this.subscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){ // summary: // susbscribes the function to the topic. Subsequent events // dispached to the topic will create a function call for the // obj.funcName() function. // topic: // a unique, opaque string that names the topic // obj: // the scope to locate the function in // funcName: // the name of the function to being registered as a listener var topic = this.getTopic(topic); topic.subscribe(obj, funcName); } this.unsubscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){ // summary: // unsubscribes the obj.funcName() from the topic // topic: // a unique, opaque string that names the topic // obj: // the scope to locate the function in // funcName: // the name of the function to being unregistered as a listener var topic = this.getTopic(topic); topic.unsubscribe(obj, funcName); } this.destroy = function(/*String*/topic){ // summary: // destroys the topic and unregisters all listeners // topic: // a unique, opaque string that names the topic this.getTopic(topic).destroy(); delete this.topics[topic]; } this.publishApply = function(/*String*/topic, /*Array*/args){ // summary: // dispatches an event to the topic using the args array as the // source for the call arguments to each listener. This is similar // to JavaScript's built-in Function.apply() // topic: // a unique, opaque string that names the topic // args: // the arguments to be passed into listeners of the topic var topic = this.getTopic(topic); topic.sendMessage.apply(topic, args); } this.publish = function(/*String*/topic, /*Object*/message){ // summary: // manually "publish" to the passed topic // topic: // a unique, opaque string that names the topic // message: // can be an array of parameters (similar to publishApply), or // will be treated as one of many arguments to be passed along in // a "flat" unrolling var topic = this.getTopic(topic); // if message is an array, we treat it as a set of arguments, // otherwise, we just pass on the arguments passed in as-is var args = []; // could we use concat instead here? for(var x=1; xwidget for widgets without parents (top level widgets) this.topWidgets = {}; var widgetTypeCtr = {}; var renderPrefixCache = []; this.getUniqueId = function (widgetType) { var widgetId; do{ widgetId = widgetType + "_" + (widgetTypeCtr[widgetType] != undefined ? ++widgetTypeCtr[widgetType] : widgetTypeCtr[widgetType] = 0); }while(this.getWidgetById(widgetId)); return widgetId; } this.add = function(widget){ //dojo.profile.start("dojo.widget.manager.add"); this.widgets.push(widget); // Opera9 uses ID (caps) if(!widget.extraArgs["id"]){ widget.extraArgs["id"] = widget.extraArgs["ID"]; } // FIXME: the rest of this method is very slow! if(widget.widgetId == ""){ if(widget["id"]){ widget.widgetId = widget["id"]; }else if(widget.extraArgs["id"]){ widget.widgetId = widget.extraArgs["id"]; }else{ widget.widgetId = this.getUniqueId(widget.ns+'_'+widget.widgetType); } } if(this.widgetIds[widget.widgetId]){ dojo.debug("widget ID collision on ID: "+widget.widgetId); } this.widgetIds[widget.widgetId] = widget; // Widget.destroy already calls removeById(), so we don't need to // connect() it here //dojo.profile.end("dojo.widget.manager.add"); } this.destroyAll = function(){ for(var x=this.widgets.length-1; x>=0; x--){ try{ // this.widgets[x].destroyChildren(); this.widgets[x].destroy(true); delete this.widgets[x]; }catch(e){ } } } // FIXME: we should never allow removal of the root widget until all others // are removed! this.remove = function(widgetIndex){ if(dojo.lang.isNumber(widgetIndex)){ var tw = this.widgets[widgetIndex].widgetId; delete this.topWidgets[tw]; delete this.widgetIds[tw]; this.widgets.splice(widgetIndex, 1); }else{ this.removeById(widgetIndex); } } // FIXME: suboptimal performance this.removeById = function(id) { if(!dojo.lang.isString(id)){ id = id["widgetId"]; if(!id){ dojo.debug("invalid widget or id passed to removeById"); return; } } for (var i=0; i.widget by convention dojo.ns.register(ns, ns + '.widget'); nsObj = dojo.ns.get(ns); } // allow the namespace to resolve the widget module if(nsObj){nsObj.resolve(widgetName);} // locate a widget implementation in the registered module for our current rendering environment impl = findImplementation(lowerCaseWidgetName, nsObj.module); if(impl){return(imps[lowerCaseWidgetName] = impl)}; // try to load a manifest to resolve this implemenation nsObj = dojo.ns.require(ns); if((nsObj)&&(nsObj.resolver)){ nsObj.resolve(widgetName); impl = findImplementation(lowerCaseWidgetName, nsObj.module); if(impl){return(imps[lowerCaseWidgetName] = impl)}; } // this is an error condition under new rules dojo.deprecated('dojo.widget.Manager.getImplementationName', 'Could not locate widget implementation for "' + widgetName + '" in "' + nsObj.module + '" registered to namespace "' + nsObj.name + '". ' + "Developers must specify correct namespaces for all non-Dojo widgets", "0.5"); // backward compat: if the user has not specified any namespace and their widget is not in dojo.widget.* // search registered widget packages [sic] // note: registerWidgetPackage itself is now deprecated for(var i=0; i 0) { return widgets[n]; } return widgets; } g("registerWidgetPackage"); g("getImplementation", "getWidgetImplementation"); g("getImplementationName", "getWidgetImplementationName"); dw.widgets = dwm.widgets; dw.widgetIds = dwm.widgetIds; dw.root = dwm.root; })(); dojo.provide("dojo.i18n.common"); dojo.i18n.getLocalization = function(/*String*/packageName, /*String*/bundleName, /*String?*/locale){ // summary: // Returns an Object containing the localization for a given resource bundle // in a package, matching the specified locale. // // description: // Returns a hash containing name/value pairs in its prototypesuch that values can be easily overridden. // Throws an exception if the bundle is not found. // Bundle must have already been loaded by dojo.requireLocalization() or by a build optimization step. // // packageName: package which is associated with this resource // bundleName: the base filename of the resource bundle (without the ".js" suffix) // locale: the variant to load (optional). By default, the locale defined by the // host environment: dojo.locale dojo.hostenv.preloadLocalizations(); locale = dojo.hostenv.normalizeLocale(locale); // look for nearest locale match var elements = locale.split('-'); var module = [packageName,"nls",bundleName].join('.'); var bundle = dojo.hostenv.findModule(module, true); var localization; for(var i = elements.length; i > 0; i--){ var loc = elements.slice(0, i).join('_'); if(bundle[loc]){ localization = bundle[loc]; break; } } if(!localization){ localization = bundle.ROOT; } // make a singleton prototype so that the caller won't accidentally change the values globally if(localization){ var clazz = function(){}; clazz.prototype = localization; return new clazz(); // Object } dojo.raise("Bundle not found: " + bundleName + " in " + packageName+" , locale=" + locale); }; dojo.i18n.isLTR = function(/*String?*/locale){ // summary: // Is the language read left-to-right? Most exceptions are for middle eastern languages. // // locale: a string representing the locale. By default, the locale defined by the // host environment: dojo.locale var lang = dojo.hostenv.normalizeLocale(locale).split('-')[0]; var RTL = {ar:true,fa:true,he:true,ur:true,yi:true}; return !RTL[lang]; // Boolean }; dojo.provide("dojo.tlocale");