PageRenderTime 53ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 2ms

/selenium/src/web/tests/html/dojo-0.4.0-mini/dojo.js.uncompressed.js

https://github.com/shs96c/webdriver
JavaScript | 9197 lines | 6112 code | 892 blank | 2193 comment | 1546 complexity | 2461e504a85094e92540c368759942c7 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. if(typeof dojo == "undefined"){
  2. /**
  3. * @file bootstrap1.js
  4. *
  5. * summary: First file that is loaded that 'bootstraps' the entire dojo library suite.
  6. * note: Must run before hostenv_*.js file.
  7. *
  8. * @author Copyright 2004 Mark D. Anderson (mda@discerning.com)
  9. * TODOC: should the copyright be changed to Dojo Foundation?
  10. * @license Licensed under the Academic Free License 2.1 http://www.opensource.org/licenses/afl-2.1.php
  11. *
  12. * $Id: bootstrap1.js 6258 2006-10-20 03:12:36Z jburke $
  13. */
  14. // TODOC: HOW TO DOC THE BELOW?
  15. // @global: djConfig
  16. // summary:
  17. // Application code can set the global 'djConfig' prior to loading
  18. // the library to override certain global settings for how dojo works.
  19. // description: The variables that can be set are as follows:
  20. // - isDebug: false
  21. // - allowQueryConfig: false
  22. // - baseScriptUri: ""
  23. // - baseRelativePath: ""
  24. // - libraryScriptUri: ""
  25. // - iePreventClobber: false
  26. // - ieClobberMinimal: true
  27. // - locale: undefined
  28. // - extraLocale: undefined
  29. // - preventBackButtonFix: true
  30. // - searchIds: []
  31. // - parseWidgets: true
  32. // TODOC: HOW TO DOC THESE VARIABLES?
  33. // TODOC: IS THIS A COMPLETE LIST?
  34. // note:
  35. // 'djConfig' does not exist under 'dojo.*' so that it can be set before the
  36. // 'dojo' variable exists.
  37. // note:
  38. // Setting any of these variables *after* the library has loaded does nothing at all.
  39. // TODOC: is this still true? Release notes for 0.3 indicated they could be set after load.
  40. //
  41. //TODOC: HOW TO DOC THIS?
  42. // @global: dj_global
  43. // summary:
  44. // an alias for the top-level global object in the host environment
  45. // (e.g., the window object in a browser).
  46. // description:
  47. // Refer to 'dj_global' rather than referring to window to ensure your
  48. // code runs correctly in contexts other than web browsers (eg: Rhino on a server).
  49. var dj_global = this;
  50. //TODOC: HOW TO DOC THIS?
  51. // @global: dj_currentContext
  52. // summary:
  53. // Private global context object. Where 'dj_global' always refers to the boot-time
  54. // global context, 'dj_currentContext' can be modified for temporary context shifting.
  55. // dojo.global() returns dj_currentContext.
  56. // description:
  57. // Refer to dojo.global() rather than referring to dj_global to ensure your
  58. // code runs correctly in managed contexts.
  59. var dj_currentContext = this;
  60. // ****************************************************************
  61. // global public utils
  62. // TODOC: DO WE WANT TO NOTE THAT THESE ARE GLOBAL PUBLIC UTILS?
  63. // ****************************************************************
  64. function dj_undef(/*String*/ name, /*Object?*/ object){
  65. //summary: Returns true if 'name' is defined on 'object' (or globally if 'object' is null).
  66. //description: Note that 'defined' and 'exists' are not the same concept.
  67. return (typeof (object || dj_currentContext)[name] == "undefined"); // Boolean
  68. }
  69. // make sure djConfig is defined
  70. if(dj_undef("djConfig", this)){
  71. var djConfig = {};
  72. }
  73. //TODOC: HOW TO DOC THIS?
  74. // dojo is the root variable of (almost all) our public symbols -- make sure it is defined.
  75. if(dj_undef("dojo", this)){
  76. var dojo = {};
  77. }
  78. dojo.global = function(){
  79. // summary:
  80. // return the current global context object
  81. // (e.g., the window object in a browser).
  82. // description:
  83. // Refer to 'dojo.global()' rather than referring to window to ensure your
  84. // code runs correctly in contexts other than web browsers (eg: Rhino on a server).
  85. return dj_currentContext;
  86. }
  87. // Override locale setting, if specified
  88. dojo.locale = djConfig.locale;
  89. //TODOC: HOW TO DOC THIS?
  90. dojo.version = {
  91. // summary: version number of this instance of dojo.
  92. major: 0, minor: 4, patch: 0, flag: "",
  93. revision: Number("$Rev: 6258 $".match(/[0-9]+/)[0]),
  94. toString: function(){
  95. with(dojo.version){
  96. return major + "." + minor + "." + patch + flag + " (" + revision + ")"; // String
  97. }
  98. }
  99. }
  100. dojo.evalProp = function(/*String*/ name, /*Object*/ object, /*Boolean?*/ create){
  101. // summary: Returns 'object[name]'. If not defined and 'create' is true, will return a new Object.
  102. // description:
  103. // Returns null if 'object[name]' is not defined and 'create' is not true.
  104. // Note: 'defined' and 'exists' are not the same concept.
  105. if((!object)||(!name)) return undefined; // undefined
  106. if(!dj_undef(name, object)) return object[name]; // mixed
  107. return (create ? (object[name]={}) : undefined); // mixed
  108. }
  109. dojo.parseObjPath = function(/*String*/ path, /*Object?*/ context, /*Boolean?*/ create){
  110. // summary: Parse string path to an object, and return corresponding object reference and property name.
  111. // description:
  112. // Returns an object with two properties, 'obj' and 'prop'.
  113. // 'obj[prop]' is the reference indicated by 'path'.
  114. // path: Path to an object, in the form "A.B.C".
  115. // context: Object to use as root of path. Defaults to 'dojo.global()'.
  116. // create: If true, Objects will be created at any point along the 'path' that is undefined.
  117. var object = (context || dojo.global());
  118. var names = path.split('.');
  119. var prop = names.pop();
  120. for (var i=0,l=names.length;i<l && object;i++){
  121. object = dojo.evalProp(names[i], object, create);
  122. }
  123. return {obj: object, prop: prop}; // Object: {obj: Object, prop: String}
  124. }
  125. dojo.evalObjPath = function(/*String*/ path, /*Boolean?*/ create){
  126. // summary: Return the value of object at 'path' in the global scope, without using 'eval()'.
  127. // path: Path to an object, in the form "A.B.C".
  128. // create: If true, Objects will be created at any point along the 'path' that is undefined.
  129. if(typeof path != "string"){
  130. return dojo.global();
  131. }
  132. // fast path for no periods
  133. if(path.indexOf('.') == -1){
  134. return dojo.evalProp(path, dojo.global(), create); // mixed
  135. }
  136. //MOW: old 'with' syntax was confusing and would throw an error if parseObjPath returned null.
  137. var ref = dojo.parseObjPath(path, dojo.global(), create);
  138. if(ref){
  139. return dojo.evalProp(ref.prop, ref.obj, create); // mixed
  140. }
  141. return null;
  142. }
  143. dojo.errorToString = function(/*Error*/ exception){
  144. // summary: Return an exception's 'message', 'description' or text.
  145. // TODO: overriding Error.prototype.toString won't accomplish this?
  146. // ... since natively generated Error objects do not always reflect such things?
  147. if(!dj_undef("message", exception)){
  148. return exception.message; // String
  149. }else if(!dj_undef("description", exception)){
  150. return exception.description; // String
  151. }else{
  152. return exception; // Error
  153. }
  154. }
  155. dojo.raise = function(/*String*/ message, /*Error?*/ exception){
  156. // summary: Common point for raising exceptions in Dojo to enable logging.
  157. // Throws an error message with text of 'exception' if provided, or
  158. // rethrows exception object.
  159. if(exception){
  160. message = message + ": "+dojo.errorToString(exception);
  161. }
  162. // print the message to the user if hostenv.println is defined
  163. try { if(djConfig.isDebug){ dojo.hostenv.println("FATAL exception raised: "+message); } } catch (e) {}
  164. throw exception || Error(message);
  165. }
  166. //Stub functions so things don't break.
  167. //TODOC: HOW TO DOC THESE?
  168. dojo.debug = function(){};
  169. dojo.debugShallow = function(obj){};
  170. dojo.profile = { start: function(){}, end: function(){}, stop: function(){}, dump: function(){} };
  171. function dj_eval(/*String*/ scriptFragment){
  172. // summary: Perform an evaluation in the global scope. Use this rather than calling 'eval()' directly.
  173. // description: Placed in a separate function to minimize size of trapped evaluation context.
  174. // note:
  175. // - JSC eval() takes an optional second argument which can be 'unsafe'.
  176. // - Mozilla/SpiderMonkey eval() takes an optional second argument which is the
  177. // scope object for new symbols.
  178. return dj_global.eval ? dj_global.eval(scriptFragment) : eval(scriptFragment); // mixed
  179. }
  180. dojo.unimplemented = function(/*String*/ funcname, /*String?*/ extra){
  181. // summary: Throw an exception because some function is not implemented.
  182. // extra: Text to append to the exception message.
  183. var message = "'" + funcname + "' not implemented";
  184. if (extra != null) { message += " " + extra; }
  185. dojo.raise(message);
  186. }
  187. dojo.deprecated = function(/*String*/ behaviour, /*String?*/ extra, /*String?*/ removal){
  188. // summary: Log a debug message to indicate that a behavior has been deprecated.
  189. // extra: Text to append to the message.
  190. // removal: Text to indicate when in the future the behavior will be removed.
  191. var message = "DEPRECATED: " + behaviour;
  192. if(extra){ message += " " + extra; }
  193. if(removal){ message += " -- will be removed in version: " + removal; }
  194. dojo.debug(message);
  195. }
  196. dojo.render = (function(){
  197. //TODOC: HOW TO DOC THIS?
  198. // summary: Details rendering support, OS and browser of the current environment.
  199. // TODOC: is this something many folks will interact with? If so, we should doc the structure created...
  200. function vscaffold(prefs, names){
  201. var tmp = {
  202. capable: false,
  203. support: {
  204. builtin: false,
  205. plugin: false
  206. },
  207. prefixes: prefs
  208. };
  209. for(var i=0; i<names.length; i++){
  210. tmp[names[i]] = false;
  211. }
  212. return tmp;
  213. }
  214. return {
  215. name: "",
  216. ver: dojo.version,
  217. os: { win: false, linux: false, osx: false },
  218. html: vscaffold(["html"], ["ie", "opera", "khtml", "safari", "moz"]),
  219. svg: vscaffold(["svg"], ["corel", "adobe", "batik"]),
  220. vml: vscaffold(["vml"], ["ie"]),
  221. swf: vscaffold(["Swf", "Flash", "Mm"], ["mm"]),
  222. swt: vscaffold(["Swt"], ["ibm"])
  223. };
  224. })();
  225. // ****************************************************************
  226. // dojo.hostenv methods that must be defined in hostenv_*.js
  227. // ****************************************************************
  228. /**
  229. * The interface definining the interaction with the EcmaScript host environment.
  230. */
  231. /*
  232. * None of these methods should ever be called directly by library users.
  233. * Instead public methods such as loadModule should be called instead.
  234. */
  235. dojo.hostenv = (function(){
  236. // TODOC: HOW TO DOC THIS?
  237. // summary: Provides encapsulation of behavior that changes across different 'host environments'
  238. // (different browsers, server via Rhino, etc).
  239. // description: None of these methods should ever be called directly by library users.
  240. // Use public methods such as 'loadModule' instead.
  241. // default configuration options
  242. var config = {
  243. isDebug: false,
  244. allowQueryConfig: false,
  245. baseScriptUri: "",
  246. baseRelativePath: "",
  247. libraryScriptUri: "",
  248. iePreventClobber: false,
  249. ieClobberMinimal: true,
  250. preventBackButtonFix: true,
  251. delayMozLoadingFix: false,
  252. searchIds: [],
  253. parseWidgets: true
  254. };
  255. if (typeof djConfig == "undefined") { djConfig = config; }
  256. else {
  257. for (var option in config) {
  258. if (typeof djConfig[option] == "undefined") {
  259. djConfig[option] = config[option];
  260. }
  261. }
  262. }
  263. return {
  264. name_: '(unset)',
  265. version_: '(unset)',
  266. getName: function(){
  267. // sumary: Return the name of the host environment.
  268. return this.name_; // String
  269. },
  270. getVersion: function(){
  271. // summary: Return the version of the hostenv.
  272. return this.version_; // String
  273. },
  274. getText: function(/*String*/ uri){
  275. // summary: Read the plain/text contents at the specified 'uri'.
  276. // description:
  277. // If 'getText()' is not implemented, then it is necessary to override
  278. // 'loadUri()' with an implementation that doesn't rely on it.
  279. dojo.unimplemented('getText', "uri=" + uri);
  280. }
  281. };
  282. })();
  283. dojo.hostenv.getBaseScriptUri = function(){
  284. // summary: Return the base script uri that other scripts are found relative to.
  285. // TODOC: HUH? This comment means nothing to me. What other scripts? Is this the path to other dojo libraries?
  286. // MAYBE: Return the base uri to scripts in the dojo library. ???
  287. // return: Empty string or a path ending in '/'.
  288. if(djConfig.baseScriptUri.length){
  289. return djConfig.baseScriptUri;
  290. }
  291. // MOW: Why not:
  292. // uri = djConfig.libraryScriptUri || djConfig.baseRelativePath
  293. // ??? Why 'new String(...)'
  294. var uri = new String(djConfig.libraryScriptUri||djConfig.baseRelativePath);
  295. if (!uri) { dojo.raise("Nothing returned by getLibraryScriptUri(): " + uri); }
  296. // MOW: uri seems to not be actually used. Seems to be hard-coding to djConfig.baseRelativePath... ???
  297. var lastslash = uri.lastIndexOf('/'); // MOW ???
  298. djConfig.baseScriptUri = djConfig.baseRelativePath;
  299. return djConfig.baseScriptUri; // String
  300. }
  301. /*
  302. * loader.js - A bootstrap module. Runs before the hostenv_*.js file. Contains all of the package loading methods.
  303. */
  304. //A semi-colon is at the start of the line because after doing a build, this function definition
  305. //get compressed onto the same line as the last line in bootstrap1.js. That list line is just a
  306. //curly bracket, and the browser complains about that syntax. The semicolon fixes it. Putting it
  307. //here instead of at the end of bootstrap1.js, since it is more of an issue for this file, (using
  308. //the closure), and bootstrap1.js could change in the future.
  309. ;(function(){
  310. //Additional properties for dojo.hostenv
  311. var _addHostEnv = {
  312. pkgFileName: "__package__",
  313. // for recursion protection
  314. loading_modules_: {},
  315. loaded_modules_: {},
  316. addedToLoadingCount: [],
  317. removedFromLoadingCount: [],
  318. inFlightCount: 0,
  319. // FIXME: it should be possible to pull module prefixes in from djConfig
  320. modulePrefixes_: {
  321. dojo: {name: "dojo", value: "src"}
  322. },
  323. setModulePrefix: function(/*String*/module, /*String*/prefix){
  324. // summary: establishes module/prefix pair
  325. this.modulePrefixes_[module] = {name: module, value: prefix};
  326. },
  327. moduleHasPrefix: function(/*String*/module){
  328. // summary: checks to see if module has been established
  329. var mp = this.modulePrefixes_;
  330. return Boolean(mp[module] && mp[module].value); // Boolean
  331. },
  332. getModulePrefix: function(/*String*/module){
  333. // summary: gets the prefix associated with module
  334. if(this.moduleHasPrefix(module)){
  335. return this.modulePrefixes_[module].value; // String
  336. }
  337. return module; // String
  338. },
  339. getTextStack: [],
  340. loadUriStack: [],
  341. loadedUris: [],
  342. //WARNING: This variable is referenced by packages outside of bootstrap: FloatingPane.js and undo/browser.js
  343. post_load_: false,
  344. //Egad! Lots of test files push on this directly instead of using dojo.addOnLoad.
  345. modulesLoadedListeners: [],
  346. unloadListeners: [],
  347. loadNotifying: false
  348. };
  349. //Add all of these properties to dojo.hostenv
  350. for(var param in _addHostEnv){
  351. dojo.hostenv[param] = _addHostEnv[param];
  352. }
  353. })();
  354. dojo.hostenv.loadPath = function(/*String*/relpath, /*String?*/module, /*Function?*/cb){
  355. // summary:
  356. // Load a Javascript module given a relative path
  357. //
  358. // description:
  359. // Loads and interprets the script located at relpath, which is relative to the
  360. // script root directory. If the script is found but its interpretation causes
  361. // a runtime exception, that exception is not caught by us, so the caller will
  362. // see it. We return a true value if and only if the script is found.
  363. //
  364. // For now, we do not have an implementation of a true search path. We
  365. // consider only the single base script uri, as returned by getBaseScriptUri().
  366. //
  367. // relpath: A relative path to a script (no leading '/', and typically
  368. // ending in '.js').
  369. // module: A module whose existance to check for after loading a path.
  370. // Can be used to determine success or failure of the load.
  371. // cb: a callback function to pass the result of evaluating the script
  372. var uri;
  373. if(relpath.charAt(0) == '/' || relpath.match(/^\w+:/)){
  374. // dojo.raise("relpath '" + relpath + "'; must be relative");
  375. uri = relpath;
  376. }else{
  377. uri = this.getBaseScriptUri() + relpath;
  378. }
  379. if(djConfig.cacheBust && dojo.render.html.capable){
  380. uri += "?" + String(djConfig.cacheBust).replace(/\W+/g,"");
  381. }
  382. try{
  383. return !module ? this.loadUri(uri, cb) : this.loadUriAndCheck(uri, module, cb); // Boolean
  384. }catch(e){
  385. dojo.debug(e);
  386. return false; // Boolean
  387. }
  388. }
  389. dojo.hostenv.loadUri = function(/*String (URL)*/uri, /*Function?*/cb){
  390. // summary:
  391. // Loads JavaScript from a URI
  392. //
  393. // description:
  394. // Reads the contents of the URI, and evaluates the contents. This is used to load modules as well
  395. // as resource bundles. Returns true if it succeeded. Returns false if the URI reading failed.
  396. // Throws if the evaluation throws.
  397. //
  398. // uri: a uri which points at the script to be loaded
  399. // cb: a callback function to process the result of evaluating the script as an expression, typically
  400. // used by the resource bundle loader to load JSON-style resources
  401. if(this.loadedUris[uri]){
  402. return true; // Boolean
  403. }
  404. var contents = this.getText(uri, null, true);
  405. if(!contents){ return false; } // Boolean
  406. this.loadedUris[uri] = true;
  407. if(cb){ contents = '('+contents+')'; }
  408. var value = dj_eval(contents);
  409. if(cb){ cb(value); }
  410. return true; // Boolean
  411. }
  412. // FIXME: probably need to add logging to this method
  413. dojo.hostenv.loadUriAndCheck = function(/*String (URL)*/uri, /*String*/moduleName, /*Function?*/cb){
  414. // summary: calls loadUri then findModule and returns true if both succeed
  415. var ok = true;
  416. try{
  417. ok = this.loadUri(uri, cb);
  418. }catch(e){
  419. dojo.debug("failed loading ", uri, " with error: ", e);
  420. }
  421. return Boolean(ok && this.findModule(moduleName, false)); // Boolean
  422. }
  423. dojo.loaded = function(){ }
  424. dojo.unloaded = function(){ }
  425. dojo.hostenv.loaded = function(){
  426. this.loadNotifying = true;
  427. this.post_load_ = true;
  428. var mll = this.modulesLoadedListeners;
  429. for(var x=0; x<mll.length; x++){
  430. mll[x]();
  431. }
  432. //Clear listeners so new ones can be added
  433. //For other xdomain package loads after the initial load.
  434. this.modulesLoadedListeners = [];
  435. this.loadNotifying = false;
  436. dojo.loaded();
  437. }
  438. dojo.hostenv.unloaded = function(){
  439. var mll = this.unloadListeners;
  440. while(mll.length){
  441. (mll.pop())();
  442. }
  443. dojo.unloaded();
  444. }
  445. dojo.addOnLoad = function(/*Object?*/obj, /*String|Function*/functionName) {
  446. // summary:
  447. // Registers a function to be triggered after the DOM has finished loading
  448. // and widgets declared in markup have been instantiated. Images and CSS files
  449. // may or may not have finished downloading when the specified function is called.
  450. // (Note that widgets' CSS and HTML code is guaranteed to be downloaded before said
  451. // widgets are instantiated.)
  452. //
  453. // usage:
  454. // dojo.addOnLoad(functionPointer)
  455. // dojo.addOnLoad(object, "functionName")
  456. var dh = dojo.hostenv;
  457. if(arguments.length == 1) {
  458. dh.modulesLoadedListeners.push(obj);
  459. } else if(arguments.length > 1) {
  460. dh.modulesLoadedListeners.push(function() {
  461. obj[functionName]();
  462. });
  463. }
  464. //Added for xdomain loading. dojo.addOnLoad is used to
  465. //indicate callbacks after doing some dojo.require() statements.
  466. //In the xdomain case, if all the requires are loaded (after initial
  467. //page load), then immediately call any listeners.
  468. if(dh.post_load_ && dh.inFlightCount == 0 && !dh.loadNotifying){
  469. dh.callLoaded();
  470. }
  471. }
  472. dojo.addOnUnload = function(/*Object?*/obj, /*String|Function?*/functionName){
  473. // summary: registers a function to be triggered when the page unloads
  474. //
  475. // usage:
  476. // dojo.addOnLoad(functionPointer)
  477. // dojo.addOnLoad(object, "functionName")
  478. var dh = dojo.hostenv;
  479. if(arguments.length == 1){
  480. dh.unloadListeners.push(obj);
  481. } else if(arguments.length > 1) {
  482. dh.unloadListeners.push(function() {
  483. obj[functionName]();
  484. });
  485. }
  486. }
  487. dojo.hostenv.modulesLoaded = function(){
  488. if(this.post_load_){ return; }
  489. if(this.loadUriStack.length==0 && this.getTextStack.length==0){
  490. if(this.inFlightCount > 0){
  491. dojo.debug("files still in flight!");
  492. return;
  493. }
  494. dojo.hostenv.callLoaded();
  495. }
  496. }
  497. dojo.hostenv.callLoaded = function(){
  498. if(typeof setTimeout == "object"){
  499. setTimeout("dojo.hostenv.loaded();", 0);
  500. }else{
  501. dojo.hostenv.loaded();
  502. }
  503. }
  504. dojo.hostenv.getModuleSymbols = function(/*String*/modulename){
  505. // summary:
  506. // Converts a module name in dotted JS notation to an array representing the path in the source tree
  507. var syms = modulename.split(".");
  508. for(var i = syms.length; i>0; i--){
  509. var parentModule = syms.slice(0, i).join(".");
  510. if ((i==1) && !this.moduleHasPrefix(parentModule)){
  511. //Support default module directory (sibling of dojo)
  512. syms[0] = "../" + syms[0];
  513. }else{
  514. var parentModulePath = this.getModulePrefix(parentModule);
  515. if(parentModulePath != parentModule){
  516. syms.splice(0, i, parentModulePath);
  517. break;
  518. }
  519. }
  520. }
  521. return syms; // Array
  522. }
  523. dojo.hostenv._global_omit_module_check = false;
  524. dojo.hostenv.loadModule = function(/*String*/moduleName, /*Boolean?*/exactOnly, /*Boolean?*/omitModuleCheck){
  525. // summary:
  526. // loads a Javascript module from the appropriate URI
  527. //
  528. // description:
  529. // loadModule("A.B") first checks to see if symbol A.B is defined.
  530. // If it is, it is simply returned (nothing to do).
  531. //
  532. // If it is not defined, it will look for "A/B.js" in the script root directory,
  533. // followed by "A.js".
  534. //
  535. // It throws if it cannot find a file to load, or if the symbol A.B is not
  536. // defined after loading.
  537. //
  538. // It returns the object A.B.
  539. //
  540. // This does nothing about importing symbols into the current package.
  541. // It is presumed that the caller will take care of that. For example, to import
  542. // all symbols:
  543. //
  544. // with (dojo.hostenv.loadModule("A.B")) {
  545. // ...
  546. // }
  547. //
  548. // And to import just the leaf symbol:
  549. //
  550. // var B = dojo.hostenv.loadModule("A.B");
  551. // ...
  552. //
  553. // dj_load is an alias for dojo.hostenv.loadModule
  554. if(!moduleName){ return; }
  555. omitModuleCheck = this._global_omit_module_check || omitModuleCheck;
  556. var module = this.findModule(moduleName, false);
  557. if(module){
  558. return module;
  559. }
  560. // protect against infinite recursion from mutual dependencies
  561. if(dj_undef(moduleName, this.loading_modules_)){
  562. this.addedToLoadingCount.push(moduleName);
  563. }
  564. this.loading_modules_[moduleName] = 1;
  565. // convert periods to slashes
  566. var relpath = moduleName.replace(/\./g, '/') + '.js';
  567. var nsyms = moduleName.split(".");
  568. // this line allowed loading of a module manifest as if it were a namespace
  569. // it's an interesting idea, but shouldn't be combined with 'namespaces' proper
  570. // and leads to unwanted dependencies
  571. // the effect can be achieved in other (albeit less-flexible) ways now, so I am
  572. // removing this pending further design work
  573. // perhaps we can explicitly define this idea of a 'module manifest', and subclass
  574. // 'namespace manifest' from that
  575. //dojo.getNamespace(nsyms[0]);
  576. var syms = this.getModuleSymbols(moduleName);
  577. var startedRelative = ((syms[0].charAt(0) != '/') && !syms[0].match(/^\w+:/));
  578. var last = syms[syms.length - 1];
  579. var ok;
  580. // figure out if we're looking for a full package, if so, we want to do
  581. // things slightly diffrently
  582. if(last=="*"){
  583. moduleName = nsyms.slice(0, -1).join('.');
  584. while(syms.length){
  585. syms.pop();
  586. syms.push(this.pkgFileName);
  587. relpath = syms.join("/") + '.js';
  588. if(startedRelative && relpath.charAt(0)=="/"){
  589. relpath = relpath.slice(1);
  590. }
  591. ok = this.loadPath(relpath, !omitModuleCheck ? moduleName : null);
  592. if(ok){ break; }
  593. syms.pop();
  594. }
  595. }else{
  596. relpath = syms.join("/") + '.js';
  597. moduleName = nsyms.join('.');
  598. var modArg = !omitModuleCheck ? moduleName : null;
  599. ok = this.loadPath(relpath, modArg);
  600. if(!ok && !exactOnly){
  601. syms.pop();
  602. while(syms.length){
  603. relpath = syms.join('/') + '.js';
  604. ok = this.loadPath(relpath, modArg);
  605. if(ok){ break; }
  606. syms.pop();
  607. relpath = syms.join('/') + '/'+this.pkgFileName+'.js';
  608. if(startedRelative && relpath.charAt(0)=="/"){
  609. relpath = relpath.slice(1);
  610. }
  611. ok = this.loadPath(relpath, modArg);
  612. if(ok){ break; }
  613. }
  614. }
  615. if(!ok && !omitModuleCheck){
  616. dojo.raise("Could not load '" + moduleName + "'; last tried '" + relpath + "'");
  617. }
  618. }
  619. // check that the symbol was defined
  620. //Don't bother if we're doing xdomain (asynchronous) loading.
  621. if(!omitModuleCheck && !this["isXDomain"]){
  622. // pass in false so we can give better error
  623. module = this.findModule(moduleName, false);
  624. if(!module){
  625. dojo.raise("symbol '" + moduleName + "' is not defined after loading '" + relpath + "'");
  626. }
  627. }
  628. return module;
  629. }
  630. dojo.hostenv.startPackage = function(/*String*/packageName){
  631. // summary:
  632. // Creates a JavaScript package
  633. //
  634. // description:
  635. // startPackage("A.B") follows the path, and at each level creates a new empty
  636. // object or uses what already exists. It returns the result.
  637. //
  638. // packageName: the package to be created as a String in dot notation
  639. //Make sure we have a string.
  640. var fullPkgName = String(packageName);
  641. var strippedPkgName = fullPkgName;
  642. var syms = packageName.split(/\./);
  643. if(syms[syms.length-1]=="*"){
  644. syms.pop();
  645. strippedPkgName = syms.join(".");
  646. }
  647. var evaledPkg = dojo.evalObjPath(strippedPkgName, true);
  648. this.loaded_modules_[fullPkgName] = evaledPkg;
  649. this.loaded_modules_[strippedPkgName] = evaledPkg;
  650. return evaledPkg; // Object
  651. }
  652. dojo.hostenv.findModule = function(/*String*/moduleName, /*Boolean?*/mustExist){
  653. // summary:
  654. // Returns the Object representing the module, if it exists, otherwise null.
  655. //
  656. // moduleName A fully qualified module including package name, like 'A.B'.
  657. // mustExist Optional, default false. throw instead of returning null
  658. // if the module does not currently exist.
  659. var lmn = String(moduleName);
  660. if(this.loaded_modules_[lmn]){
  661. return this.loaded_modules_[lmn]; // Object
  662. }
  663. if(mustExist){
  664. dojo.raise("no loaded module named '" + moduleName + "'");
  665. }
  666. return null; // null
  667. }
  668. //Start of old bootstrap2:
  669. dojo.kwCompoundRequire = function(/*Object containing Arrays*/modMap){
  670. // description:
  671. // This method taks a "map" of arrays which one can use to optionally load dojo
  672. // modules. The map is indexed by the possible dojo.hostenv.name_ values, with
  673. // two additional values: "default" and "common". The items in the "default"
  674. // array will be loaded if none of the other items have been choosen based on
  675. // the hostenv.name_ item. The items in the "common" array will _always_ be
  676. // loaded, regardless of which list is chosen. Here's how it's normally
  677. // called:
  678. //
  679. // dojo.kwCompoundRequire({
  680. // browser: [
  681. // ["foo.bar.baz", true, true], // an example that passes multiple args to loadModule()
  682. // "foo.sample.*",
  683. // "foo.test,
  684. // ],
  685. // default: [ "foo.sample.*" ],
  686. // common: [ "really.important.module.*" ]
  687. // });
  688. var common = modMap["common"]||[];
  689. var result = modMap[dojo.hostenv.name_] ? common.concat(modMap[dojo.hostenv.name_]||[]) : common.concat(modMap["default"]||[]);
  690. for(var x=0; x<result.length; x++){
  691. var curr = result[x];
  692. if(curr.constructor == Array){
  693. dojo.hostenv.loadModule.apply(dojo.hostenv, curr);
  694. }else{
  695. dojo.hostenv.loadModule(curr);
  696. }
  697. }
  698. }
  699. dojo.require = function(/*String*/ resourceName){
  700. // summary
  701. // Ensure that the given resource (ie, javascript
  702. // source file) has been loaded.
  703. // description
  704. // dojo.require() is similar to C's #include command or java's "import" command.
  705. // You call dojo.require() to pull in the resources (ie, javascript source files)
  706. // that define the functions you are using.
  707. //
  708. // Note that in the case of a build, many resources have already been included
  709. // into dojo.js (ie, many of the javascript source files have been compressed and
  710. // concatened into dojo.js), so many dojo.require() calls will simply return
  711. // without downloading anything.
  712. dojo.hostenv.loadModule.apply(dojo.hostenv, arguments);
  713. }
  714. dojo.requireIf = function(/*Boolean*/ condition, /*String*/ resourceName){
  715. // summary
  716. // If the condition is true then call dojo.require() for the specified resource
  717. var arg0 = arguments[0];
  718. if((arg0 === true)||(arg0=="common")||(arg0 && dojo.render[arg0].capable)){
  719. var args = [];
  720. for (var i = 1; i < arguments.length; i++) { args.push(arguments[i]); }
  721. dojo.require.apply(dojo, args);
  722. }
  723. }
  724. dojo.requireAfterIf = dojo.requireIf;
  725. dojo.provide = function(/*String*/ resourceName){
  726. // summary
  727. // Each javascript source file must have (exactly) one dojo.provide()
  728. // call at the top of the file, corresponding to the file name.
  729. // For example, dojo/src/foo.js must have dojo.provide("dojo.foo"); at the top of the file.
  730. //
  731. // description
  732. // Each javascript source file is called a resource. When a resource
  733. // is loaded by the browser, dojo.provide() registers that it has
  734. // been loaded.
  735. //
  736. // For backwards compatibility reasons, in addition to registering the resource,
  737. // dojo.provide() also ensures that the javascript object for the module exists. For
  738. // example, dojo.provide("dojo.html.common"), in addition to registering that common.js
  739. // is a resource for the dojo.html module, will ensure that the dojo.html javascript object
  740. // exists, so that calls like dojo.html.foo = function(){ ... } don't fail.
  741. //
  742. // In the case of a build (or in the future, a rollup), where multiple javascript source
  743. // files are combined into one bigger file (similar to a .lib or .jar file), that file
  744. // will contain multiple dojo.provide() calls, to note that it includes
  745. // multiple resources.
  746. return dojo.hostenv.startPackage.apply(dojo.hostenv, arguments);
  747. }
  748. dojo.registerModulePath = function(/*String*/module, /*String*/prefix){
  749. // summary: maps a module name to a path
  750. // description: An unregistered module is given the default path of ../<module>,
  751. // relative to Dojo root. For example, module acme is mapped to ../acme.
  752. // If you want to use a different module name, use dojo.registerModulePath.
  753. return dojo.hostenv.setModulePrefix(module, prefix);
  754. }
  755. dojo.setModulePrefix = function(/*String*/module, /*String*/prefix){
  756. // summary: maps a module name to a path
  757. dojo.deprecated('dojo.setModulePrefix("' + module + '", "' + prefix + '")', "replaced by dojo.registerModulePath", "0.5");
  758. return dojo.registerModulePath(module, prefix);
  759. }
  760. dojo.exists = function(/*Object*/obj, /*String*/name){
  761. // summary: determine if an object supports a given method
  762. // description: useful for longer api chains where you have to test each object in the chain
  763. var p = name.split(".");
  764. for(var i = 0; i < p.length; i++){
  765. if(!obj[p[i]]){ return false; } // Boolean
  766. obj = obj[p[i]];
  767. }
  768. return true; // Boolean
  769. }
  770. // Localization routines
  771. dojo.hostenv.normalizeLocale = function(/*String?*/locale){
  772. // summary:
  773. // Returns canonical form of locale, as used by Dojo. All variants are case-insensitive and are separated by '-'
  774. // as specified in RFC 3066. If no locale is specified, the user agent's default is returned.
  775. return locale ? locale.toLowerCase() : dojo.locale; // String
  776. };
  777. dojo.hostenv.searchLocalePath = function(/*String*/locale, /*Boolean*/down, /*Function*/searchFunc){
  778. // summary:
  779. // A helper method to assist in searching for locale-based resources. Will iterate through
  780. // the variants of a particular locale, either up or down, executing a callback function.
  781. // For example, "en-us" and true will try "en-us" followed by "en" and finally "ROOT".
  782. locale = dojo.hostenv.normalizeLocale(locale);
  783. var elements = locale.split('-');
  784. var searchlist = [];
  785. for(var i = elements.length; i > 0; i--){
  786. searchlist.push(elements.slice(0, i).join('-'));
  787. }
  788. searchlist.push(false);
  789. if(down){searchlist.reverse();}
  790. for(var j = searchlist.length - 1; j >= 0; j--){
  791. var loc = searchlist[j] || "ROOT";
  792. var stop = searchFunc(loc);
  793. if(stop){ break; }
  794. }
  795. }
  796. //These two functions are placed outside of preloadLocalizations
  797. //So that the xd loading can use/override them.
  798. dojo.hostenv.localesGenerated /***BUILD:localesGenerated***/; // value will be inserted here at build time, if necessary
  799. dojo.hostenv.registerNlsPrefix = function(){
  800. // summary:
  801. // Register module "nls" to point where Dojo can find pre-built localization files
  802. dojo.registerModulePath("nls","nls");
  803. }
  804. dojo.hostenv.preloadLocalizations = function(){
  805. // summary:
  806. // Load built, flattened resource bundles, if available for all locales used in the page.
  807. // Execute only once. Note that this is a no-op unless there is a build.
  808. if(dojo.hostenv.localesGenerated){
  809. dojo.hostenv.registerNlsPrefix();
  810. function preload(locale){
  811. locale = dojo.hostenv.normalizeLocale(locale);
  812. dojo.hostenv.searchLocalePath(locale, true, function(loc){
  813. for(var i=0; i<dojo.hostenv.localesGenerated.length;i++){
  814. if(dojo.hostenv.localesGenerated[i] == loc){
  815. dojo["require"]("nls.dojo_"+loc);
  816. return true; // Boolean
  817. }
  818. }
  819. return false; // Boolean
  820. });
  821. }
  822. preload();
  823. var extra = djConfig.extraLocale||[];
  824. for(var i=0; i<extra.length; i++){
  825. preload(extra[i]);
  826. }
  827. }
  828. dojo.hostenv.preloadLocalizations = function(){};
  829. }
  830. dojo.requireLocalization = function(/*String*/moduleName, /*String*/bundleName, /*String?*/locale){
  831. // summary:
  832. // Declares translated resources and loads them if necessary, in the same style as dojo.require.
  833. // Contents of the resource bundle are typically strings, but may be any name/value pair,
  834. // represented in JSON format. See also dojo.i18n.getLocalization.
  835. //
  836. // moduleName: name of the package containing the "nls" directory in which the bundle is found
  837. // bundleName: bundle name, i.e. the filename without the '.js' suffix
  838. // locale: the locale to load (optional) By default, the browser's user locale as defined by dojo.locale
  839. //
  840. // description:
  841. // Load translated resource bundles provided underneath the "nls" directory within a package.
  842. // Translated resources may be located in different packages throughout the source tree. For example,
  843. // a particular widget may define one or more resource bundles, structured in a program as follows,
  844. // where moduleName is mycode.mywidget and bundleNames available include bundleone and bundletwo:
  845. // ...
  846. // mycode/
  847. // mywidget/
  848. // nls/
  849. // bundleone.js (the fallback translation, English in this example)
  850. // bundletwo.js (also a fallback translation)
  851. // de/
  852. // bundleone.js
  853. // bundletwo.js
  854. // de-at/
  855. // bundleone.js
  856. // en/
  857. // (empty; use the fallback translation)
  858. // en-us/
  859. // bundleone.js
  860. // en-gb/
  861. // bundleone.js
  862. // es/
  863. // bundleone.js
  864. // bundletwo.js
  865. // ...etc
  866. // ...
  867. // Each directory is named for a locale as specified by RFC 3066, (http://www.ietf.org/rfc/rfc3066.txt),
  868. // normalized in lowercase. Note that the two bundles in the example do not define all the same variants.
  869. // For a given locale, bundles will be loaded for that locale and all more general locales above it, including
  870. // a fallback at the root directory. For example, a declaration for the "de-at" locale will first
  871. // load nls/de-at/bundleone.js, then nls/de/bundleone.js and finally nls/bundleone.js. The data will
  872. // be flattened into a single Object so that lookups will follow this cascading pattern. An optional build
  873. // step can preload the bundles to avoid data redundancy and the multiple network hits normally required to
  874. // load these resources.
  875. dojo.hostenv.preloadLocalizations();
  876. var bundlePackage = [moduleName, "nls", bundleName].join(".");
  877. //NOTE: When loading these resources, the packaging does not match what is on disk. This is an
  878. // implementation detail, as this is just a private data structure to hold the loaded resources.
  879. // e.g. tests/hello/nls/en-us/salutations.js is loaded as the object tests.hello.nls.salutations.en_us={...}
  880. // The structure on disk is intended to be most convenient for developers and translators, but in memory
  881. // it is more logical and efficient to store in a different order. Locales cannot use dashes, since the
  882. // resulting path will not evaluate as valid JS, so we translate them to underscores.
  883. var bundle = dojo.hostenv.findModule(bundlePackage);
  884. if(bundle){
  885. if(djConfig.localizationComplete && bundle._built){return;}
  886. var jsLoc = dojo.hostenv.normalizeLocale(locale).replace('-', '_');
  887. var translationPackage = bundlePackage+"."+jsLoc;
  888. if(dojo.hostenv.findModule(translationPackage)){return;}
  889. }
  890. bundle = dojo.hostenv.startPackage(bundlePackage);
  891. var syms = dojo.hostenv.getModuleSymbols(moduleName);
  892. var modpath = syms.concat("nls").join("/");
  893. var parent;
  894. dojo.hostenv.searchLocalePath(locale, false, function(loc){
  895. var jsLoc = loc.replace('-', '_');
  896. var translationPackage = bundlePackage + "." + jsLoc;
  897. var loaded = false;
  898. if(!dojo.hostenv.findModule(translationPackage)){
  899. // Mark loaded whether it's found or not, so that further load attempts will not be made
  900. dojo.hostenv.startPackage(translationPackage);
  901. var module = [modpath];
  902. if(loc != "ROOT"){module.push(loc);}
  903. module.push(bundleName);
  904. var filespec = module.join("/") + '.js';
  905. loaded = dojo.hostenv.loadPath(filespec, null, function(hash){
  906. // Use singleton with prototype to point to parent bundle, then mix-in result from loadPath
  907. var clazz = function(){};
  908. clazz.prototype = parent;
  909. bundle[jsLoc] = new clazz();
  910. for(var j in hash){ bundle[jsLoc][j] = hash[j]; }
  911. });
  912. }else{
  913. loaded = true;
  914. }
  915. if(loaded && bundle[jsLoc]){
  916. parent = bundle[jsLoc];
  917. }else{
  918. bundle[jsLoc] = parent;
  919. }
  920. });
  921. };
  922. (function(){
  923. // If other locales are used, dojo.requireLocalization should load them as well, by default.
  924. // Override dojo.requireLocalization to do load the default bundle, then iterate through the
  925. // extraLocale list and load those translations as well, unless a particular locale was requested.
  926. var extra = djConfig.extraLocale;
  927. if(extra){
  928. if(!extra instanceof Array){
  929. extra = [extra];
  930. }
  931. var req = dojo.requireLocalization;
  932. dojo.requireLocalization = function(m, b, locale){
  933. req(m,b,locale);
  934. if(locale){return;}
  935. for(var i=0; i<extra.length; i++){
  936. req(m,b,extra[i]);
  937. }
  938. };
  939. }
  940. })();
  941. };
  942. if (typeof window != 'undefined') {
  943. // attempt to figure out the path to dojo if it isn't set in the config
  944. (function() {
  945. // before we get any further with the config options, try to pick them out
  946. // of the URL. Most of this code is from NW
  947. if(djConfig.allowQueryConfig){
  948. var baseUrl = document.location.toString(); // FIXME: use location.query instead?
  949. var params = baseUrl.split("?", 2);
  950. if(params.length > 1){
  951. var paramStr = params[1];
  952. var pairs = paramStr.split("&");
  953. for(var x in pairs){
  954. var sp = pairs[x].split("=");
  955. // FIXME: is this eval dangerous?
  956. if((sp[0].length > 9)&&(sp[0].substr(0, 9) == "djConfig.")){
  957. var opt = sp[0].substr(9);
  958. try{
  959. djConfig[opt]=eval(sp[1]);
  960. }catch(e){
  961. djConfig[opt]=sp[1];
  962. }
  963. }
  964. }
  965. }
  966. }
  967. if(((djConfig["baseScriptUri"] == "")||(djConfig["baseRelativePath"] == "")) &&(document && document.getElementsByTagName)){
  968. var scripts = document.getElementsByTagName("script");
  969. var rePkg = /(__package__|dojo|bootstrap1)\.js([\?\.]|$)/i;
  970. for(var i = 0; i < scripts.length; i++) {
  971. var src = scripts[i].getAttribute("src");
  972. if(!src) { continue; }
  973. var m = src.match(rePkg);
  974. if(m) {
  975. var root = src.substring(0, m.index);
  976. if(src.indexOf("bootstrap1") > -1) { root += "../"; }
  977. if(!this["djConfig"]) { djConfig = {}; }
  978. if(djConfig["baseScriptUri"] == "") { djConfig["baseScriptUri"] = root; }
  979. if(djConfig["baseRelativePath"] == "") { djConfig["baseRelativePath"] = root; }
  980. break;
  981. }
  982. }
  983. }
  984. // fill in the rendering support information in dojo.render.*
  985. var dr = dojo.render;
  986. var drh = dojo.render.html;
  987. var drs = dojo.render.svg;
  988. var dua = (drh.UA = navigator.userAgent);
  989. var dav = (drh.AV = navigator.appVersion);
  990. var t = true;
  991. var f = false;
  992. drh.capable = t;
  993. drh.support.builtin = t;
  994. dr.ver = parseFloat(drh.AV);
  995. dr.os.mac = dav.indexOf("Macintosh") >= 0;
  996. dr.os.win = dav.indexOf("Windows") >= 0;
  997. // could also be Solaris or something, but it's the same browser
  998. dr.os.linux = dav.indexOf("X11") >= 0;
  999. drh.opera = dua.indexOf("Opera") >= 0;
  1000. drh.khtml = (dav.indexOf("Konqueror") >= 0)||(dav.indexOf("Safari") >= 0);
  1001. drh.safari = dav.indexOf("Safari") >= 0;
  1002. var geckoPos = dua.indexOf("Gecko");
  1003. drh.mozilla = drh.moz = (geckoPos >= 0)&&(!drh.khtml);
  1004. if (drh.mozilla) {
  1005. // gecko version is YYYYMMDD
  1006. drh.geckoVersion = dua.substring(geckoPos + 6, geckoPos + 14);
  1007. }
  1008. drh.ie = (document.all)&&(!drh.opera);
  1009. drh.ie50 = drh.ie && dav.indexOf("MSIE 5.0")>=0;
  1010. drh.ie55 = drh.ie && dav.indexOf("MSIE 5.5")>=0;
  1011. drh.ie60 = drh.ie && dav.indexOf("MSIE 6.0")>=0;
  1012. drh.ie70 = drh.ie && dav.indexOf("MSIE 7.0")>=0;
  1013. var cm = document["compatMode"];
  1014. drh.quirks = (cm == "BackCompat")||(cm == "QuirksMode")||drh.ie55||drh.ie50;
  1015. // TODO: is the HTML LANG attribute relevant?
  1016. dojo.locale = dojo.locale || (drh.ie ? navigator.userLanguage : navigator.language).toLowerCase();
  1017. dr.vml.capable=drh.ie;
  1018. drs.capable = f;
  1019. drs.support.plugin = f;
  1020. drs.support.builtin = f;
  1021. var tdoc = window["document"];
  1022. var tdi = tdoc["implementation"];
  1023. if((tdi)&&(tdi["hasFeature"])&&(tdi.hasFeature("org.w3c.dom.svg", "1.0"))){
  1024. drs.capable = t;
  1025. drs.support.builtin = t;
  1026. drs.support.plugin = f;
  1027. }
  1028. // webkits after 420 support SVG natively. The test string is "AppleWebKit/420+"
  1029. if(drh.safari){
  1030. var tmp = dua.split("AppleWebKit/")[1];
  1031. var ver = parseFloat(tmp.split(" ")[0]);
  1032. if(ver >= 420){
  1033. drs.capable = t;
  1034. drs.support.builtin = t;
  1035. drs.support.plugin = f;
  1036. }
  1037. }
  1038. })();
  1039. dojo.hostenv.startPackage("dojo.hostenv");
  1040. dojo.render.name = dojo.hostenv.name_ = 'browser';
  1041. dojo.hostenv.searchIds = [];
  1042. // These are in order of decreasing likelihood; this will change in time.
  1043. dojo.hostenv._XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
  1044. dojo.hostenv.getXmlhttpObject = function(){
  1045. var http = null;
  1046. var last_e = null;
  1047. try{ http = new XMLHttpRequest(); }catch(e){}
  1048. if(!http){
  1049. for(var i=0; i<3; ++i){
  1050. var progid = dojo.hostenv._XMLHTTP_PROGIDS[i];
  1051. try{
  1052. http = new ActiveXObject(progid);
  1053. }catch(e){
  1054. last_e = e;
  1055. }
  1056. if(http){
  1057. dojo.hostenv._XMLHTTP_PROGIDS = [progid]; // so faster next time
  1058. break;
  1059. }
  1060. }
  1061. /*if(http && !http.toString) {
  1062. http.toString = function() { "[object XMLHttpRequest]"; }
  1063. }*/
  1064. }
  1065. if(!http){
  1066. return dojo.raise("XMLHTTP not available", last_e);
  1067. }
  1068. return http;
  1069. }
  1070. /**
  1071. * Read the contents of the specified uri and return those contents.
  1072. *
  1073. * @param uri A relative or absolute uri. If absolute, it still must be in the
  1074. * same "domain" as we are.
  1075. *
  1076. * @param async_cb If not specified, load synchronously. If specified, load
  1077. * asynchronously, and use async_cb as the progress handler which takes the
  1078. * xmlhttp object as its argument. If async_cb, this function returns null.
  1079. *
  1080. * @param fail_ok Default false. If fail_ok and !async_cb and loading fails,
  1081. * return null instead of throwing.
  1082. */
  1083. dojo.hostenv._blockAsync = false;
  1084. dojo.hostenv.getText = function(uri, async_cb, fail_ok){
  1085. // need to block async callbacks from snatching this thread as the result
  1086. // of an async callback might call another sync XHR, this hangs khtml forever
  1087. // hostenv._blockAsync must also be checked in BrowserIO's watchInFlight()
  1088. // NOTE: must be declared before scope switches ie. this.getXmlhttpObject()
  1089. if(!async_cb){ this._blockAsync = true; }
  1090. var http = this.getXmlhttpObject();
  1091. function isDocumentOk(http){
  1092. var stat = http["status"];
  1093. // allow a 304 use cache, needed in konq (is this compliant with the http spec?)
  1094. return Boolean((!stat)||((200 <= stat)&&(300 > stat))||(stat==304));
  1095. }
  1096. if(async_cb){
  1097. var _this = this, timer = null, gbl = dojo.global();
  1098. var xhr = dojo.evalObjPath("dojo.io.XMLHTTPTransport");
  1099. http.onreadystatechange = function(){
  1100. if(timer){ gbl.clearTimeout(timer); timer = null; }
  1101. if(_this._blockAsync || (xhr && xhr._blockAsync)){
  1102. timer = gbl.setTimeout(function () { http.onreadystatechange.apply(this); }, 10);
  1103. }else{
  1104. if(4==http.readyState){
  1105. if(isDocumentOk(http)){
  1106. // dojo.debug("LOADED URI: "+uri);
  1107. async_cb(http.responseText);
  1108. }
  1109. }
  1110. }
  1111. }
  1112. }
  1113. http.open('GET', uri, async_cb ? true : false);
  1114. try{
  1115. http.send(null);
  1116. if(async_cb){
  1117. return null;
  1118. }
  1119. if(!isDocumentOk(http)){
  1120. var err = Error("Unable to load "+uri+" status:"+ http.status);
  1121. err.status = http.status;
  1122. err.responseText = http.responseText;
  1123. throw err;
  1124. }
  1125. }catch(e){
  1126. this._blockAsync = false;
  1127. if((fail_ok)&&(!async_cb)){
  1128. return null;
  1129. }else{
  1130. throw e;
  1131. }
  1132. }
  1133. this._blockAsync = false;
  1134. return http.responseText;
  1135. }
  1136. /*
  1137. * It turns out that if we check *right now*, as this script file is being loaded,
  1138. * then the last script element in the window DOM is ourselves.
  1139. * That is because any subsequent script elements haven't shown up in the document
  1140. * object yet.
  1141. */
  1142. /*
  1143. function dj_last_script_src() {
  1144. var scripts = window.document.getElementsByTagName('script');
  1145. if(scripts.length < 1){
  1146. dojo.raise("No script elements in window.document, so can't figure out my script src");
  1147. }
  1148. var script = scripts[scripts.length - 1];
  1149. var src = script.src;
  1150. if(!src){
  1151. dojo.raise("Last script element (out of " + scripts.length + ") has no src");
  1152. }
  1153. return src;
  1154. }
  1155. if(!dojo.hostenv["library_script_uri_"]){
  1156. dojo.hostenv.library_script_uri_ = dj_last_script_src();
  1157. }
  1158. */
  1159. dojo.hostenv.defaultDebugContainerId = 'dojoDebug';
  1160. dojo.hostenv._println_buffer = [];
  1161. dojo.hostenv._println_safe = false;
  1162. dojo.hostenv.println = function (line){
  1163. if(!dojo.hostenv._println_safe){
  1164. dojo.hostenv._println_buffer.push(line);
  1165. }else{
  1166. try {
  1167. var console = document.getElementById(djConfig.debugContainerId ?
  1168. djConfig.debugContainerId : dojo.hostenv.defaultDebugContainerId);
  1169. if(!console) { console = dojo.body(); }
  1170. var div = document.createElement("div");
  1171. div.appendChild(document.createTextNode(line));
  1172. console.appendChild(div);
  1173. } catch (e) {
  1174. try{
  1175. // safari needs the output wrapped in an element for some reason
  1176. document.write("<div>" + line + "</div>");
  1177. }catch(e2){
  1178. window.status = line;
  1179. }
  1180. }
  1181. }
  1182. }
  1183. dojo.addOnLoad(function(){
  1184. dojo.hostenv._println_safe = true;
  1185. while(dojo.hostenv._println_buffer.length > 0){
  1186. dojo.hostenv.println(dojo.hostenv._println_buffer.shift());
  1187. }
  1188. });
  1189. function dj_addNodeEvtHdlr(node, evtName, fp, capture){
  1190. var oldHandler = node["on"+evtName] || function(){};
  1191. node["on"+evtName] = function(){
  1192. fp.apply(node, arguments);
  1193. oldHandler.apply(node, arguments);
  1194. }
  1195. return true;
  1196. }
  1197. // BEGIN DOMContentLoaded, from Dean Edwards (http://dean.edwards.name/weblog/2006/06/again/)
  1198. function dj_load_init(e){
  1199. // allow multiple calls, only first one will take effect
  1200. // A bug in khtml calls events callbacks for document for event which isnt supported
  1201. // for example a created contextmenu event calls DOMContentLoaded, workaround
  1202. var type = (e && e.type) ? e.type.toLowerCase() : "load";
  1203. if(arguments.callee.initialized || (type!="domcontentloaded" && type!="load")){ return; }
  1204. arguments.callee.initialized = true;
  1205. if(typeof(_timer) != 'undefined'){
  1206. clearInterval(_timer);
  1207. delete _timer;
  1208. }
  1209. var initFunc = function(){
  1210. //perform initialization
  1211. if(dojo.render.html.ie){
  1212. dojo.hostenv.makeWidgets();
  1213. }
  1214. };
  1215. if(dojo.hostenv.inFlightCount == 0){
  1216. initFunc();
  1217. dojo.hostenv.modulesLoaded();
  1218. }else{
  1219. dojo.addOnLoad(initFunc);
  1220. }
  1221. }
  1222. // START DOMContentLoaded
  1223. // Mozilla and Opera 9 expose the event we could use
  1224. if(document.addEventListener){
  1225. if(dojo.render.html.opera || (dojo.render.html.moz && !djConfig.delayMozLoadingFix)){
  1226. document.addEventListener("DOMContentLoaded", dj_load_init, null);
  1227. }
  1228. // mainly for Opera 8.5, won't be fired if DOMContentLoaded fired already.
  1229. // also used for Mozilla because of trac #1640
  1230. window.addEventListener("load", dj_load_init, null);
  1231. }
  1232. // for Internet Explorer. readyState will not be achieved on init call, but dojo doesn't need it
  1233. // however, we'll include it because we don't know if there are other functions added that might.
  1234. // Note that this has changed because the build process strips all comments--including conditional
  1235. // ones.
  1236. if(dojo.render.html.ie && dojo.render.os.win){
  1237. document.attachEvent("onreadystatechange", function(e){
  1238. if(document.readyState == "complete"){
  1239. dj_load_init();
  1240. }
  1241. });
  1242. }
  1243. if (/(WebKit|khtml)/i.test(navigator.userAgent)) { // sniff
  1244. var _timer = setInterval(function() {
  1245. if (/loaded|complete/.test(document.readyState)) {
  1246. dj_load_init(); // call the onload handler
  1247. }
  1248. }, 10);
  1249. }
  1250. // END DOMContentLoaded
  1251. // IE WebControl hosted in an application can fire "beforeunload" and "unload"
  1252. // events when control visibility changes, causing Dojo to unload too soon. The
  1253. // following code fixes the problem
  1254. // Reference: http://support.microsoft.com/default.aspx?scid=kb;en-us;199155
  1255. if(dojo.render.html.ie){
  1256. dj_addNodeEvtHdlr(window, "beforeunload", function(){
  1257. dojo.hostenv._unloading = true;
  1258. window.setTimeout(function() {
  1259. dojo.hostenv._unloading = false;
  1260. }, 0);
  1261. });
  1262. }
  1263. dj_addNodeEvtHdlr(window, "unload", function(){
  1264. dojo.hostenv.unloaded();
  1265. if((!dojo.render.html.ie)||(dojo.render.html.ie && dojo.hostenv._unloading)){
  1266. dojo.hostenv.unloaded();
  1267. }
  1268. });
  1269. dojo.hostenv.makeWidgets = function(){
  1270. // you can put searchIds in djConfig and dojo.hostenv at the moment
  1271. // we should probably eventually move to one or the other
  1272. var sids = [];
  1273. if(djConfig.searchIds && djConfig.searchIds.length > 0) {
  1274. sids = sids.concat(djConfig.searchIds);
  1275. }
  1276. if(dojo.hostenv.searchIds && dojo.hostenv.searchIds.length > 0) {
  1277. sids = sids.concat(dojo.hostenv.searchIds);
  1278. }
  1279. if((djConfig.parseWidgets)||(sids.length > 0)){
  1280. if(dojo.evalObjPath("dojo.widget.Parse")){
  1281. // we must do this on a delay to avoid:
  1282. // http://www.shaftek.org/blog/archives/000212.html
  1283. // (IE bug)
  1284. var parser = new dojo.xml.Parse();
  1285. if(sids.length > 0){
  1286. for(var x=0; x<sids.length; x++){
  1287. var tmpNode = document.getElementById(sids[x]);
  1288. if(!tmpNode){ continue; }
  1289. var frag = parser.parseElement(tmpNode, null, true);
  1290. dojo.widget.getParser().createComponents(frag);
  1291. }
  1292. }else if(djConfig.parseWidgets){
  1293. var frag = parser.parseElement(dojo.body(), null, true);
  1294. dojo.widget.getParser().createComponents(frag);
  1295. }
  1296. }
  1297. }
  1298. }
  1299. dojo.addOnLoad(function(){
  1300. if(!dojo.render.html.ie) {
  1301. dojo.hostenv.makeWidgets();
  1302. }
  1303. });
  1304. try {
  1305. if (dojo.render.html.ie) {
  1306. document.namespaces.add("v","urn:schemas-microsoft-com:vml");
  1307. document.createStyleSheet().addRule("v\\:*", "behavior:url(#default#VML)");
  1308. }
  1309. } catch (e) { }
  1310. // stub, over-ridden by debugging code. This will at least keep us from
  1311. // breaking when it's not included
  1312. dojo.hostenv.writeIncludes = function(){}
  1313. //TODOC: HOW TO DOC THIS?
  1314. // @global: dj_currentDocument
  1315. // summary:
  1316. // Current document object. 'dj_currentDocument' can be modified for temporary context shifting.
  1317. // description:
  1318. // dojo.doc() returns dojo.currentDocument.
  1319. // Refer to dojo.doc() rather than referring to 'window.document' to ensure your
  1320. // code runs correctly in managed contexts.
  1321. if(!dj_undef("document", this)){
  1322. dj_currentDocument = this.document;
  1323. }
  1324. dojo.doc = function(){
  1325. // summary:
  1326. // return the document object associated with the dojo.global()
  1327. return dj_currentDocument;
  1328. }
  1329. dojo.body = function(){
  1330. // summary:
  1331. // return the body object associated with dojo.doc()
  1332. // Note: document.body is not defined for a strict xhtml document
  1333. return dojo.doc().body || dojo.doc().getElementsByTagName("body")[0];
  1334. }
  1335. dojo.byId = function(id, doc){
  1336. if((id)&&((typeof id == "string")||(id instanceof String))){
  1337. if (!doc) { doc = dj_currentDocument; }
  1338. var ele = doc.getElementById(id);
  1339. // workaround bug in IE and Opera 8.2 where getElementById returns wrong element
  1340. if (ele && (ele.id != id) && doc.all) {
  1341. ele = null;
  1342. // get all matching elements with this id
  1343. eles = doc.all[id];
  1344. if (eles) {
  1345. // if more than 1, choose first with the correct id
  1346. if (eles.length) {
  1347. for (var i=0; i < eles.length; i++) {
  1348. if (eles[i].id == id) {
  1349. ele = eles[i];
  1350. break;
  1351. }
  1352. }
  1353. // return 1 and only element
  1354. } else { ele = eles; }
  1355. }
  1356. }
  1357. return ele;
  1358. }
  1359. return id; // assume it's a node
  1360. }
  1361. dojo.setContext = function(/*Object*/globalObject, /*Object*/ globalDocument){
  1362. dj_currentContext = globalObject;
  1363. dj_currentDocument = globalDocument;
  1364. };
  1365. dojo._fireCallback = function(callback, context, cbArguments) {
  1366. if((context)&&((typeof callback == "string")||(callback instanceof String))){
  1367. callback=context[callback];
  1368. }
  1369. return (context ? callback.apply(context, cbArguments || [ ]) : callback());
  1370. }
  1371. dojo.withGlobal = function(/*Object*/globalObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments){
  1372. // summary:
  1373. // Call callback with globalObject as dojo.global() and globalObject.document
  1374. // as dojo.doc(). If provided, globalObject will be executed in the context of
  1375. // object thisObject
  1376. // description:
  1377. // When callback() returns or throws an error, the dojo.global() and dojo.doc() will
  1378. // be restored to its previous state.
  1379. var rval;
  1380. var oldGlob = dj_currentContext;
  1381. var oldDoc = dj_currentDocument;
  1382. try{
  1383. dojo.setContext(globalObject, globalObject.document);
  1384. rval = dojo._fireCallback(callback, thisObject, cbArguments);
  1385. }finally{
  1386. dojo.setContext(oldGlob, oldDoc);
  1387. }
  1388. return rval;
  1389. }
  1390. dojo.withDoc = function (/*Object*/documentObject, /*Function*/callback, /*Object?*/thisObject, /*Array?*/cbArguments) {
  1391. // summary:
  1392. // Call callback with documentObject as dojo.doc(). If provided, callback will be executed
  1393. // in the context of object thisObject
  1394. // description:
  1395. // When callback() returns or throws an error, the dojo.doc() will
  1396. // be restored to its previous state.
  1397. var rval;
  1398. var oldDoc = dj_currentDocument;
  1399. try{
  1400. dj_currentDocument = documentObject;
  1401. rval = dojo._fireCallback(callback, thisObject, cbArguments);
  1402. }finally{
  1403. dj_currentDocument = oldDoc;
  1404. }
  1405. return rval;
  1406. }
  1407. } //if (typeof window != 'undefined')
  1408. //Semicolon is for when this file is integrated with a custom build on one line
  1409. //with some other file's contents. Sometimes that makes things not get defined
  1410. //properly, particularly with the using the closure below to do all the work.
  1411. ;(function(){
  1412. //Don't do this work if dojo.js has already done it.
  1413. if(typeof dj_usingBootstrap != "undefined"){
  1414. return;
  1415. }
  1416. var isRhino = false;
  1417. var isSpidermonkey = false;
  1418. var isDashboard = false;
  1419. if((typeof this["load"] == "function")&&((typeof this["Packages"] == "function")||(typeof this["Packages"] == "object"))){
  1420. isRhino = true;
  1421. }else if(typeof this["load"] == "function"){
  1422. isSpidermonkey = true;
  1423. }else if(window.widget){
  1424. isDashboard = true;
  1425. }
  1426. var tmps = [];
  1427. if((this["djConfig"])&&((djConfig["isDebug"])||(djConfig["debugAtAllCosts"]))){
  1428. tmps.push("debug.js");
  1429. }
  1430. if((this["djConfig"])&&(djConfig["debugAtAllCosts"])&&(!isRhino)&&(!isDashboard)){
  1431. tmps.push("browser_debug.js");
  1432. }
  1433. var loaderRoot = djConfig["baseScriptUri"];
  1434. if((this["djConfig"])&&(djConfig["baseLoaderUri"])){
  1435. loaderRoot = djConfig["baseLoaderUri"];
  1436. }
  1437. for(var x=0; x < tmps.length; x++){
  1438. var spath = loaderRoot+"src/"+tmps[x];
  1439. if(isRhino||isSpidermonkey){
  1440. load(spath);
  1441. } else {
  1442. try {
  1443. document.write("<scr"+"ipt type='text/javascript' src='"+spath+"'></scr"+"ipt>");
  1444. } catch (e) {
  1445. var script = document.createElement("script");
  1446. script.src = spath;
  1447. document.getElementsByTagName("head")[0].appendChild(script);
  1448. }
  1449. }
  1450. }
  1451. })();
  1452. dojo.provide("dojo.string.common");
  1453. dojo.string.trim = function(/* string */str, /* integer? */wh){
  1454. // summary
  1455. // Trim whitespace from str. If wh > 0, trim from start, if wh < 0, trim from end, else both
  1456. if(!str.replace){ return str; }
  1457. if(!str.length){ return str; }
  1458. var re = (wh > 0) ? (/^\s+/) : (wh < 0) ? (/\s+$/) : (/^\s+|\s+$/g);
  1459. return str.replace(re, ""); // string
  1460. }
  1461. dojo.string.trimStart = function(/* string */str) {
  1462. // summary
  1463. // Trim whitespace at the beginning of 'str'
  1464. return dojo.string.trim(str, 1); // string
  1465. }
  1466. dojo.string.trimEnd = function(/* string */str) {
  1467. // summary
  1468. // Trim whitespace at the end of 'str'
  1469. return dojo.string.trim(str, -1);
  1470. }
  1471. dojo.string.repeat = function(/* string */str, /* integer */count, /* string? */separator) {
  1472. // summary
  1473. // Return 'str' repeated 'count' times, optionally placing 'separator' between each rep
  1474. var out = "";
  1475. for(var i = 0; i < count; i++) {
  1476. out += str;
  1477. if(separator && i < count - 1) {
  1478. out += separator;
  1479. }
  1480. }
  1481. return out; // string
  1482. }
  1483. dojo.string.pad = function(/* string */str, /* integer */len/*=2*/, /* string */ c/*='0'*/, /* integer */dir/*=1*/) {
  1484. // summary
  1485. // Pad 'str' to guarantee that it is at least 'len' length with the character 'c' at either the
  1486. // start (dir=1) or end (dir=-1) of the string
  1487. var out = String(str);
  1488. if(!c) {
  1489. c = '0';
  1490. }
  1491. if(!dir) {
  1492. dir = 1;
  1493. }
  1494. while(out.length < len) {
  1495. if(dir > 0) {
  1496. out = c + out;
  1497. } else {
  1498. out += c;
  1499. }
  1500. }
  1501. return out; // string
  1502. }
  1503. dojo.string.padLeft = function(/* string */str, /* integer */len, /* string */c) {
  1504. // summary
  1505. // same as dojo.string.pad(str, len, c, 1)
  1506. return dojo.string.pad(str, len, c, 1); // string
  1507. }
  1508. dojo.string.padRight = function(/* string */str, /* integer */len, /* string */c) {
  1509. // summary
  1510. // same as dojo.string.pad(str, len, c, -1)
  1511. return dojo.string.pad(str, len, c, -1); // string
  1512. }
  1513. dojo.provide("dojo.string");
  1514. dojo.provide("dojo.lang.common");
  1515. dojo.lang.inherits = function(/*Function*/ subclass, /*Function*/ superclass){
  1516. // summary: Set up inheritance between two classes.
  1517. if(typeof superclass != 'function'){
  1518. dojo.raise("dojo.inherits: superclass argument ["+superclass+"] must be a function (subclass: ["+subclass+"']");
  1519. }
  1520. subclass.prototype = new superclass();
  1521. subclass.prototype.constructor = subclass;
  1522. subclass.superclass = superclass.prototype;
  1523. // DEPRECATED: super is a reserved word, use 'superclass'
  1524. subclass['super'] = superclass.prototype;
  1525. }
  1526. dojo.lang._mixin = function(/*Object*/ obj, /*Object*/ props){
  1527. // summary: Adds all properties and methods of props to obj.
  1528. var tobj = {};
  1529. for(var x in props){
  1530. // the "tobj" condition avoid copying properties in "props"
  1531. // inherited from Object.prototype. For example, if obj has a custom
  1532. // toString() method, don't overwrite it with the toString() method
  1533. // that props inherited from Object.protoype
  1534. if((typeof tobj[x] == "undefined") || (tobj[x] != props[x])){
  1535. obj[x] = props[x];
  1536. }
  1537. }
  1538. // IE doesn't recognize custom toStrings in for..in
  1539. if(dojo.render.html.ie
  1540. && (typeof(props["toString"]) == "function")
  1541. && (props["toString"] != obj["toString"])
  1542. && (props["toString"] != tobj["toString"]))
  1543. {
  1544. obj.toString = props.toString;
  1545. }
  1546. return obj; // Object
  1547. }
  1548. dojo.lang.mixin = function(/*Object*/ obj, /*Object...*/props){
  1549. // summary: Adds all properties and methods of props to obj.
  1550. for(var i=1, l=arguments.length; i<l; i++){
  1551. dojo.lang._mixin(obj, arguments[i]);
  1552. }
  1553. return obj; // Object
  1554. }
  1555. dojo.lang.extend = function(/*Object*/ constructor, /*Object...*/ props){
  1556. // summary: Adds all properties and methods of props to constructor's prototype,
  1557. // making them available to all instances created with constructor.
  1558. for(var i=1, l=arguments.length; i<l; i++){
  1559. dojo.lang._mixin(constructor.prototype, arguments[i]);
  1560. }
  1561. return constructor; // Object
  1562. }
  1563. // Promote to dojo module
  1564. dojo.inherits = dojo.lang.inherits;
  1565. //dojo.lang._mixin = dojo.lang._mixin;
  1566. dojo.mixin = dojo.lang.mixin;
  1567. dojo.extend = dojo.lang.extend;
  1568. dojo.lang.find = function( /*Array*/ array,
  1569. /*Object*/ value,
  1570. /*Boolean?*/ identity,
  1571. /*Boolean?*/ findLast){
  1572. // summary: Return the index of value in array, returning -1 if not found.
  1573. // identity: If true, matches with identity comparison (===).
  1574. // If false, uses normal comparison (==).
  1575. // findLast: If true, returns index of last instance of value.
  1576. // examples:
  1577. // find(array, value[, identity [findLast]]) // recommended
  1578. // find(value, array[, identity [findLast]]) // deprecated
  1579. // support both (array, value) and (value, array)
  1580. if(!dojo.lang.isArrayLike(array) && dojo.lang.isArrayLike(value)) {
  1581. dojo.deprecated('dojo.lang.find(value, array)', 'use dojo.lang.find(array, value) instead', "0.5");
  1582. var temp = array;
  1583. array = value;
  1584. value = temp;
  1585. }
  1586. var isString = dojo.lang.isString(array);
  1587. if(isString) { array = array.split(""); }
  1588. if(findLast) {
  1589. var step = -1;
  1590. var i = array.length - 1;
  1591. var end = -1;
  1592. } else {
  1593. var step = 1;
  1594. var i = 0;
  1595. var end = array.length;
  1596. }
  1597. if(identity){
  1598. while(i != end) {
  1599. if(array[i] === value){ return i; }
  1600. i += step;
  1601. }
  1602. }else{
  1603. while(i != end) {
  1604. if(array[i] == value){ return i; }
  1605. i += step;
  1606. }
  1607. }
  1608. return -1; // number
  1609. }
  1610. dojo.lang.indexOf = dojo.lang.find;
  1611. dojo.lang.findLast = function(/*Array*/ array, /*Object*/ value, /*boolean?*/ identity){
  1612. // summary: Return index of last occurance of value in array, returning -1 if not found.
  1613. // identity: If true, matches with identity comparison (===). If false, uses normal comparison (==).
  1614. return dojo.lang.find(array, value, identity, true); // number
  1615. }
  1616. dojo.lang.lastIndexOf = dojo.lang.findLast;
  1617. dojo.lang.inArray = function(array /*Array*/, value /*Object*/){
  1618. // summary: Return true if value is present in array.
  1619. return dojo.lang.find(array, value) > -1; // boolean
  1620. }
  1621. /**
  1622. * Partial implmentation of is* functions from
  1623. * http://www.crockford.com/javascript/recommend.html
  1624. * NOTE: some of these may not be the best thing to use in all situations
  1625. * as they aren't part of core JS and therefore can't work in every case.
  1626. * See WARNING messages inline for tips.
  1627. *
  1628. * The following is* functions are fairly "safe"
  1629. */
  1630. dojo.lang.isObject = function(/*anything*/ it){
  1631. // summary: Return true if it is an Object, Array or Function.
  1632. if(typeof it == "undefined"){ return false; }
  1633. return (typeof it == "object" || it === null || dojo.lang.isArray(it) || dojo.lang.isFunction(it)); // Boolean
  1634. }
  1635. dojo.lang.isArray = function(/*anything*/ it){
  1636. // summary: Return true if it is an Array.
  1637. return (it && it instanceof Array || typeof it == "array"); // Boolean
  1638. }
  1639. dojo.lang.isArrayLike = function(/*anything*/ it){
  1640. // summary: Return true if it can be used as an array (i.e. is an object with an integer length property).
  1641. if((!it)||(dojo.lang.isUndefined(it))){ return false; }
  1642. if(dojo.lang.isString(it)){ return false; }
  1643. if(dojo.lang.isFunction(it)){ return false; } // keeps out built-in constructors (Number, String, ...) which have length properties
  1644. if(dojo.lang.isArray(it)){ return true; }
  1645. // form node itself is ArrayLike, but not always iterable. Use form.elements instead.
  1646. if((it.tagName)&&(it.tagName.toLowerCase()=='form')){ return false; }
  1647. if(dojo.lang.isNumber(it.length) && isFinite(it.length)){ return true; }
  1648. return false; // Boolean
  1649. }
  1650. dojo.lang.isFunction = function(/*anything*/ it){
  1651. // summary: Return true if it is a Function.
  1652. if(!it){ return false; }
  1653. // webkit treats NodeList as a function, which is bad
  1654. if((typeof(it) == "function") && (it == "[object NodeList]")) { return false; }
  1655. return (it instanceof Function || typeof it == "function"); // Boolean
  1656. }
  1657. dojo.lang.isString = function(/*anything*/ it){
  1658. // summary: Return true if it is a String.
  1659. return (typeof it == "string" || it instanceof String);
  1660. }
  1661. dojo.lang.isAlien = function(/*anything*/ it){
  1662. // summary: Return true if it is not a built-in function.
  1663. if(!it){ return false; }
  1664. return !dojo.lang.isFunction() && /\{\s*\[native code\]\s*\}/.test(String(it)); // Boolean
  1665. }
  1666. dojo.lang.isBoolean = function(/*anything*/ it){
  1667. // summary: Return true if it is a Boolean.
  1668. return (it instanceof Boolean || typeof it == "boolean"); // Boolean
  1669. }
  1670. /**
  1671. * The following is***() functions are somewhat "unsafe". Fortunately,
  1672. * there are workarounds the the language provides and are mentioned
  1673. * in the WARNING messages.
  1674. *
  1675. */
  1676. dojo.lang.isNumber = function(/*anything*/ it){
  1677. // summary: Return true if it is a number.
  1678. // description:
  1679. // WARNING - In most cases, isNaN(it) is sufficient to determine whether or not
  1680. // something is a number or can be used as such. For example, a number or string
  1681. // can be used interchangably when accessing array items (array["1"] is the same as
  1682. // array[1]) and isNaN will return false for both values ("1" and 1). However,
  1683. // isNumber("1") will return false, which is generally not too useful.
  1684. // Also, isNumber(NaN) returns true, again, this isn't generally useful, but there
  1685. // are corner cases (like when you want to make sure that two things are really
  1686. // the same type of thing). That is really where isNumber "shines".
  1687. //
  1688. // Recommendation - Use isNaN(it) when possible
  1689. return (it instanceof Number || typeof it == "number"); // Boolean
  1690. }
  1691. /*
  1692. * FIXME: Should isUndefined go away since it is error prone?
  1693. */
  1694. dojo.lang.isUndefined = function(/*anything*/ it){
  1695. // summary: Return true if it is not defined.
  1696. // description:
  1697. // WARNING - In some cases, isUndefined will not behave as you
  1698. // might expect. If you do isUndefined(foo) and there is no earlier
  1699. // reference to foo, an error will be thrown before isUndefined is
  1700. // called. It behaves correctly if you scope yor object first, i.e.
  1701. // isUndefined(foo.bar) where foo is an object and bar isn't a
  1702. // property of the object.
  1703. //
  1704. // Recommendation - Use typeof foo == "undefined" when possible
  1705. return ((typeof(it) == "undefined")&&(it == undefined)); // Boolean
  1706. }
  1707. // end Crockford functions
  1708. dojo.provide("dojo.lang.extras");
  1709. dojo.lang.setTimeout = function(/*Function*/func, /*int*/delay /*, ...*/){
  1710. // summary:
  1711. // Sets a timeout in milliseconds to execute a function in a given context
  1712. // with optional arguments.
  1713. //
  1714. // usage:
  1715. // setTimeout (Object context, function func, number delay[, arg1[, ...]]);
  1716. // setTimeout (function func, number delay[, arg1[, ...]]);
  1717. var context = window, argsStart = 2;
  1718. if(!dojo.lang.isFunction(func)){
  1719. context = func;
  1720. func = delay;
  1721. delay = arguments[2];
  1722. argsStart++;
  1723. }
  1724. if(dojo.lang.isString(func)){
  1725. func = context[func];
  1726. }
  1727. var args = [];
  1728. for (var i = argsStart; i < arguments.length; i++){
  1729. args.push(arguments[i]);
  1730. }
  1731. return dojo.global().setTimeout(function () { func.apply(context, args); }, delay); // int
  1732. }
  1733. dojo.lang.clearTimeout = function(/*int*/timer){
  1734. // summary: clears timer by number from the execution queue
  1735. dojo.global().clearTimeout(timer);
  1736. }
  1737. dojo.lang.getNameInObj = function(/*Object*/ns, /*unknown*/item){
  1738. // summary: looks for a value in the object ns with a value matching item and returns the property name
  1739. // ns: if null, dj_global is used
  1740. // item: value to match
  1741. if(!ns){ ns = dj_global; }
  1742. for(var x in ns){
  1743. if(ns[x] === item){
  1744. return new String(x); // String
  1745. }
  1746. }
  1747. return null; // null
  1748. }
  1749. dojo.lang.shallowCopy = function(/*Object*/obj, /*Boolean?*/deep){
  1750. // summary: copies object obj one level deep, or full depth if deep is true
  1751. var i, ret;
  1752. if(obj === null){ /*obj: null*/ return null; } // null
  1753. if(dojo.lang.isObject(obj)){
  1754. // obj: Object
  1755. ret = new obj.constructor();
  1756. for(i in obj){
  1757. if(dojo.lang.isUndefined(ret[i])){
  1758. ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
  1759. }
  1760. }
  1761. } else if(dojo.lang.isArray(obj)){
  1762. // obj: Array
  1763. ret = [];
  1764. for(i=0; i<obj.length; i++){
  1765. ret[i] = deep ? dojo.lang.shallowCopy(obj[i], deep) : obj[i];
  1766. }
  1767. } else {
  1768. // obj: unknown
  1769. ret = obj;
  1770. }
  1771. return ret; // unknown
  1772. }
  1773. dojo.lang.firstValued = function(/* ... */){
  1774. // summary: Return the first argument that isn't undefined
  1775. for(var i = 0; i < arguments.length; i++){
  1776. if(typeof arguments[i] != "undefined"){
  1777. return arguments[i]; // unknown
  1778. }
  1779. }
  1780. return undefined; // undefined
  1781. }
  1782. dojo.lang.getObjPathValue = function(/*String*/objpath, /*Object?*/context, /*Boolean?*/create){
  1783. // summary:
  1784. // Gets a value from a reference specified as a string descriptor,
  1785. // (e.g. "A.B") in the given context.
  1786. //
  1787. // context: if not specified, dj_global is used
  1788. // create: if true, undefined objects in the path are created.
  1789. with(dojo.parseObjPath(objpath, context, create)){
  1790. return dojo.evalProp(prop, obj, create); // unknown
  1791. }
  1792. }
  1793. dojo.lang.setObjPathValue = function(/*String*/objpath, /*unknown*/value, /*Object?*/context, /*Boolean?*/create){
  1794. // summary:
  1795. // Sets a value on a reference specified as a string descriptor.
  1796. // (e.g. "A.B") in the given context.
  1797. //
  1798. // context: if not specified, dj_global is used
  1799. // create: if true, undefined objects in the path are created.
  1800. if(arguments.length < 4){
  1801. create = true;
  1802. }
  1803. with(dojo.parseObjPath(objpath, context, create)){
  1804. if(obj && (create || (prop in obj))){
  1805. obj[prop] = value;
  1806. }
  1807. }
  1808. }
  1809. dojo.provide("dojo.io.common");
  1810. /******************************************************************************
  1811. * Notes about dojo.io design:
  1812. *
  1813. * The dojo.io.* package has the unenviable task of making a lot of different
  1814. * types of I/O feel natural, despite a universal lack of good (or even
  1815. * reasonable!) I/O capability in the host environment. So lets pin this down
  1816. * a little bit further.
  1817. *
  1818. * Rhino:
  1819. * perhaps the best situation anywhere. Access to Java classes allows you
  1820. * to do anything one might want in terms of I/O, both synchronously and
  1821. * async. Can open TCP sockets and perform low-latency client/server
  1822. * interactions. HTTP transport is available through Java HTTP client and
  1823. * server classes. Wish it were always this easy.
  1824. *
  1825. * xpcshell:
  1826. * XPCOM for I/O.
  1827. *
  1828. * spidermonkey:
  1829. * S.O.L.
  1830. *
  1831. * Browsers:
  1832. * Browsers generally do not provide any useable filesystem access. We are
  1833. * therefore limited to HTTP for moving information to and from Dojo
  1834. * instances living in a browser.
  1835. *
  1836. * XMLHTTP:
  1837. * Sync or async, allows reading of arbitrary text files (including
  1838. * JS, which can then be eval()'d), writing requires server
  1839. * cooperation and is limited to HTTP mechanisms (POST and GET).
  1840. *
  1841. * <iframe> hacks:
  1842. * iframe document hacks allow browsers to communicate asynchronously
  1843. * with a server via HTTP POST and GET operations. With significant
  1844. * effort and server cooperation, low-latency data transit between
  1845. * client and server can be acheived via iframe mechanisms (repubsub).
  1846. *
  1847. * SVG:
  1848. * Adobe's SVG viewer implements helpful primitives for XML-based
  1849. * requests, but receipt of arbitrary text data seems unlikely w/o
  1850. * <![CDATA[]]> sections.
  1851. *
  1852. *
  1853. * A discussion between Dylan, Mark, Tom, and Alex helped to lay down a lot
  1854. * the IO API interface. A transcript of it can be found at:
  1855. * http://dojotoolkit.org/viewcvs/viewcvs.py/documents/irc/irc_io_api_log.txt?rev=307&view=auto
  1856. *
  1857. * Also referenced in the design of the API was the DOM 3 L&S spec:
  1858. * http://www.w3.org/TR/2004/REC-DOM-Level-3-LS-20040407/load-save.html
  1859. ******************************************************************************/
  1860. // a map of the available transport options. Transports should add themselves
  1861. // by calling add(name)
  1862. dojo.io.transports = [];
  1863. dojo.io.hdlrFuncNames = [ "load", "error", "timeout" ]; // we're omitting a progress() event for now
  1864. dojo.io.Request = function(/*String*/ url, /*String*/ mimetype, /*String*/ transport, /*String or Boolean*/ changeUrl){
  1865. // summary:
  1866. // Constructs a Request object that is used by dojo.io.bind(). dojo.io.bind() will create one of these for you if
  1867. // you call dojo.io.bind() with an plain object containing the bind parameters.
  1868. // This method can either take the arguments specified, or an Object containing all of the parameters that you
  1869. // want to use to create the dojo.io.Request (similar to how dojo.io.bind() is called.
  1870. // The named parameters to this constructor represent the minimum set of parameters need
  1871. if((arguments.length == 1)&&(arguments[0].constructor == Object)){
  1872. this.fromKwArgs(arguments[0]);
  1873. }else{
  1874. this.url = url;
  1875. if(mimetype){ this.mimetype = mimetype; }
  1876. if(transport){ this.transport = transport; }
  1877. if(arguments.length >= 4){ this.changeUrl = changeUrl; }
  1878. }
  1879. }
  1880. dojo.lang.extend(dojo.io.Request, {
  1881. /** The URL to hit */
  1882. url: "",
  1883. /** The mime type used to interrpret the response body */
  1884. mimetype: "text/plain",
  1885. /** The HTTP method to use */
  1886. method: "GET",
  1887. /** An Object containing key-value pairs to be included with the request */
  1888. content: undefined, // Object
  1889. /** The transport medium to use */
  1890. transport: undefined, // String
  1891. /** If defined the URL of the page is physically changed */
  1892. changeUrl: undefined, // String
  1893. /** A form node to use in the request */
  1894. formNode: undefined, // HTMLFormElement
  1895. /** Whether the request should be made synchronously */
  1896. sync: false,
  1897. bindSuccess: false,
  1898. /** Cache/look for the request in the cache before attempting to request?
  1899. * NOTE: this isn't a browser cache, this is internal and would only cache in-page
  1900. */
  1901. useCache: false,
  1902. /** Prevent the browser from caching this by adding a query string argument to the URL */
  1903. preventCache: false,
  1904. // events stuff
  1905. load: function(/*String*/ type, /*Object*/ data, /*Object*/ transportImplementation, /*Object*/ kwArgs){
  1906. // summary:
  1907. // Called on successful completion of a bind.
  1908. // type:
  1909. // A string with value "load"
  1910. // data:
  1911. // The object representing the result of the bind. The actual structure
  1912. // of the data object will depend on the mimetype that was given to bind
  1913. // in the bind arguments.
  1914. // transportImplementation:
  1915. // The object that implements a particular transport. Structure is depedent
  1916. // on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
  1917. // XMLHttpRequest object from the browser.
  1918. // kwArgs:
  1919. // Object that contains the request parameters that were given to the
  1920. // bind call. Useful for storing and retrieving state from when bind
  1921. // was called.
  1922. },
  1923. error: function(/*String*/ type, /*Object*/ error, /*Object*/ transportImplementation, /*Object*/ kwArgs){
  1924. // summary:
  1925. // Called when there is an error with a bind.
  1926. // type:
  1927. // A string with value "error"
  1928. // error:
  1929. // The error object. Should be a dojo.io.Error object, but not guaranteed.
  1930. // transportImplementation:
  1931. // The object that implements a particular transport. Structure is depedent
  1932. // on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
  1933. // XMLHttpRequest object from the browser.
  1934. // kwArgs:
  1935. // Object that contains the request parameters that were given to the
  1936. // bind call. Useful for storing and retrieving state from when bind
  1937. // was called.
  1938. },
  1939. timeout: function(/*String*/ type, /*Object*/ empty, /*Object*/ transportImplementation, /*Object*/ kwArgs){
  1940. // summary:
  1941. // Called when there is an error with a bind. Only implemented in certain transports at this time.
  1942. // type:
  1943. // A string with value "timeout"
  1944. // empty:
  1945. // Should be null. Just a spacer argument so that load, error, timeout and handle have the
  1946. // same signatures.
  1947. // transportImplementation:
  1948. // The object that implements a particular transport. Structure is depedent
  1949. // on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
  1950. // XMLHttpRequest object from the browser. May be null for the timeout case for
  1951. // some transports.
  1952. // kwArgs:
  1953. // Object that contains the request parameters that were given to the
  1954. // bind call. Useful for storing and retrieving state from when bind
  1955. // was called.
  1956. },
  1957. handle: function(/*String*/ type, /*Object*/ data, /*Object*/ transportImplementation, /*Object*/ kwArgs){
  1958. // summary:
  1959. // The handle method can be defined instead of defining separate load, error and timeout
  1960. // callbacks.
  1961. // type:
  1962. // A string with the type of callback: "load", "error", or "timeout".
  1963. // data:
  1964. // See the above callbacks for what this parameter could be.
  1965. // transportImplementation:
  1966. // The object that implements a particular transport. Structure is depedent
  1967. // on the transport. For XMLHTTPTransport (dojo.io.BrowserIO), it will be the
  1968. // XMLHttpRequest object from the browser.
  1969. // kwArgs:
  1970. // Object that contains the request parameters that were given to the
  1971. // bind call. Useful for storing and retrieving state from when bind
  1972. // was called.
  1973. },
  1974. //FIXME: change IframeIO.js to use timeouts?
  1975. // The number of seconds to wait until firing a timeout callback.
  1976. // If it is zero, that means, don't do a timeout check.
  1977. timeoutSeconds: 0,
  1978. // the abort method needs to be filled in by the transport that accepts the
  1979. // bind() request
  1980. abort: function(){ },
  1981. // backButton: function(){ },
  1982. // forwardButton: function(){ },
  1983. fromKwArgs: function(/*Object*/ kwArgs){
  1984. // summary:
  1985. // Creates a dojo.io.Request from a simple object (kwArgs object).
  1986. // normalize args
  1987. if(kwArgs["url"]){ kwArgs.url = kwArgs.url.toString(); }
  1988. if(kwArgs["formNode"]) { kwArgs.formNode = dojo.byId(kwArgs.formNode); }
  1989. if(!kwArgs["method"] && kwArgs["formNode"] && kwArgs["formNode"].method) {
  1990. kwArgs.method = kwArgs["formNode"].method;
  1991. }
  1992. // backwards compatibility
  1993. if(!kwArgs["handle"] && kwArgs["handler"]){ kwArgs.handle = kwArgs.handler; }
  1994. if(!kwArgs["load"] && kwArgs["loaded"]){ kwArgs.load = kwArgs.loaded; }
  1995. if(!kwArgs["changeUrl"] && kwArgs["changeURL"]) { kwArgs.changeUrl = kwArgs.changeURL; }
  1996. // encoding fun!
  1997. kwArgs.encoding = dojo.lang.firstValued(kwArgs["encoding"], djConfig["bindEncoding"], "");
  1998. kwArgs.sendTransport = dojo.lang.firstValued(kwArgs["sendTransport"], djConfig["ioSendTransport"], false);
  1999. var isFunction = dojo.lang.isFunction;
  2000. for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
  2001. var fn = dojo.io.hdlrFuncNames[x];
  2002. if(kwArgs[fn] && isFunction(kwArgs[fn])){ continue; }
  2003. if(kwArgs["handle"] && isFunction(kwArgs["handle"])){
  2004. kwArgs[fn] = kwArgs.handle;
  2005. }
  2006. // handler is aliased above, shouldn't need this check
  2007. /* else if(dojo.lang.isObject(kwArgs.handler)){
  2008. if(isFunction(kwArgs.handler[fn])){
  2009. kwArgs[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"]||function(){};
  2010. }
  2011. }*/
  2012. }
  2013. dojo.lang.mixin(this, kwArgs);
  2014. }
  2015. });
  2016. dojo.io.Error = function(/*String*/ msg, /*String*/ type, /*Number*/num){
  2017. // summary:
  2018. // Constructs an object representing a bind error.
  2019. this.message = msg;
  2020. this.type = type || "unknown"; // must be one of "io", "parse", "unknown"
  2021. this.number = num || 0; // per-substrate error number, not normalized
  2022. }
  2023. dojo.io.transports.addTransport = function(name){
  2024. // summary:
  2025. // Used to register transports that can support bind calls.
  2026. this.push(name);
  2027. // FIXME: do we need to handle things that aren't direct children of the
  2028. // dojo.io module? (say, dojo.io.foo.fooTransport?)
  2029. this[name] = dojo.io[name];
  2030. }
  2031. // binding interface, the various implementations register their capabilities
  2032. // and the bind() method dispatches
  2033. dojo.io.bind = function(/*Object*/ request){
  2034. // summary:
  2035. // Binding interface for IO. Loading different IO transports, like
  2036. // dojo.io.BrowserIO or dojo.io.IframeIO will register with bind
  2037. // to handle particular types of bind calls.
  2038. // request:
  2039. // Object containing bind arguments. This object is converted to
  2040. // a dojo.io.Request object, and that request object is the return
  2041. // value for this method.
  2042. if(!(request instanceof dojo.io.Request)){
  2043. try{
  2044. request = new dojo.io.Request(request);
  2045. }catch(e){ dojo.debug(e); }
  2046. }
  2047. // if the request asks for a particular implementation, use it
  2048. var tsName = "";
  2049. if(request["transport"]){
  2050. tsName = request["transport"];
  2051. if(!this[tsName]){
  2052. dojo.io.sendBindError(request, "No dojo.io.bind() transport with name '"
  2053. + request["transport"] + "'.");
  2054. return request; //dojo.io.Request
  2055. }
  2056. if(!this[tsName].canHandle(request)){
  2057. dojo.io.sendBindError(request, "dojo.io.bind() transport with name '"
  2058. + request["transport"] + "' cannot handle this type of request.");
  2059. return request; //dojo.io.Request
  2060. }
  2061. }else{
  2062. // otherwise we do our best to auto-detect what available transports
  2063. // will handle
  2064. for(var x=0; x<dojo.io.transports.length; x++){
  2065. var tmp = dojo.io.transports[x];
  2066. if((this[tmp])&&(this[tmp].canHandle(request))){
  2067. tsName = tmp;
  2068. break;
  2069. }
  2070. }
  2071. if(tsName == ""){
  2072. dojo.io.sendBindError(request, "None of the loaded transports for dojo.io.bind()"
  2073. + " can handle the request.");
  2074. return request; //dojo.io.Request
  2075. }
  2076. }
  2077. this[tsName].bind(request);
  2078. request.bindSuccess = true;
  2079. return request; //dojo.io.Request
  2080. }
  2081. dojo.io.sendBindError = function(request /* Object */, message /* String */){
  2082. // summary:
  2083. // Used internally by dojo.io.bind() to return/raise a bind error.
  2084. //Need to be careful since not all hostenvs support setTimeout.
  2085. if((typeof request.error == "function" || typeof request.handle == "function")
  2086. && (typeof setTimeout == "function" || typeof setTimeout == "object")){
  2087. var errorObject = new dojo.io.Error(message);
  2088. setTimeout(function(){
  2089. request[(typeof request.error == "function") ? "error" : "handle"]("error", errorObject, null, request);
  2090. }, 50);
  2091. }else{
  2092. dojo.raise(message);
  2093. }
  2094. }
  2095. dojo.io.queueBind = function(/* Object */ request){
  2096. // summary:
  2097. // queueBind will use dojo.io.bind() but guarantee that only one bind
  2098. // call is handled at a time. If queueBind is called while a bind call
  2099. // is in process, it will queue up the other calls to bind and call them
  2100. // in order as bind calls complete.
  2101. // request:
  2102. // Same sort of request object as used for dojo.io.bind().
  2103. if(!(request instanceof dojo.io.Request)){
  2104. try{
  2105. request = new dojo.io.Request(request);
  2106. }catch(e){ dojo.debug(e); }
  2107. }
  2108. // make sure we get called if/when we get a response
  2109. var oldLoad = request.load;
  2110. request.load = function(){
  2111. dojo.io._queueBindInFlight = false;
  2112. var ret = oldLoad.apply(this, arguments);
  2113. dojo.io._dispatchNextQueueBind();
  2114. return ret;
  2115. }
  2116. var oldErr = request.error;
  2117. request.error = function(){
  2118. dojo.io._queueBindInFlight = false;
  2119. var ret = oldErr.apply(this, arguments);
  2120. dojo.io._dispatchNextQueueBind();
  2121. return ret;
  2122. }
  2123. dojo.io._bindQueue.push(request);
  2124. dojo.io._dispatchNextQueueBind();
  2125. return request; //dojo.io.Request
  2126. }
  2127. dojo.io._dispatchNextQueueBind = function(){
  2128. // summary:
  2129. // Private method used by dojo.io.queueBind().
  2130. if(!dojo.io._queueBindInFlight){
  2131. dojo.io._queueBindInFlight = true;
  2132. if(dojo.io._bindQueue.length > 0){
  2133. dojo.io.bind(dojo.io._bindQueue.shift());
  2134. }else{
  2135. dojo.io._queueBindInFlight = false;
  2136. }
  2137. }
  2138. }
  2139. dojo.io._bindQueue = [];
  2140. dojo.io._queueBindInFlight = false;
  2141. dojo.io.argsFromMap = function(/*Object*/ map, /*String*/ encoding, /*String*/ last){
  2142. // summary:
  2143. // Converts name/values pairs in the map object to an URL-encoded string
  2144. // with format of name1=value1&name2=value2...
  2145. // map:
  2146. // Object that has the contains the names and values.
  2147. // encoding:
  2148. // String to specify how to encode the name and value. If the encoding string
  2149. // contains "utf" (case-insensitive), then encodeURIComponent is used. Otherwise
  2150. // dojo.string.encodeAscii is used.
  2151. // last:
  2152. // The last parameter in the list. Helps with final string formatting?
  2153. var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
  2154. var mapped = [];
  2155. var control = new Object();
  2156. for(var name in map){
  2157. var domap = function(elt){
  2158. var val = enc(name)+"="+enc(elt);
  2159. mapped[(last == name) ? "push" : "unshift"](val);
  2160. }
  2161. if(!control[name]){
  2162. var value = map[name];
  2163. // FIXME: should be isArrayLike?
  2164. if (dojo.lang.isArray(value)){
  2165. dojo.lang.forEach(value, domap);
  2166. }else{
  2167. domap(value);
  2168. }
  2169. }
  2170. }
  2171. return mapped.join("&"); //String
  2172. }
  2173. dojo.io.setIFrameSrc = function(/*DOMNode*/ iframe, /*String*/ src, /*Boolean*/ replace){
  2174. //summary:
  2175. // Sets the URL that is loaded in an IFrame. The replace parameter indicates whether
  2176. // location.replace() should be used when changing the location of the iframe.
  2177. try{
  2178. var r = dojo.render.html;
  2179. // dojo.debug(iframe);
  2180. if(!replace){
  2181. if(r.safari){
  2182. iframe.location = src;
  2183. }else{
  2184. frames[iframe.name].location = src;
  2185. }
  2186. }else{
  2187. // Fun with DOM 0 incompatibilities!
  2188. var idoc;
  2189. if(r.ie){
  2190. idoc = iframe.contentWindow.document;
  2191. }else if(r.safari){
  2192. idoc = iframe.document;
  2193. }else{ // if(r.moz){
  2194. idoc = iframe.contentWindow;
  2195. }
  2196. //For Safari (at least 2.0.3) and Opera, if the iframe
  2197. //has just been created but it doesn't have content
  2198. //yet, then iframe.document may be null. In that case,
  2199. //use iframe.location and return.
  2200. if(!idoc){
  2201. iframe.location = src;
  2202. return;
  2203. }else{
  2204. idoc.location.replace(src);
  2205. }
  2206. }
  2207. }catch(e){
  2208. dojo.debug(e);
  2209. dojo.debug("setIFrameSrc: "+e);
  2210. }
  2211. }
  2212. /*
  2213. dojo.io.sampleTranport = new function(){
  2214. this.canHandle = function(kwArgs){
  2215. // canHandle just tells dojo.io.bind() if this is a good transport to
  2216. // use for the particular type of request.
  2217. if(
  2218. (
  2219. (kwArgs["mimetype"] == "text/plain") ||
  2220. (kwArgs["mimetype"] == "text/html") ||
  2221. (kwArgs["mimetype"] == "text/javascript")
  2222. )&&(
  2223. (kwArgs["method"] == "get") ||
  2224. ( (kwArgs["method"] == "post") && (!kwArgs["formNode"]) )
  2225. )
  2226. ){
  2227. return true;
  2228. }
  2229. return false;
  2230. }
  2231. this.bind = function(kwArgs){
  2232. var hdlrObj = {};
  2233. // set up a handler object
  2234. for(var x=0; x<dojo.io.hdlrFuncNames.length; x++){
  2235. var fn = dojo.io.hdlrFuncNames[x];
  2236. if(typeof kwArgs.handler == "object"){
  2237. if(typeof kwArgs.handler[fn] == "function"){
  2238. hdlrObj[fn] = kwArgs.handler[fn]||kwArgs.handler["handle"];
  2239. }
  2240. }else if(typeof kwArgs[fn] == "function"){
  2241. hdlrObj[fn] = kwArgs[fn];
  2242. }else{
  2243. hdlrObj[fn] = kwArgs["handle"]||function(){};
  2244. }
  2245. }
  2246. // build a handler function that calls back to the handler obj
  2247. var hdlrFunc = function(evt){
  2248. if(evt.type == "onload"){
  2249. hdlrObj.load("load", evt.data, evt);
  2250. }else if(evt.type == "onerr"){
  2251. var errObj = new dojo.io.Error("sampleTransport Error: "+evt.msg);
  2252. hdlrObj.error("error", errObj);
  2253. }
  2254. }
  2255. // the sample transport would attach the hdlrFunc() when sending the
  2256. // request down the pipe at this point
  2257. var tgtURL = kwArgs.url+"?"+dojo.io.argsFromMap(kwArgs.content);
  2258. // sampleTransport.sendRequest(tgtURL, hdlrFunc);
  2259. }
  2260. dojo.io.transports.addTransport("sampleTranport");
  2261. }
  2262. */
  2263. dojo.provide("dojo.lang.array");
  2264. // FIXME: Is this worthless since you can do: if(name in obj)
  2265. // is this the right place for this?
  2266. dojo.lang.has = function(/*Object*/obj, /*String*/name){
  2267. try{
  2268. return typeof obj[name] != "undefined";
  2269. }catch(e){ return false; }
  2270. }
  2271. dojo.lang.isEmpty = function(/*Object*/obj){
  2272. if(dojo.lang.isObject(obj)){
  2273. var tmp = {};
  2274. var count = 0;
  2275. for(var x in obj){
  2276. if(obj[x] && (!tmp[x])){
  2277. count++;
  2278. break;
  2279. }
  2280. }
  2281. return count == 0;
  2282. }else if(dojo.lang.isArrayLike(obj) || dojo.lang.isString(obj)){
  2283. return obj.length == 0;
  2284. }
  2285. }
  2286. dojo.lang.map = function(/*Array*/arr, /*Object|Function*/obj, /*Function?*/unary_func){
  2287. var isString = dojo.lang.isString(arr);
  2288. if(isString){
  2289. // arr: String
  2290. arr = arr.split("");
  2291. }
  2292. if(dojo.lang.isFunction(obj)&&(!unary_func)){
  2293. unary_func = obj;
  2294. obj = dj_global;
  2295. }else if(dojo.lang.isFunction(obj) && unary_func){
  2296. // ff 1.5 compat
  2297. var tmpObj = obj;
  2298. obj = unary_func;
  2299. unary_func = tmpObj;
  2300. }
  2301. if(Array.map){
  2302. var outArr = Array.map(arr, unary_func, obj);
  2303. }else{
  2304. var outArr = [];
  2305. for(var i=0;i<arr.length;++i){
  2306. outArr.push(unary_func.call(obj, arr[i]));
  2307. }
  2308. }
  2309. if(isString) {
  2310. return outArr.join(""); // String
  2311. } else {
  2312. return outArr; // Array
  2313. }
  2314. }
  2315. dojo.lang.reduce = function(/*Array*/arr, initialValue, /*Object|null*/obj, /*Function*/binary_func){
  2316. var reducedValue = initialValue;
  2317. var ob = obj ? obj : dj_global;
  2318. dojo.lang.map(arr,
  2319. function(val){
  2320. reducedValue = binary_func.call(ob, reducedValue, val);
  2321. }
  2322. );
  2323. return reducedValue;
  2324. }
  2325. // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:Array:forEach
  2326. dojo.lang.forEach = function(/*Array*/anArray, /*Function*/callback, /*Object?*/thisObject){
  2327. if(dojo.lang.isString(anArray)){
  2328. // anArray: String
  2329. anArray = anArray.split("");
  2330. }
  2331. if(Array.forEach){
  2332. Array.forEach(anArray, callback, thisObject);
  2333. }else{
  2334. // FIXME: there are several ways of handilng thisObject. Is dj_global always the default context?
  2335. if(!thisObject){
  2336. thisObject=dj_global;
  2337. }
  2338. for(var i=0,l=anArray.length; i<l; i++){
  2339. callback.call(thisObject, anArray[i], i, anArray);
  2340. }
  2341. }
  2342. }
  2343. dojo.lang._everyOrSome = function(/*Boolean*/every, /*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
  2344. if(dojo.lang.isString(arr)){
  2345. //arr: String
  2346. arr = arr.split("");
  2347. }
  2348. if(Array.every){
  2349. return Array[ every ? "every" : "some" ](arr, callback, thisObject);
  2350. }else{
  2351. if(!thisObject){
  2352. thisObject = dj_global;
  2353. }
  2354. for(var i=0,l=arr.length; i<l; i++){
  2355. var result = callback.call(thisObject, arr[i], i, arr);
  2356. if(every && !result){
  2357. return false; // Boolean
  2358. }else if((!every)&&(result)){
  2359. return true; // Boolean
  2360. }
  2361. }
  2362. return Boolean(every); // Boolean
  2363. }
  2364. }
  2365. dojo.lang.every = function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
  2366. return this._everyOrSome(true, arr, callback, thisObject); // Boolean
  2367. }
  2368. dojo.lang.some = function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
  2369. return this._everyOrSome(false, arr, callback, thisObject); // Boolean
  2370. }
  2371. dojo.lang.filter = function(/*Array*/arr, /*Function*/callback, /*Object?*/thisObject){
  2372. var isString = dojo.lang.isString(arr);
  2373. if(isString){ /*arr: String*/arr = arr.split(""); }
  2374. var outArr;
  2375. if(Array.filter){
  2376. outArr = Array.filter(arr, callback, thisObject);
  2377. } else {
  2378. if(!thisObject){
  2379. if(arguments.length >= 3){ dojo.raise("thisObject doesn't exist!"); }
  2380. thisObject = dj_global;
  2381. }
  2382. outArr = [];
  2383. for(var i = 0; i < arr.length; i++){
  2384. if(callback.call(thisObject, arr[i], i, arr)){
  2385. outArr.push(arr[i]);
  2386. }
  2387. }
  2388. }
  2389. if(isString){
  2390. return outArr.join(""); // String
  2391. } else {
  2392. return outArr; // Array
  2393. }
  2394. }
  2395. dojo.lang.unnest = function(/* ... */){
  2396. // summary:
  2397. // Creates a 1-D array out of all the arguments passed,
  2398. // unravelling any array-like objects in the process
  2399. //
  2400. // usage:
  2401. // unnest(1, 2, 3) ==> [1, 2, 3]
  2402. // unnest(1, [2, [3], [[[4]]]]) ==> [1, 2, 3, 4]
  2403. var out = [];
  2404. for(var i = 0; i < arguments.length; i++){
  2405. if(dojo.lang.isArrayLike(arguments[i])){
  2406. var add = dojo.lang.unnest.apply(this, arguments[i]);
  2407. out = out.concat(add);
  2408. }else{
  2409. out.push(arguments[i]);
  2410. }
  2411. }
  2412. return out; // Array
  2413. }
  2414. dojo.lang.toArray = function(/*Object*/arrayLike, /*Number*/startOffset){
  2415. // summary:
  2416. // Converts an array-like object (i.e. arguments, DOMCollection)
  2417. // to an array
  2418. var array = [];
  2419. for(var i = startOffset||0; i < arrayLike.length; i++){
  2420. array.push(arrayLike[i]);
  2421. }
  2422. return array; // Array
  2423. }
  2424. dojo.provide("dojo.lang.func");
  2425. /**
  2426. * Runs a function in a given scope (thisObject), can
  2427. * also be used to preserve scope.
  2428. *
  2429. * hitch(foo, "bar"); // runs foo.bar() in the scope of foo
  2430. * hitch(foo, myFunction); // runs myFunction in the scope of foo
  2431. */
  2432. dojo.lang.hitch = function(thisObject, method){
  2433. var fcn = (dojo.lang.isString(method) ? thisObject[method] : method) || function(){};
  2434. return function() {
  2435. return fcn.apply(thisObject, arguments);
  2436. };
  2437. }
  2438. dojo.lang.anonCtr = 0;
  2439. dojo.lang.anon = {};
  2440. dojo.lang.nameAnonFunc = function(anonFuncPtr, namespaceObj, searchForNames){
  2441. var nso = (namespaceObj || dojo.lang.anon);
  2442. if( (searchForNames) ||
  2443. ((dj_global["djConfig"])&&(djConfig["slowAnonFuncLookups"] == true)) ){
  2444. for(var x in nso){
  2445. try{
  2446. if(nso[x] === anonFuncPtr){
  2447. return x;
  2448. }
  2449. }catch(e){} // window.external fails in IE embedded in Eclipse (Eclipse bug #151165)
  2450. }
  2451. }
  2452. var ret = "__"+dojo.lang.anonCtr++;
  2453. while(typeof nso[ret] != "undefined"){
  2454. ret = "__"+dojo.lang.anonCtr++;
  2455. }
  2456. nso[ret] = anonFuncPtr;
  2457. return ret;
  2458. }
  2459. dojo.lang.forward = function(funcName){
  2460. // Returns a function that forwards a method call to this.func(...)
  2461. return function(){
  2462. return this[funcName].apply(this, arguments);
  2463. };
  2464. }
  2465. dojo.lang.curry = function(ns, func /* args ... */){
  2466. var outerArgs = [];
  2467. ns = ns||dj_global;
  2468. if(dojo.lang.isString(func)){
  2469. func = ns[func];
  2470. }
  2471. for(var x=2; x<arguments.length; x++){
  2472. outerArgs.push(arguments[x]);
  2473. }
  2474. // since the event system replaces the original function with a new
  2475. // join-point runner with an arity of 0, we check to see if it's left us
  2476. // any clues about the original arity in lieu of the function's actual
  2477. // length property
  2478. var ecount = (func["__preJoinArity"]||func.length) - outerArgs.length;
  2479. // borrowed from svend tofte
  2480. function gather(nextArgs, innerArgs, expected){
  2481. var texpected = expected;
  2482. var totalArgs = innerArgs.slice(0); // copy
  2483. for(var x=0; x<nextArgs.length; x++){
  2484. totalArgs.push(nextArgs[x]);
  2485. }
  2486. // check the list of provided nextArgs to see if it, plus the
  2487. // number of innerArgs already supplied, meets the total
  2488. // expected.
  2489. expected = expected-nextArgs.length;
  2490. if(expected<=0){
  2491. var res = func.apply(ns, totalArgs);
  2492. expected = texpected;
  2493. return res;
  2494. }else{
  2495. return function(){
  2496. return gather(arguments,// check to see if we've been run
  2497. // with enough args
  2498. totalArgs, // a copy
  2499. expected); // how many more do we need to run?;
  2500. };
  2501. }
  2502. }
  2503. return gather([], outerArgs, ecount);
  2504. }
  2505. dojo.lang.curryArguments = function(ns, func, args, offset){
  2506. var targs = [];
  2507. var x = offset||0;
  2508. for(x=offset; x<args.length; x++){
  2509. targs.push(args[x]); // ensure that it's an arr
  2510. }
  2511. return dojo.lang.curry.apply(dojo.lang, [ns, func].concat(targs));
  2512. }
  2513. dojo.lang.tryThese = function(){
  2514. for(var x=0; x<arguments.length; x++){
  2515. try{
  2516. if(typeof arguments[x] == "function"){
  2517. var ret = (arguments[x]());
  2518. if(ret){
  2519. return ret;
  2520. }
  2521. }
  2522. }catch(e){
  2523. dojo.debug(e);
  2524. }
  2525. }
  2526. }
  2527. dojo.lang.delayThese = function(farr, cb, delay, onend){
  2528. /**
  2529. * alternate: (array funcArray, function callback, function onend)
  2530. * alternate: (array funcArray, function callback)
  2531. * alternate: (array funcArray)
  2532. */
  2533. if(!farr.length){
  2534. if(typeof onend == "function"){
  2535. onend();
  2536. }
  2537. return;
  2538. }
  2539. if((typeof delay == "undefined")&&(typeof cb == "number")){
  2540. delay = cb;
  2541. cb = function(){};
  2542. }else if(!cb){
  2543. cb = function(){};
  2544. if(!delay){ delay = 0; }
  2545. }
  2546. setTimeout(function(){
  2547. (farr.shift())();
  2548. cb();
  2549. dojo.lang.delayThese(farr, cb, delay, onend);
  2550. }, delay);
  2551. }
  2552. dojo.provide("dojo.string.extras");
  2553. //TODO: should we use ${} substitution syntax instead, like widgets do?
  2554. dojo.string.substituteParams = function(/*string*/template, /* object - optional or ... */hash){
  2555. // summary:
  2556. // Performs parameterized substitutions on a string. Throws an exception if any parameter is unmatched.
  2557. //
  2558. // description:
  2559. // For example,
  2560. // dojo.string.substituteParams("File '%{0}' is not found in directory '%{1}'.","foo.html","/temp");
  2561. // returns
  2562. // "File 'foo.html' is not found in directory '/temp'."
  2563. //
  2564. // template: the original string template with %{values} to be replaced
  2565. // hash: name/value pairs (type object) to provide substitutions. Alternatively, substitutions may be
  2566. // included as arguments 1..n to this function, corresponding to template parameters 0..n-1
  2567. var map = (typeof hash == 'object') ? hash : dojo.lang.toArray(arguments, 1);
  2568. return template.replace(/\%\{(\w+)\}/g, function(match, key){
  2569. if(typeof(map[key]) != "undefined" && map[key] != null){
  2570. return map[key];
  2571. }
  2572. dojo.raise("Substitution not found: " + key);
  2573. }); // string
  2574. };
  2575. dojo.string.capitalize = function(/*string*/str){
  2576. // summary:
  2577. // Uppercases the first letter of each word
  2578. if(!dojo.lang.isString(str)){ return ""; }
  2579. if(arguments.length == 0){ str = this; }
  2580. var words = str.split(' ');
  2581. for(var i=0; i<words.length; i++){
  2582. words[i] = words[i].charAt(0).toUpperCase() + words[i].substring(1);
  2583. }
  2584. return words.join(" "); // string
  2585. }
  2586. dojo.string.isBlank = function(/*string*/str){
  2587. // summary:
  2588. // Return true if the entire string is whitespace characters
  2589. if(!dojo.lang.isString(str)){ return true; }
  2590. return (dojo.string.trim(str).length == 0); // boolean
  2591. }
  2592. //FIXME: not sure exactly what encodeAscii is trying to do, or if it's working right
  2593. dojo.string.encodeAscii = function(/*string*/str){
  2594. if(!dojo.lang.isString(str)){ return str; } // unknown
  2595. var ret = "";
  2596. var value = escape(str);
  2597. var match, re = /%u([0-9A-F]{4})/i;
  2598. while((match = value.match(re))){
  2599. var num = Number("0x"+match[1]);
  2600. var newVal = escape("&#" + num + ";");
  2601. ret += value.substring(0, match.index) + newVal;
  2602. value = value.substring(match.index+match[0].length);
  2603. }
  2604. ret += value.replace(/\+/g, "%2B");
  2605. return ret; // string
  2606. }
  2607. dojo.string.escape = function(/*string*/type, /*string*/str){
  2608. // summary:
  2609. // Adds escape sequences for special characters according to the convention of 'type'
  2610. //
  2611. // type: one of xml|html|xhtml|sql|regexp|regex|javascript|jscript|js|ascii
  2612. // str: the string to be escaped
  2613. var args = dojo.lang.toArray(arguments, 1);
  2614. switch(type.toLowerCase()){
  2615. case "xml":
  2616. case "html":
  2617. case "xhtml":
  2618. return dojo.string.escapeXml.apply(this, args); // string
  2619. case "sql":
  2620. return dojo.string.escapeSql.apply(this, args); // string
  2621. case "regexp":
  2622. case "regex":
  2623. return dojo.string.escapeRegExp.apply(this, args); // string
  2624. case "javascript":
  2625. case "jscript":
  2626. case "js":
  2627. return dojo.string.escapeJavaScript.apply(this, args); // string
  2628. case "ascii":
  2629. // so it's encode, but it seems useful
  2630. return dojo.string.encodeAscii.apply(this, args); // string
  2631. default:
  2632. return str; // string
  2633. }
  2634. }
  2635. dojo.string.escapeXml = function(/*string*/str, /*boolean*/noSingleQuotes){
  2636. //summary:
  2637. // Adds escape sequences for special characters in XML: &<>"'
  2638. // Optionally skips escapes for single quotes
  2639. str = str.replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
  2640. .replace(/>/gm, "&gt;").replace(/"/gm, "&quot;");
  2641. if(!noSingleQuotes){ str = str.replace(/'/gm, "&#39;"); }
  2642. return str; // string
  2643. }
  2644. dojo.string.escapeSql = function(/*string*/str){
  2645. //summary:
  2646. // Adds escape sequences for single quotes in SQL expressions
  2647. return str.replace(/'/gm, "''"); //string
  2648. }
  2649. dojo.string.escapeRegExp = function(/*string*/str){
  2650. //summary:
  2651. // Adds escape sequences for special characters in regular expressions
  2652. return str.replace(/\\/gm, "\\\\").replace(/([\f\b\n\t\r[\^$|?*+(){}])/gm, "\\$1"); // string
  2653. }
  2654. //FIXME: should this one also escape backslash?
  2655. dojo.string.escapeJavaScript = function(/*string*/str){
  2656. //summary:
  2657. // Adds escape sequences for single and double quotes as well
  2658. // as non-visible characters in JavaScript string literal expressions
  2659. return str.replace(/(["'\f\b\n\t\r])/gm, "\\$1"); // string
  2660. }
  2661. //FIXME: looks a lot like escapeJavaScript, just adds quotes? deprecate one?
  2662. dojo.string.escapeString = function(/*string*/str){
  2663. //summary:
  2664. // Adds escape sequences for non-visual characters, double quote and backslash
  2665. // and surrounds with double quotes to form a valid string literal.
  2666. return ('"' + str.replace(/(["\\])/g, '\\$1') + '"'
  2667. ).replace(/[\f]/g, "\\f"
  2668. ).replace(/[\b]/g, "\\b"
  2669. ).replace(/[\n]/g, "\\n"
  2670. ).replace(/[\t]/g, "\\t"
  2671. ).replace(/[\r]/g, "\\r"); // string
  2672. }
  2673. // TODO: make an HTML version
  2674. dojo.string.summary = function(/*string*/str, /*number*/len){
  2675. // summary:
  2676. // Truncates 'str' after 'len' characters and appends periods as necessary so that it ends with "..."
  2677. if(!len || str.length <= len){
  2678. return str; // string
  2679. }
  2680. return str.substring(0, len).replace(/\.+$/, "") + "..."; // string
  2681. }
  2682. dojo.string.endsWith = function(/*string*/str, /*string*/end, /*boolean*/ignoreCase){
  2683. // summary:
  2684. // Returns true if 'str' ends with 'end'
  2685. if(ignoreCase){
  2686. str = str.toLowerCase();
  2687. end = end.toLowerCase();
  2688. }
  2689. if((str.length - end.length) < 0){
  2690. return false; // boolean
  2691. }
  2692. return str.lastIndexOf(end) == str.length - end.length; // boolean
  2693. }
  2694. dojo.string.endsWithAny = function(/*string*/str /* , ... */){
  2695. // summary:
  2696. // Returns true if 'str' ends with any of the arguments[2 -> n]
  2697. for(var i = 1; i < arguments.length; i++) {
  2698. if(dojo.string.endsWith(str, arguments[i])) {
  2699. return true; // boolean
  2700. }
  2701. }
  2702. return false; // boolean
  2703. }
  2704. dojo.string.startsWith = function(/*string*/str, /*string*/start, /*boolean*/ignoreCase){
  2705. // summary:
  2706. // Returns true if 'str' starts with 'start'
  2707. if(ignoreCase) {
  2708. str = str.toLowerCase();
  2709. start = start.toLowerCase();
  2710. }
  2711. return str.indexOf(start) == 0; // boolean
  2712. }
  2713. dojo.string.startsWithAny = function(/*string*/str /* , ... */){
  2714. // summary:
  2715. // Returns true if 'str' starts with any of the arguments[2 -> n]
  2716. for(var i = 1; i < arguments.length; i++) {
  2717. if(dojo.string.startsWith(str, arguments[i])) {
  2718. return true; // boolean
  2719. }
  2720. }
  2721. return false; // boolean
  2722. }
  2723. dojo.string.has = function(/*string*/str /* , ... */) {
  2724. // summary:
  2725. // Returns true if 'str' contains any of the arguments 2 -> n
  2726. for(var i = 1; i < arguments.length; i++) {
  2727. if(str.indexOf(arguments[i]) > -1){
  2728. return true; // boolean
  2729. }
  2730. }
  2731. return false; // boolean
  2732. }
  2733. dojo.string.normalizeNewlines = function(/*string*/text, /*string? (\n or \r)*/newlineChar){
  2734. // summary:
  2735. // Changes occurences of CR and LF in text to CRLF, or if newlineChar is provided as '\n' or '\r',
  2736. // substitutes newlineChar for occurrences of CR/LF and CRLF
  2737. if (newlineChar == "\n"){
  2738. text = text.replace(/\r\n/g, "\n");
  2739. text = text.replace(/\r/g, "\n");
  2740. } else if (newlineChar == "\r"){
  2741. text = text.replace(/\r\n/g, "\r");
  2742. text = text.replace(/\n/g, "\r");
  2743. }else{
  2744. text = text.replace(/([^\r])\n/g, "$1\r\n").replace(/\r([^\n])/g, "\r\n$1");
  2745. }
  2746. return text; // string
  2747. }
  2748. dojo.string.splitEscaped = function(/*string*/str, /*string of length=1*/charac){
  2749. // summary:
  2750. // Splits 'str' into an array separated by 'charac', but skips characters escaped with a backslash
  2751. var components = [];
  2752. for (var i = 0, prevcomma = 0; i < str.length; i++){
  2753. if (str.charAt(i) == '\\'){ i++; continue; }
  2754. if (str.charAt(i) == charac){
  2755. components.push(str.substring(prevcomma, i));
  2756. prevcomma = i + 1;
  2757. }
  2758. }
  2759. components.push(str.substr(prevcomma));
  2760. return components; // array
  2761. }
  2762. dojo.provide("dojo.dom");
  2763. dojo.dom.ELEMENT_NODE = 1;
  2764. dojo.dom.ATTRIBUTE_NODE = 2;
  2765. dojo.dom.TEXT_NODE = 3;
  2766. dojo.dom.CDATA_SECTION_NODE = 4;
  2767. dojo.dom.ENTITY_REFERENCE_NODE = 5;
  2768. dojo.dom.ENTITY_NODE = 6;
  2769. dojo.dom.PROCESSING_INSTRUCTION_NODE = 7;
  2770. dojo.dom.COMMENT_NODE = 8;
  2771. dojo.dom.DOCUMENT_NODE = 9;
  2772. dojo.dom.DOCUMENT_TYPE_NODE = 10;
  2773. dojo.dom.DOCUMENT_FRAGMENT_NODE = 11;
  2774. dojo.dom.NOTATION_NODE = 12;
  2775. dojo.dom.dojoml = "http://www.dojotoolkit.org/2004/dojoml";
  2776. /**
  2777. * comprehensive list of XML namespaces
  2778. **/
  2779. dojo.dom.xmlns = {
  2780. // summary
  2781. // aliases for various common XML namespaces
  2782. svg : "http://www.w3.org/2000/svg",
  2783. smil : "http://www.w3.org/2001/SMIL20/",
  2784. mml : "http://www.w3.org/1998/Math/MathML",
  2785. cml : "http://www.xml-cml.org",
  2786. xlink : "http://www.w3.org/1999/xlink",
  2787. xhtml : "http://www.w3.org/1999/xhtml",
  2788. xul : "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
  2789. xbl : "http://www.mozilla.org/xbl",
  2790. fo : "http://www.w3.org/1999/XSL/Format",
  2791. xsl : "http://www.w3.org/1999/XSL/Transform",
  2792. xslt : "http://www.w3.org/1999/XSL/Transform",
  2793. xi : "http://www.w3.org/2001/XInclude",
  2794. xforms : "http://www.w3.org/2002/01/xforms",
  2795. saxon : "http://icl.com/saxon",
  2796. xalan : "http://xml.apache.org/xslt",
  2797. xsd : "http://www.w3.org/2001/XMLSchema",
  2798. dt: "http://www.w3.org/2001/XMLSchema-datatypes",
  2799. xsi : "http://www.w3.org/2001/XMLSchema-instance",
  2800. rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
  2801. rdfs : "http://www.w3.org/2000/01/rdf-schema#",
  2802. dc : "http://purl.org/dc/elements/1.1/",
  2803. dcq: "http://purl.org/dc/qualifiers/1.0",
  2804. "soap-env" : "http://schemas.xmlsoap.org/soap/envelope/",
  2805. wsdl : "http://schemas.xmlsoap.org/wsdl/",
  2806. AdobeExtensions : "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"
  2807. };
  2808. dojo.dom.isNode = function(/* object */wh){
  2809. // summary
  2810. // checks to see if wh is actually a node.
  2811. if(typeof Element == "function") {
  2812. try {
  2813. return wh instanceof Element; // boolean
  2814. } catch(E) {}
  2815. } else {
  2816. // best-guess
  2817. return wh && !isNaN(wh.nodeType); // boolean
  2818. }
  2819. }
  2820. dojo.dom.getUniqueId = function(){
  2821. // summary
  2822. // returns a unique string for use with any DOM element
  2823. var _document = dojo.doc();
  2824. do {
  2825. var id = "dj_unique_" + (++arguments.callee._idIncrement);
  2826. }while(_document.getElementById(id));
  2827. return id; // string
  2828. }
  2829. dojo.dom.getUniqueId._idIncrement = 0;
  2830. dojo.dom.firstElement = dojo.dom.getFirstChildElement = function(/* Element */parentNode, /* string? */tagName){
  2831. // summary
  2832. // returns the first child element matching tagName
  2833. var node = parentNode.firstChild;
  2834. while(node && node.nodeType != dojo.dom.ELEMENT_NODE){
  2835. node = node.nextSibling;
  2836. }
  2837. if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
  2838. node = dojo.dom.nextElement(node, tagName);
  2839. }
  2840. return node; // Element
  2841. }
  2842. dojo.dom.lastElement = dojo.dom.getLastChildElement = function(/* Element */parentNode, /* string? */tagName){
  2843. // summary
  2844. // returns the last child element matching tagName
  2845. var node = parentNode.lastChild;
  2846. while(node && node.nodeType != dojo.dom.ELEMENT_NODE) {
  2847. node = node.previousSibling;
  2848. }
  2849. if(tagName && node && node.tagName && node.tagName.toLowerCase() != tagName.toLowerCase()) {
  2850. node = dojo.dom.prevElement(node, tagName);
  2851. }
  2852. return node; // Element
  2853. }
  2854. dojo.dom.nextElement = dojo.dom.getNextSiblingElement = function(/* Node */node, /* string? */tagName){
  2855. // summary
  2856. // returns the next sibling element matching tagName
  2857. if(!node) { return null; }
  2858. do {
  2859. node = node.nextSibling;
  2860. } while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
  2861. if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
  2862. return dojo.dom.nextElement(node, tagName);
  2863. }
  2864. return node; // Element
  2865. }
  2866. dojo.dom.prevElement = dojo.dom.getPreviousSiblingElement = function(/* Node */node, /* string? */tagName){
  2867. // summary
  2868. // returns the previous sibling element matching tagName
  2869. if(!node) { return null; }
  2870. if(tagName) { tagName = tagName.toLowerCase(); }
  2871. do {
  2872. node = node.previousSibling;
  2873. } while(node && node.nodeType != dojo.dom.ELEMENT_NODE);
  2874. if(node && tagName && tagName.toLowerCase() != node.tagName.toLowerCase()) {
  2875. return dojo.dom.prevElement(node, tagName);
  2876. }
  2877. return node; // Element
  2878. }
  2879. // TODO: hmph
  2880. /*this.forEachChildTag = function(node, unaryFunc) {
  2881. var child = this.getFirstChildTag(node);
  2882. while(child) {
  2883. if(unaryFunc(child) == "break") { break; }
  2884. child = this.getNextSiblingTag(child);
  2885. }
  2886. }*/
  2887. dojo.dom.moveChildren = function(/* Element */srcNode, /* Element */destNode, /* boolean? */trim){
  2888. // summary
  2889. // Moves children from srcNode to destNode and returns the count of children moved;
  2890. // will trim off text nodes if trim == true
  2891. var count = 0;
  2892. if(trim) {
  2893. while(srcNode.hasChildNodes() &&
  2894. srcNode.firstChild.nodeType == dojo.dom.TEXT_NODE) {
  2895. srcNode.removeChild(srcNode.firstChild);
  2896. }
  2897. while(srcNode.hasChildNodes() &&
  2898. srcNode.lastChild.nodeType == dojo.dom.TEXT_NODE) {
  2899. srcNode.removeChild(srcNode.lastChild);
  2900. }
  2901. }
  2902. while(srcNode.hasChildNodes()){
  2903. destNode.appendChild(srcNode.firstChild);
  2904. count++;
  2905. }
  2906. return count; // number
  2907. }
  2908. dojo.dom.copyChildren = function(/* Element */srcNode, /* Element */destNode, /* boolean? */trim){
  2909. // summary
  2910. // Copies children from srcNde to destNode and returns the count of children copied;
  2911. // will trim off text nodes if trim == true
  2912. var clonedNode = srcNode.cloneNode(true);
  2913. return this.moveChildren(clonedNode, destNode, trim); // number
  2914. }
  2915. dojo.dom.removeChildren = function(/* Element */node){
  2916. // summary
  2917. // removes all children from node and returns the count of children removed.
  2918. var count = node.childNodes.length;
  2919. while(node.hasChildNodes()){ node.removeChild(node.firstChild); }
  2920. return count; // number
  2921. }
  2922. dojo.dom.replaceChildren = function(/* Element */node, /* Node */newChild){
  2923. // summary
  2924. // Removes all children of node and appends newChild
  2925. // FIXME: what if newChild is an array-like object?
  2926. dojo.dom.removeChildren(node);
  2927. node.appendChild(newChild);
  2928. }
  2929. dojo.dom.removeNode = function(/* Node */node){
  2930. // summary
  2931. // if node has a parent, removes node from parent and returns a reference to the removed child.
  2932. if(node && node.parentNode){
  2933. // return a ref to the removed child
  2934. return node.parentNode.removeChild(node); // Node
  2935. }
  2936. }
  2937. dojo.dom.getAncestors = function(/* Node */node, /* function? */filterFunction, /* boolean? */returnFirstHit) {
  2938. // summary
  2939. // returns all ancestors matching optional filterFunction; will return only the first if returnFirstHit
  2940. var ancestors = [];
  2941. var isFunction = (filterFunction && (filterFunction instanceof Function || typeof filterFunction == "function"));
  2942. while(node) {
  2943. if (!isFunction || filterFunction(node)) {
  2944. ancestors.push(node);
  2945. }
  2946. if (returnFirstHit && ancestors.length > 0) {
  2947. return ancestors[0]; // Node
  2948. }
  2949. node = node.parentNode;
  2950. }
  2951. if (returnFirstHit) { return null; }
  2952. return ancestors; // array
  2953. }
  2954. dojo.dom.getAncestorsByTag = function(/* Node */node, /* string */tag, /* boolean? */returnFirstHit) {
  2955. // summary
  2956. // returns all ancestors matching tag (as tagName), will only return first one if returnFirstHit
  2957. tag = tag.toLowerCase();
  2958. return dojo.dom.getAncestors(node, function(el){
  2959. return ((el.tagName)&&(el.tagName.toLowerCase() == tag));
  2960. }, returnFirstHit); // Node || array
  2961. }
  2962. dojo.dom.getFirstAncestorByTag = function(/* Node */node, /* string */tag) {
  2963. // summary
  2964. // Returns first ancestor of node with tag tagName
  2965. return dojo.dom.getAncestorsByTag(node, tag, true); // Node
  2966. }
  2967. dojo.dom.isDescendantOf = function(/* Node */node, /* Node */ancestor, /* boolean? */guaranteeDescendant){
  2968. // summary
  2969. // Returns boolean if node is a descendant of ancestor
  2970. // guaranteeDescendant allows us to be a "true" isDescendantOf function
  2971. if(guaranteeDescendant && node) { node = node.parentNode; }
  2972. while(node) {
  2973. if(node == ancestor){
  2974. return true; // boolean
  2975. }
  2976. node = node.parentNode;
  2977. }
  2978. return false; // boolean
  2979. }
  2980. dojo.dom.innerXML = function(/* Node */node){
  2981. // summary
  2982. // Implementation of MS's innerXML function.
  2983. if(node.innerXML){
  2984. return node.innerXML; // string
  2985. }else if (node.xml){
  2986. return node.xml; // string
  2987. }else if(typeof XMLSerializer != "undefined"){
  2988. return (new XMLSerializer()).serializeToString(node); // string
  2989. }
  2990. }
  2991. dojo.dom.createDocument = function(){
  2992. // summary
  2993. // cross-browser implementation of creating an XML document object.
  2994. var doc = null;
  2995. var _document = dojo.doc();
  2996. if(!dj_undef("ActiveXObject")){
  2997. var prefixes = [ "MSXML2", "Microsoft", "MSXML", "MSXML3" ];
  2998. for(var i = 0; i<prefixes.length; i++){
  2999. try{
  3000. doc = new ActiveXObject(prefixes[i]+".XMLDOM");
  3001. }catch(e){ /* squelch */ };
  3002. if(doc){ break; }
  3003. }
  3004. }else if((_document.implementation)&&
  3005. (_document.implementation.createDocument)){
  3006. doc = _document.implementation.createDocument("", "", null);
  3007. }
  3008. return doc; // DOMDocument
  3009. }
  3010. dojo.dom.createDocumentFromText = function(/* string */str, /* string? */mimetype){
  3011. // summary
  3012. // attempts to create a Document object based on optional mime-type, using str as the contents of the document
  3013. if(!mimetype){ mimetype = "text/xml"; }
  3014. if(!dj_undef("DOMParser")){
  3015. var parser = new DOMParser();
  3016. return parser.parseFromString(str, mimetype); // DOMDocument
  3017. }else if(!dj_undef("ActiveXObject")){
  3018. var domDoc = dojo.dom.createDocument();
  3019. if(domDoc){
  3020. domDoc.async = false;
  3021. domDoc.loadXML(str);
  3022. return domDoc; // DOMDocument
  3023. }else{
  3024. dojo.debug("toXml didn't work?");
  3025. }
  3026. /*
  3027. }else if((dojo.render.html.capable)&&(dojo.render.html.safari)){
  3028. // FIXME: this doesn't appear to work!
  3029. // from: http://web-graphics.com/mtarchive/001606.php
  3030. // var xml = '<?xml version="1.0"?>'+str;
  3031. var mtype = "text/xml";
  3032. var xml = '<?xml version="1.0"?>'+str;
  3033. var url = "data:"+mtype+";charset=utf-8,"+encodeURIComponent(xml);
  3034. var req = new XMLHttpRequest();
  3035. req.open("GET", url, false);
  3036. req.overrideMimeType(mtype);
  3037. req.send(null);
  3038. return req.responseXML;
  3039. */
  3040. }else{
  3041. var _document = dojo.doc();
  3042. if(_document.createElement){
  3043. // FIXME: this may change all tags to uppercase!
  3044. var tmp = _document.createElement("xml");
  3045. tmp.innerHTML = str;
  3046. if(_document.implementation && _document.implementation.createDocument) {
  3047. var xmlDoc = _document.implementation.createDocument("foo", "", null);
  3048. for(var i = 0; i < tmp.childNodes.length; i++) {
  3049. xmlDoc.importNode(tmp.childNodes.item(i), true);
  3050. }
  3051. return xmlDoc; // DOMDocument
  3052. }
  3053. // FIXME: probably not a good idea to have to return an HTML fragment
  3054. // FIXME: the tmp.doc.firstChild is as tested from IE, so it may not
  3055. // work that way across the board
  3056. return ((tmp.document)&&
  3057. (tmp.document.firstChild ? tmp.document.firstChild : tmp)); // DOMDocument
  3058. }
  3059. }
  3060. return null;
  3061. }
  3062. dojo.dom.prependChild = function(/* Element */node, /* Element */parent) {
  3063. // summary
  3064. // prepends node to parent's children nodes
  3065. if(parent.firstChild) {
  3066. parent.insertBefore(node, parent.firstChild);
  3067. } else {
  3068. parent.appendChild(node);
  3069. }
  3070. return true; // boolean
  3071. }
  3072. dojo.dom.insertBefore = function(/* Node */node, /* Node */ref, /* boolean? */force){
  3073. // summary
  3074. // Try to insert node before ref
  3075. if (force != true &&
  3076. (node === ref || node.nextSibling === ref)){ return false; }
  3077. var parent = ref.parentNode;
  3078. parent.insertBefore(node, ref);
  3079. return true; // boolean
  3080. }
  3081. dojo.dom.insertAfter = function(/* Node */node, /* Node */ref, /* boolean? */force){
  3082. // summary
  3083. // Try to insert node after ref
  3084. var pn = ref.parentNode;
  3085. if(ref == pn.lastChild){
  3086. if((force != true)&&(node === ref)){
  3087. return false; // boolean
  3088. }
  3089. pn.appendChild(node);
  3090. }else{
  3091. return this.insertBefore(node, ref.nextSibling, force); // boolean
  3092. }
  3093. return true; // boolean
  3094. }
  3095. dojo.dom.insertAtPosition = function(/* Node */node, /* Node */ref, /* string */position){
  3096. // summary
  3097. // attempt to insert node in relation to ref based on position
  3098. if((!node)||(!ref)||(!position)){
  3099. return false; // boolean
  3100. }
  3101. switch(position.toLowerCase()){
  3102. case "before":
  3103. return dojo.dom.insertBefore(node, ref); // boolean
  3104. case "after":
  3105. return dojo.dom.insertAfter(node, ref); // boolean
  3106. case "first":
  3107. if(ref.firstChild){
  3108. return dojo.dom.insertBefore(node, ref.firstChild); // boolean
  3109. }else{
  3110. ref.appendChild(node);
  3111. return true; // boolean
  3112. }
  3113. break;
  3114. default: // aka: last
  3115. ref.appendChild(node);
  3116. return true; // boolean
  3117. }
  3118. }
  3119. dojo.dom.insertAtIndex = function(/* Node */node, /* Element */containingNode, /* number */insertionIndex){
  3120. // summary
  3121. // insert node into child nodes nodelist of containingNode at insertionIndex.
  3122. var siblingNodes = containingNode.childNodes;
  3123. // if there aren't any kids yet, just add it to the beginning
  3124. if (!siblingNodes.length){
  3125. containingNode.appendChild(node);
  3126. return true; // boolean
  3127. }
  3128. // otherwise we need to walk the childNodes
  3129. // and find our spot
  3130. var after = null;
  3131. for(var i=0; i<siblingNodes.length; i++){
  3132. var sibling_index = siblingNodes.item(i)["getAttribute"] ? parseInt(siblingNodes.item(i).getAttribute("dojoinsertionindex")) : -1;
  3133. if (sibling_index < insertionIndex){
  3134. after = siblingNodes.item(i);
  3135. }
  3136. }
  3137. if (after){
  3138. // add it after the node in {after}
  3139. return dojo.dom.insertAfter(node, after); // boolean
  3140. }else{
  3141. // add it to the start
  3142. return dojo.dom.insertBefore(node, siblingNodes.item(0)); // boolean
  3143. }
  3144. }
  3145. dojo.dom.textContent = function(/* Node */node, /* string */text){
  3146. // summary
  3147. // implementation of the DOM Level 3 attribute; scan node for text
  3148. if (arguments.length>1) {
  3149. var _document = dojo.doc();
  3150. dojo.dom.replaceChildren(node, _document.createTextNode(text));
  3151. return text; // string
  3152. } else {
  3153. if(node.textContent != undefined){ //FF 1.5
  3154. return node.textContent; // string
  3155. }
  3156. var _result = "";
  3157. if (node == null) { return _result; }
  3158. for (var i = 0; i < node.childNodes.length; i++) {
  3159. switch (node.childNodes[i].nodeType) {
  3160. case 1: // ELEMENT_NODE
  3161. case 5: // ENTITY_REFERENCE_NODE
  3162. _result += dojo.dom.textContent(node.childNodes[i]);
  3163. break;
  3164. case 3: // TEXT_NODE
  3165. case 2: // ATTRIBUTE_NODE
  3166. case 4: // CDATA_SECTION_NODE
  3167. _result += node.childNodes[i].nodeValue;
  3168. break;
  3169. default:
  3170. break;
  3171. }
  3172. }
  3173. return _result; // string
  3174. }
  3175. }
  3176. dojo.dom.hasParent = function (/* Node */node) {
  3177. // summary
  3178. // returns whether or not node is a child of another node.
  3179. return node && node.parentNode && dojo.dom.isNode(node.parentNode); // boolean
  3180. }
  3181. /**
  3182. * Examples:
  3183. *
  3184. * myFooNode = <foo />
  3185. * isTag(myFooNode, "foo"); // returns "foo"
  3186. * isTag(myFooNode, "bar"); // returns ""
  3187. * isTag(myFooNode, "FOO"); // returns ""
  3188. * isTag(myFooNode, "hey", "foo", "bar"); // returns "foo"
  3189. **/
  3190. dojo.dom.isTag = function(/* Node */node /* ... */) {
  3191. // summary
  3192. // determines if node has any of the provided tag names and returns the tag name that matches, empty string otherwise.
  3193. if(node && node.tagName) {
  3194. for(var i=1; i<arguments.length; i++){
  3195. if(node.tagName==String(arguments[i])){
  3196. return String(arguments[i]); // string
  3197. }
  3198. }
  3199. }
  3200. return ""; // string
  3201. }
  3202. dojo.dom.setAttributeNS = function(/* Element */elem, /* string */namespaceURI, /* string */attrName, /* string */attrValue){
  3203. // summary
  3204. // implementation of DOM2 setAttributeNS that works cross browser.
  3205. if(elem == null || ((elem == undefined)&&(typeof elem == "undefined"))){
  3206. dojo.raise("No element given to dojo.dom.setAttributeNS");
  3207. }
  3208. if(!((elem.setAttributeNS == undefined)&&(typeof elem.setAttributeNS == "undefined"))){ // w3c
  3209. elem.setAttributeNS(namespaceURI, attrName, attrValue);
  3210. }else{ // IE
  3211. // get a root XML document
  3212. var ownerDoc = elem.ownerDocument;
  3213. var attribute = ownerDoc.createNode(
  3214. 2, // node type
  3215. attrName,
  3216. namespaceURI
  3217. );
  3218. // set value
  3219. attribute.nodeValue = attrValue;
  3220. // attach to element
  3221. elem.setAttributeNode(attribute);
  3222. }
  3223. }
  3224. dojo.provide("dojo.undo.browser");
  3225. try{
  3226. if((!djConfig["preventBackButtonFix"])&&(!dojo.hostenv.post_load_)){
  3227. document.write("<iframe style='border: 0px; width: 1px; height: 1px; position: absolute; bottom: 0px; right: 0px; visibility: visible;' name='djhistory' id='djhistory' src='"+(dojo.hostenv.getBaseScriptUri()+'iframe_history.html')+"'></iframe>");
  3228. }
  3229. }catch(e){/* squelch */}
  3230. if(dojo.render.html.opera){
  3231. dojo.debug("Opera is not supported with dojo.undo.browser, so back/forward detection will not work.");
  3232. }
  3233. /* NOTES:
  3234. * Safari 1.2:
  3235. * back button "works" fine, however it's not possible to actually
  3236. * DETECT that you've moved backwards by inspecting window.location.
  3237. * Unless there is some other means of locating.
  3238. * FIXME: perhaps we can poll on history.length?
  3239. * Safari 2.0.3+ (and probably 1.3.2+):
  3240. * works fine, except when changeUrl is used. When changeUrl is used,
  3241. * Safari jumps all the way back to whatever page was shown before
  3242. * the page that uses dojo.undo.browser support.
  3243. * IE 5.5 SP2:
  3244. * back button behavior is macro. It does not move back to the
  3245. * previous hash value, but to the last full page load. This suggests
  3246. * that the iframe is the correct way to capture the back button in
  3247. * these cases.
  3248. * Don't test this page using local disk for MSIE. MSIE will not create
  3249. * a history list for iframe_history.html if served from a file: URL.
  3250. * The XML served back from the XHR tests will also not be properly
  3251. * created if served from local disk. Serve the test pages from a web
  3252. * server to test in that browser.
  3253. * IE 6.0:
  3254. * same behavior as IE 5.5 SP2
  3255. * Firefox 1.0+:
  3256. * the back button will return us to the previous hash on the same
  3257. * page, thereby not requiring an iframe hack, although we do then
  3258. * need to run a timer to detect inter-page movement.
  3259. */
  3260. dojo.undo.browser = {
  3261. initialHref: window.location.href,
  3262. initialHash: window.location.hash,
  3263. moveForward: false,
  3264. historyStack: [],
  3265. forwardStack: [],
  3266. historyIframe: null,
  3267. bookmarkAnchor: null,
  3268. locationTimer: null,
  3269. /**
  3270. * setInitialState sets the state object and back callback for the very first page that is loaded.
  3271. * It is recommended that you call this method as part of an event listener that is registered via
  3272. * dojo.addOnLoad().
  3273. */
  3274. setInitialState: function(args){
  3275. this.initialState = this._createState(this.initialHref, args, this.initialHash);
  3276. },
  3277. //FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things.
  3278. //FIXME: is there a slight race condition in moz using change URL with the timer check and when
  3279. // the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent.
  3280. /**
  3281. * addToHistory takes one argument, and it is an object that defines the following functions:
  3282. * - To support getting back button notifications, the object argument should implement a
  3283. * function called either "back", "backButton", or "handle". The string "back" will be
  3284. * passed as the first and only argument to this callback.
  3285. * - To support getting forward button notifications, the object argument should implement a
  3286. * function called either "forward", "forwardButton", or "handle". The string "forward" will be
  3287. * passed as the first and only argument to this callback.
  3288. * - If you want the browser location string to change, define "changeUrl" on the object. If the
  3289. * value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment
  3290. * identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does
  3291. * not evaluate to false, that value will be used as the fragment identifier. For example,
  3292. * if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1
  3293. *
  3294. * Full example:
  3295. *
  3296. * dojo.undo.browser.addToHistory({
  3297. * back: function() { alert('back pressed'); },
  3298. * forward: function() { alert('forward pressed'); },
  3299. * changeUrl: true
  3300. * });
  3301. */
  3302. addToHistory: function(args){
  3303. //If addToHistory is called, then that means we prune the
  3304. //forward stack -- the user went back, then wanted to
  3305. //start a new forward path.
  3306. this.forwardStack = [];
  3307. var hash = null;
  3308. var url = null;
  3309. if(!this.historyIframe){
  3310. this.historyIframe = window.frames["djhistory"];
  3311. }
  3312. if(!this.bookmarkAnchor){
  3313. this.bookmarkAnchor = document.createElement("a");
  3314. dojo.body().appendChild(this.bookmarkAnchor);
  3315. this.bookmarkAnchor.style.display = "none";
  3316. }
  3317. if(args["changeUrl"]){
  3318. hash = "#"+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime());
  3319. //If the current hash matches the new one, just replace the history object with
  3320. //this new one. It doesn't make sense to track different state objects for the same
  3321. //logical URL. This matches the browser behavior of only putting in one history
  3322. //item no matter how many times you click on the same #hash link, at least in Firefox
  3323. //and Safari, and there is no reliable way in those browsers to know if a #hash link
  3324. //has been clicked on multiple times. So making this the standard behavior in all browsers
  3325. //so that dojo.undo.browser's behavior is the same in all browsers.
  3326. if(this.historyStack.length == 0 && this.initialState.urlHash == hash){
  3327. this.initialState = this._createState(url, args, hash);
  3328. return;
  3329. }else if(this.historyStack.length > 0 && this.historyStack[this.historyStack.length - 1].urlHash == hash){
  3330. this.historyStack[this.historyStack.length - 1] = this._createState(url, args, hash);
  3331. return;
  3332. }
  3333. this.changingUrl = true;
  3334. setTimeout("window.location.href = '"+hash+"'; dojo.undo.browser.changingUrl = false;", 1);
  3335. this.bookmarkAnchor.href = hash;
  3336. if(dojo.render.html.ie){
  3337. url = this._loadIframeHistory();
  3338. var oldCB = args["back"]||args["backButton"]||args["handle"];
  3339. //The function takes handleName as a parameter, in case the
  3340. //callback we are overriding was "handle". In that case,
  3341. //we will need to pass the handle name to handle.
  3342. var tcb = function(handleName){
  3343. if(window.location.hash != ""){
  3344. setTimeout("window.location.href = '"+hash+"';", 1);
  3345. }
  3346. //Use apply to set "this" to args, and to try to avoid memory leaks.
  3347. oldCB.apply(this, [handleName]);
  3348. }
  3349. //Set interceptor function in the right place.
  3350. if(args["back"]){
  3351. args.back = tcb;
  3352. }else if(args["backButton"]){
  3353. args.backButton = tcb;
  3354. }else if(args["handle"]){
  3355. args.handle = tcb;
  3356. }
  3357. var oldFW = args["forward"]||args["forwardButton"]||args["handle"];
  3358. //The function takes handleName as a parameter, in case the
  3359. //callback we are overriding was "handle". In that case,
  3360. //we will need to pass the handle name to handle.
  3361. var tfw = function(handleName){
  3362. if(window.location.hash != ""){
  3363. window.location.href = hash;
  3364. }
  3365. if(oldFW){ // we might not actually have one
  3366. //Use apply to set "this" to args, and to try to avoid memory leaks.
  3367. oldFW.apply(this, [handleName]);
  3368. }
  3369. }
  3370. //Set interceptor function in the right place.
  3371. if(args["forward"]){
  3372. args.forward = tfw;
  3373. }else if(args["forwardButton"]){
  3374. args.forwardButton = tfw;
  3375. }else if(args["handle"]){
  3376. args.handle = tfw;
  3377. }
  3378. }else if(dojo.render.html.moz){
  3379. // start the timer
  3380. if(!this.locationTimer){
  3381. this.locationTimer = setInterval("dojo.undo.browser.checkLocation();", 200);
  3382. }
  3383. }
  3384. }else{
  3385. url = this._loadIframeHistory();
  3386. }
  3387. this.historyStack.push(this._createState(url, args, hash));
  3388. },
  3389. checkLocation: function(){
  3390. if (!this.changingUrl){
  3391. var hsl = this.historyStack.length;
  3392. if((window.location.hash == this.initialHash||window.location.href == this.initialHref)&&(hsl == 1)){
  3393. // FIXME: could this ever be a forward button?
  3394. // we can't clear it because we still need to check for forwards. Ugg.
  3395. // clearInterval(this.locationTimer);
  3396. this.handleBackButton();
  3397. return;
  3398. }
  3399. // first check to see if we could have gone forward. We always halt on
  3400. // a no-hash item.
  3401. if(this.forwardStack.length > 0){
  3402. if(this.forwardStack[this.forwardStack.length-1].urlHash == window.location.hash){
  3403. this.handleForwardButton();
  3404. return;
  3405. }
  3406. }
  3407. // ok, that didn't work, try someplace back in the history stack
  3408. if((hsl >= 2)&&(this.historyStack[hsl-2])){
  3409. if(this.historyStack[hsl-2].urlHash==window.location.hash){
  3410. this.handleBackButton();
  3411. return;
  3412. }
  3413. }
  3414. }
  3415. },
  3416. iframeLoaded: function(evt, ifrLoc){
  3417. if(!dojo.render.html.opera){
  3418. var query = this._getUrlQuery(ifrLoc.href);
  3419. if(query == null){
  3420. // alert("iframeLoaded");
  3421. // we hit the end of the history, so we should go back
  3422. if(this.historyStack.length == 1){
  3423. this.handleBackButton();
  3424. }
  3425. return;
  3426. }
  3427. if(this.moveForward){
  3428. // we were expecting it, so it's not either a forward or backward movement
  3429. this.moveForward = false;
  3430. return;
  3431. }
  3432. //Check the back stack first, since it is more likely.
  3433. //Note that only one step back or forward is supported.
  3434. if(this.historyStack.length >= 2 && query == this._getUrlQuery(this.historyStack[this.historyStack.length-2].url)){
  3435. this.handleBackButton();
  3436. }
  3437. else if(this.forwardStack.length > 0 && query == this._getUrlQuery(this.forwardStack[this.forwardStack.length-1].url)){
  3438. this.handleForwardButton();
  3439. }
  3440. }
  3441. },
  3442. handleBackButton: function(){
  3443. //The "current" page is always at the top of the history stack.
  3444. var current = this.historyStack.pop();
  3445. if(!current){ return; }
  3446. var last = this.historyStack[this.historyStack.length-1];
  3447. if(!last && this.historyStack.length == 0){
  3448. last = this.initialState;
  3449. }
  3450. if (last){
  3451. if(last.kwArgs["back"]){
  3452. last.kwArgs["back"]();
  3453. }else if(last.kwArgs["backButton"]){
  3454. last.kwArgs["backButton"]();
  3455. }else if(last.kwArgs["handle"]){
  3456. last.kwArgs.handle("back");
  3457. }
  3458. }
  3459. this.forwardStack.push(current);
  3460. },
  3461. handleForwardButton: function(){
  3462. var last = this.forwardStack.pop();
  3463. if(!last){ return; }
  3464. if(last.kwArgs["forward"]){
  3465. last.kwArgs.forward();
  3466. }else if(last.kwArgs["forwardButton"]){
  3467. last.kwArgs.forwardButton();
  3468. }else if(last.kwArgs["handle"]){
  3469. last.kwArgs.handle("forward");
  3470. }
  3471. this.historyStack.push(last);
  3472. },
  3473. _createState: function(url, args, hash){
  3474. return {"url": url, "kwArgs": args, "urlHash": hash};
  3475. },
  3476. _getUrlQuery: function(url){
  3477. var segments = url.split("?");
  3478. if (segments.length < 2){
  3479. return null;
  3480. }
  3481. else{
  3482. return segments[1];
  3483. }
  3484. },
  3485. _loadIframeHistory: function(){
  3486. var url = dojo.hostenv.getBaseScriptUri()+"iframe_history.html?"+(new Date()).getTime();
  3487. this.moveForward = true;
  3488. dojo.io.setIFrameSrc(this.historyIframe, url, false);
  3489. return url;
  3490. }
  3491. }
  3492. dojo.provide("dojo.io.BrowserIO");
  3493. dojo.io.checkChildrenForFile = function(node){
  3494. var hasFile = false;
  3495. var inputs = node.getElementsByTagName("input");
  3496. dojo.lang.forEach(inputs, function(input){
  3497. if(hasFile){ return; }
  3498. if(input.getAttribute("type")=="file"){
  3499. hasFile = true;
  3500. }
  3501. });
  3502. return hasFile;
  3503. }
  3504. dojo.io.formHasFile = function(formNode){
  3505. return dojo.io.checkChildrenForFile(formNode);
  3506. }
  3507. dojo.io.updateNode = function(node, urlOrArgs){
  3508. node = dojo.byId(node);
  3509. var args = urlOrArgs;
  3510. if(dojo.lang.isString(urlOrArgs)){
  3511. args = { url: urlOrArgs };
  3512. }
  3513. args.mimetype = "text/html";
  3514. args.load = function(t, d, e){
  3515. while(node.firstChild){
  3516. if(dojo["event"]){
  3517. try{
  3518. dojo.event.browser.clean(node.firstChild);
  3519. }catch(e){}
  3520. }
  3521. node.removeChild(node.firstChild);
  3522. }
  3523. node.innerHTML = d;
  3524. };
  3525. dojo.io.bind(args);
  3526. }
  3527. dojo.io.formFilter = function(node) {
  3528. var type = (node.type||"").toLowerCase();
  3529. return !node.disabled && node.name
  3530. && !dojo.lang.inArray(["file", "submit", "image", "reset", "button"], type);
  3531. }
  3532. // TODO: Move to htmlUtils
  3533. dojo.io.encodeForm = function(formNode, encoding, formFilter){
  3534. if((!formNode)||(!formNode.tagName)||(!formNode.tagName.toLowerCase() == "form")){
  3535. dojo.raise("Attempted to encode a non-form element.");
  3536. }
  3537. if(!formFilter) { formFilter = dojo.io.formFilter; }
  3538. var enc = /utf/i.test(encoding||"") ? encodeURIComponent : dojo.string.encodeAscii;
  3539. var values = [];
  3540. for(var i = 0; i < formNode.elements.length; i++){
  3541. var elm = formNode.elements[i];
  3542. if(!elm || elm.tagName.toLowerCase() == "fieldset" || !formFilter(elm)) { continue; }
  3543. var name = enc(elm.name);
  3544. var type = elm.type.toLowerCase();
  3545. if(type == "select-multiple"){
  3546. for(var j = 0; j < elm.options.length; j++){
  3547. if(elm.options[j].selected) {
  3548. values.push(name + "=" + enc(elm.options[j].value));
  3549. }
  3550. }
  3551. }else if(dojo.lang.inArray(["radio", "checkbox"], type)){
  3552. if(elm.checked){
  3553. values.push(name + "=" + enc(elm.value));
  3554. }
  3555. }else{
  3556. values.push(name + "=" + enc(elm.value));
  3557. }
  3558. }
  3559. // now collect input type="image", which doesn't show up in the elements array
  3560. var inputs = formNode.getElementsByTagName("input");
  3561. for(var i = 0; i < inputs.length; i++) {
  3562. var input = inputs[i];
  3563. if(input.type.toLowerCase() == "image" && input.form == formNode
  3564. && formFilter(input)) {
  3565. var name = enc(input.name);
  3566. values.push(name + "=" + enc(input.value));
  3567. values.push(name + ".x=0");
  3568. values.push(name + ".y=0");
  3569. }
  3570. }
  3571. return values.join("&") + "&";
  3572. }
  3573. dojo.io.FormBind = function(args) {
  3574. this.bindArgs = {};
  3575. if(args && args.formNode) {
  3576. this.init(args);
  3577. } else if(args) {
  3578. this.init({formNode: args});
  3579. }
  3580. }
  3581. dojo.lang.extend(dojo.io.FormBind, {
  3582. form: null,
  3583. bindArgs: null,
  3584. clickedButton: null,
  3585. init: function(args) {
  3586. var form = dojo.byId(args.formNode);
  3587. if(!form || !form.tagName || form.tagName.toLowerCase() != "form") {
  3588. throw new Error("FormBind: Couldn't apply, invalid form");
  3589. } else if(this.form == form) {
  3590. return;
  3591. } else if(this.form) {
  3592. throw new Error("FormBind: Already applied to a form");
  3593. }
  3594. dojo.lang.mixin(this.bindArgs, args);
  3595. this.form = form;
  3596. this.connect(form, "onsubmit", "submit");
  3597. for(var i = 0; i < form.elements.length; i++) {
  3598. var node = form.elements[i];
  3599. if(node && node.type && dojo.lang.inArray(["submit", "button"], node.type.toLowerCase())) {
  3600. this.connect(node, "onclick", "click");
  3601. }
  3602. }
  3603. var inputs = form.getElementsByTagName("input");
  3604. for(var i = 0; i < inputs.length; i++) {
  3605. var input = inputs[i];
  3606. if(input.type.toLowerCase() == "image" && input.form == form) {
  3607. this.connect(input, "onclick", "click");
  3608. }
  3609. }
  3610. },
  3611. onSubmit: function(form) {
  3612. return true;
  3613. },
  3614. submit: function(e) {
  3615. e.preventDefault();
  3616. if(this.onSubmit(this.form)) {
  3617. dojo.io.bind(dojo.lang.mixin(this.bindArgs, {
  3618. formFilter: dojo.lang.hitch(this, "formFilter")
  3619. }));
  3620. }
  3621. },
  3622. click: function(e) {
  3623. var node = e.currentTarget;
  3624. if(node.disabled) { return; }
  3625. this.clickedButton = node;
  3626. },
  3627. formFilter: function(node) {
  3628. var type = (node.type||"").toLowerCase();
  3629. var accept = false;
  3630. if(node.disabled || !node.name) {
  3631. accept = false;
  3632. } else if(dojo.lang.inArray(["submit", "button", "image"], type)) {
  3633. if(!this.clickedButton) { this.clickedButton = node; }
  3634. accept = node == this.clickedButton;
  3635. } else {
  3636. accept = !dojo.lang.inArray(["file", "submit", "reset", "button"], type);
  3637. }
  3638. return accept;
  3639. },
  3640. // in case you don't have dojo.event.* pulled in
  3641. connect: function(srcObj, srcFcn, targetFcn) {
  3642. if(dojo.evalObjPath("dojo.event.connect")) {
  3643. dojo.event.connect(srcObj, srcFcn, this, targetFcn);
  3644. } else {
  3645. var fcn = dojo.lang.hitch(this, targetFcn);
  3646. srcObj[srcFcn] = function(e) {
  3647. if(!e) { e = window.event; }
  3648. if(!e.currentTarget) { e.currentTarget = e.srcElement; }
  3649. if(!e.preventDefault) { e.preventDefault = function() { window.event.returnValue = false; } }
  3650. fcn(e);
  3651. }
  3652. }
  3653. }
  3654. });
  3655. dojo.io.XMLHTTPTransport = new function(){
  3656. var _this = this;
  3657. var _cache = {}; // FIXME: make this public? do we even need to?
  3658. this.useCache = false; // if this is true, we'll cache unless kwArgs.useCache = false
  3659. this.preventCache = false; // if this is true, we'll always force GET requests to cache
  3660. // FIXME: Should this even be a function? or do we just hard code it in the next 2 functions?
  3661. function getCacheKey(url, query, method) {
  3662. return url + "|" + query + "|" + method.toLowerCase();
  3663. }
  3664. function addToCache(url, query, method, http) {
  3665. _cache[getCacheKey(url, query, method)] = http;
  3666. }
  3667. function getFromCache(url, query, method) {
  3668. return _cache[getCacheKey(url, query, method)];
  3669. }
  3670. this.clearCache = function() {
  3671. _cache = {};
  3672. }
  3673. // moved successful load stuff here
  3674. function doLoad(kwArgs, http, url, query, useCache) {
  3675. if( ((http.status>=200)&&(http.status<300))|| // allow any 2XX response code
  3676. (http.status==304)|| // get it out of the cache
  3677. (location.protocol=="file:" && (http.status==0 || http.status==undefined))||
  3678. (location.protocol=="chrome:" && (http.status==0 || http.status==undefined))
  3679. ){
  3680. var ret;
  3681. if(kwArgs.method.toLowerCase() == "head"){
  3682. var headers = http.getAllResponseHeaders();
  3683. ret = {};
  3684. ret.toString = function(){ return headers; }
  3685. var values = headers.split(/[\r\n]+/g);
  3686. for(var i = 0; i < values.length; i++) {
  3687. var pair = values[i].match(/^([^:]+)\s*:\s*(.+)$/i);
  3688. if(pair) {
  3689. ret[pair[1]] = pair[2];
  3690. }
  3691. }
  3692. }else if(kwArgs.mimetype == "text/javascript"){
  3693. try{
  3694. ret = dj_eval(http.responseText);
  3695. }catch(e){
  3696. dojo.debug(e);
  3697. dojo.debug(http.responseText);
  3698. ret = null;
  3699. }
  3700. }else if(kwArgs.mimetype == "text/json" || kwArgs.mimetype == "application/json"){
  3701. try{
  3702. ret = dj_eval("("+http.responseText+")");
  3703. }catch(e){
  3704. dojo.debug(e);
  3705. dojo.debug(http.responseText);
  3706. ret = false;
  3707. }
  3708. }else if((kwArgs.mimetype == "application/xml")||
  3709. (kwArgs.mimetype == "text/xml")){
  3710. ret = http.responseXML;
  3711. if(!ret || typeof ret == "string" || !http.getResponseHeader("Content-Type")) {
  3712. ret = dojo.dom.createDocumentFromText(http.responseText);
  3713. }
  3714. }else{
  3715. ret = http.responseText;
  3716. }
  3717. if(useCache){ // only cache successful responses
  3718. addToCache(url, query, kwArgs.method, http);
  3719. }
  3720. kwArgs[(typeof kwArgs.load == "function") ? "load" : "handle"]("load", ret, http, kwArgs);
  3721. }else{
  3722. var errObj = new dojo.io.Error("XMLHttpTransport Error: "+http.status+" "+http.statusText);
  3723. kwArgs[(typeof kwArgs.error == "function") ? "error" : "handle"]("error", errObj, http, kwArgs);
  3724. }
  3725. }
  3726. // set headers (note: Content-Type will get overriden if kwArgs.contentType is set)
  3727. function setHeaders(http, kwArgs){
  3728. if(kwArgs["headers"]) {
  3729. for(var header in kwArgs["headers"]) {
  3730. if(header.toLowerCase() == "content-type" && !kwArgs["contentType"]) {
  3731. kwArgs["contentType"] = kwArgs["headers"][header];
  3732. } else {
  3733. http.setRequestHeader(header, kwArgs["headers"][header]);
  3734. }
  3735. }
  3736. }
  3737. }
  3738. this.inFlight = [];
  3739. this.inFlightTimer = null;
  3740. this.startWatchingInFlight = function(){
  3741. if(!this.inFlightTimer){
  3742. // setInterval broken in mozilla x86_64 in some circumstances, see
  3743. // https://bugzilla.mozilla.org/show_bug.cgi?id=344439
  3744. // using setTimeout instead
  3745. this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
  3746. }
  3747. }
  3748. this.watchInFlight = function(){
  3749. var now = null;
  3750. // make sure sync calls stay thread safe, if this callback is called during a sync call
  3751. // and this results in another sync call before the first sync call ends the browser hangs
  3752. if(!dojo.hostenv._blockAsync && !_this._blockAsync){
  3753. for(var x=this.inFlight.length-1; x>=0; x--){
  3754. try{
  3755. var tif = this.inFlight[x];
  3756. if(!tif || tif.http._aborted || !tif.http.readyState){
  3757. this.inFlight.splice(x, 1); continue;
  3758. }
  3759. if(4==tif.http.readyState){
  3760. // remove it so we can clean refs
  3761. this.inFlight.splice(x, 1);
  3762. doLoad(tif.req, tif.http, tif.url, tif.query, tif.useCache);
  3763. }else if (tif.startTime){
  3764. //See if this is a timeout case.
  3765. if(!now){
  3766. now = (new Date()).getTime();
  3767. }
  3768. if(tif.startTime + (tif.req.timeoutSeconds * 1000) < now){
  3769. //Stop the request.
  3770. if(typeof tif.http.abort == "function"){
  3771. tif.http.abort();
  3772. }
  3773. // remove it so we can clean refs
  3774. this.inFlight.splice(x, 1);
  3775. tif.req[(typeof tif.req.timeout == "function") ? "timeout" : "handle"]("timeout", null, tif.http, tif.req);
  3776. }
  3777. }
  3778. }catch(e){
  3779. try{
  3780. var errObj = new dojo.io.Error("XMLHttpTransport.watchInFlight Error: " + e);
  3781. tif.req[(typeof tif.req.error == "function") ? "error" : "handle"]("error", errObj, tif.http, tif.req);
  3782. }catch(e2){
  3783. dojo.debug("XMLHttpTransport error callback failed: " + e2);
  3784. }
  3785. }
  3786. }
  3787. }
  3788. clearTimeout(this.inFlightTimer);
  3789. if(this.inFlight.length == 0){
  3790. this.inFlightTimer = null;
  3791. return;
  3792. }
  3793. this.inFlightTimer = setTimeout("dojo.io.XMLHTTPTransport.watchInFlight();", 10);
  3794. }
  3795. var hasXmlHttp = dojo.hostenv.getXmlhttpObject() ? true : false;
  3796. this.canHandle = function(kwArgs){
  3797. // canHandle just tells dojo.io.bind() if this is a good transport to
  3798. // use for the particular type of request.
  3799. // FIXME: we need to determine when form values need to be
  3800. // multi-part mime encoded and avoid using this transport for those
  3801. // requests.
  3802. return hasXmlHttp
  3803. && dojo.lang.inArray(["text/plain", "text/html", "application/xml", "text/xml", "text/javascript", "text/json", "application/json"], (kwArgs["mimetype"].toLowerCase()||""))
  3804. && !( kwArgs["formNode"] && dojo.io.formHasFile(kwArgs["formNode"]) );
  3805. }
  3806. this.multipartBoundary = "45309FFF-BD65-4d50-99C9-36986896A96F"; // unique guid as a boundary value for multipart posts
  3807. this.bind = function(kwArgs){
  3808. if(!kwArgs["url"]){
  3809. // are we performing a history action?
  3810. if( !kwArgs["formNode"]
  3811. && (kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"] || kwArgs["watchForURL"])
  3812. && (!djConfig.preventBackButtonFix)) {
  3813. dojo.deprecated("Using dojo.io.XMLHTTPTransport.bind() to add to browser history without doing an IO request",
  3814. "Use dojo.undo.browser.addToHistory() instead.", "0.4");
  3815. dojo.undo.browser.addToHistory(kwArgs);
  3816. return true;
  3817. }
  3818. }
  3819. // build this first for cache purposes
  3820. var url = kwArgs.url;
  3821. var query = "";
  3822. if(kwArgs["formNode"]){
  3823. var ta = kwArgs.formNode.getAttribute("action");
  3824. if((ta)&&(!kwArgs["url"])){ url = ta; }
  3825. var tp = kwArgs.formNode.getAttribute("method");
  3826. if((tp)&&(!kwArgs["method"])){ kwArgs.method = tp; }
  3827. query += dojo.io.encodeForm(kwArgs.formNode, kwArgs.encoding, kwArgs["formFilter"]);
  3828. }
  3829. if(url.indexOf("#") > -1) {
  3830. dojo.debug("Warning: dojo.io.bind: stripping hash values from url:", url);
  3831. url = url.split("#")[0];
  3832. }
  3833. if(kwArgs["file"]){
  3834. // force post for file transfer
  3835. kwArgs.method = "post";
  3836. }
  3837. if(!kwArgs["method"]){
  3838. kwArgs.method = "get";
  3839. }
  3840. // guess the multipart value
  3841. if(kwArgs.method.toLowerCase() == "get"){
  3842. // GET cannot use multipart
  3843. kwArgs.multipart = false;
  3844. }else{
  3845. if(kwArgs["file"]){
  3846. // enforce multipart when sending files
  3847. kwArgs.multipart = true;
  3848. }else if(!kwArgs["multipart"]){
  3849. // default
  3850. kwArgs.multipart = false;
  3851. }
  3852. }
  3853. if(kwArgs["backButton"] || kwArgs["back"] || kwArgs["changeUrl"]){
  3854. dojo.undo.browser.addToHistory(kwArgs);
  3855. }
  3856. var content = kwArgs["content"] || {};
  3857. if(kwArgs.sendTransport) {
  3858. content["dojo.transport"] = "xmlhttp";
  3859. }
  3860. do { // break-block
  3861. if(kwArgs.postContent){
  3862. query = kwArgs.postContent;
  3863. break;
  3864. }
  3865. if(content) {
  3866. query += dojo.io.argsFromMap(content, kwArgs.encoding);
  3867. }
  3868. if(kwArgs.method.toLowerCase() == "get" || !kwArgs.multipart){
  3869. break;
  3870. }
  3871. var t = [];
  3872. if(query.length){
  3873. var q = query.split("&");
  3874. for(var i = 0; i < q.length; ++i){
  3875. if(q[i].length){
  3876. var p = q[i].split("=");
  3877. t.push( "--" + this.multipartBoundary,
  3878. "Content-Disposition: form-data; name=\"" + p[0] + "\"",
  3879. "",
  3880. p[1]);
  3881. }
  3882. }
  3883. }
  3884. if(kwArgs.file){
  3885. if(dojo.lang.isArray(kwArgs.file)){
  3886. for(var i = 0; i < kwArgs.file.length; ++i){
  3887. var o = kwArgs.file[i];
  3888. t.push( "--" + this.multipartBoundary,
  3889. "Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
  3890. "Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
  3891. "",
  3892. o.content);
  3893. }
  3894. }else{
  3895. var o = kwArgs.file;
  3896. t.push( "--" + this.multipartBoundary,
  3897. "Content-Disposition: form-data; name=\"" + o.name + "\"; filename=\"" + ("fileName" in o ? o.fileName : o.name) + "\"",
  3898. "Content-Type: " + ("contentType" in o ? o.contentType : "application/octet-stream"),
  3899. "",
  3900. o.content);
  3901. }
  3902. }
  3903. if(t.length){
  3904. t.push("--"+this.multipartBoundary+"--", "");
  3905. query = t.join("\r\n");
  3906. }
  3907. }while(false);
  3908. // kwArgs.Connection = "close";
  3909. var async = kwArgs["sync"] ? false : true;
  3910. var preventCache = kwArgs["preventCache"] ||
  3911. (this.preventCache == true && kwArgs["preventCache"] != false);
  3912. var useCache = kwArgs["useCache"] == true ||
  3913. (this.useCache == true && kwArgs["useCache"] != false );
  3914. // preventCache is browser-level (add query string junk), useCache
  3915. // is for the local cache. If we say preventCache, then don't attempt
  3916. // to look in the cache, but if useCache is true, we still want to cache
  3917. // the response
  3918. if(!preventCache && useCache){
  3919. var cachedHttp = getFromCache(url, query, kwArgs.method);
  3920. if(cachedHttp){
  3921. doLoad(kwArgs, cachedHttp, url, query, false);
  3922. return;
  3923. }
  3924. }
  3925. // much of this is from getText, but reproduced here because we need
  3926. // more flexibility
  3927. var http = dojo.hostenv.getXmlhttpObject(kwArgs);
  3928. var received = false;
  3929. // build a handler function that calls back to the handler obj
  3930. if(async){
  3931. var startTime =
  3932. // FIXME: setting up this callback handler leaks on IE!!!
  3933. this.inFlight.push({
  3934. "req": kwArgs,
  3935. "http": http,
  3936. "url": url,
  3937. "query": query,
  3938. "useCache": useCache,
  3939. "startTime": kwArgs.timeoutSeconds ? (new Date()).getTime() : 0
  3940. });
  3941. this.startWatchingInFlight();
  3942. }else{
  3943. // block async callbacks until sync is in, needed in khtml, others?
  3944. _this._blockAsync = true;
  3945. }
  3946. if(kwArgs.method.toLowerCase() == "post"){
  3947. // FIXME: need to hack in more flexible Content-Type setting here!
  3948. if (!kwArgs.user) {
  3949. http.open("POST", url, async);
  3950. }else{
  3951. http.open("POST", url, async, kwArgs.user, kwArgs.password);
  3952. }
  3953. setHeaders(http, kwArgs);
  3954. http.setRequestHeader("Content-Type", kwArgs.multipart ? ("multipart/form-data; boundary=" + this.multipartBoundary) :
  3955. (kwArgs.contentType || "application/x-www-form-urlencoded"));
  3956. try{
  3957. http.send(query);
  3958. }catch(e){
  3959. if(typeof http.abort == "function"){
  3960. http.abort();
  3961. }
  3962. doLoad(kwArgs, {status: 404}, url, query, useCache);
  3963. }
  3964. }else{
  3965. var tmpUrl = url;
  3966. if(query != "") {
  3967. tmpUrl += (tmpUrl.indexOf("?") > -1 ? "&" : "?") + query;
  3968. }
  3969. if(preventCache) {
  3970. tmpUrl += (dojo.string.endsWithAny(tmpUrl, "?", "&")
  3971. ? "" : (tmpUrl.indexOf("?") > -1 ? "&" : "?")) + "dojo.preventCache=" + new Date().valueOf();
  3972. }
  3973. if (!kwArgs.user) {
  3974. http.open(kwArgs.method.toUpperCase(), tmpUrl, async);
  3975. }else{
  3976. http.open(kwArgs.method.toUpperCase(), tmpUrl, async, kwArgs.user, kwArgs.password);
  3977. }
  3978. setHeaders(http, kwArgs);
  3979. try {
  3980. http.send(null);
  3981. }catch(e) {
  3982. if(typeof http.abort == "function"){
  3983. http.abort();
  3984. }
  3985. doLoad(kwArgs, {status: 404}, url, query, useCache);
  3986. }
  3987. }
  3988. if( !async ) {
  3989. doLoad(kwArgs, http, url, query, useCache);
  3990. _this._blockAsync = false;
  3991. }
  3992. kwArgs.abort = function(){
  3993. try{// khtml doesent reset readyState on abort, need this workaround
  3994. http._aborted = true;
  3995. }catch(e){/*squelsh*/}
  3996. return http.abort();
  3997. }
  3998. return;
  3999. }
  4000. dojo.io.transports.addTransport("XMLHTTPTransport");
  4001. }
  4002. dojo.provide("dojo.io.cookie");
  4003. dojo.io.cookie.setCookie = function(name, value, days, path, domain, secure) {
  4004. var expires = -1;
  4005. if(typeof days == "number" && days >= 0) {
  4006. var d = new Date();
  4007. d.setTime(d.getTime()+(days*24*60*60*1000));
  4008. expires = d.toGMTString();
  4009. }
  4010. value = escape(value);
  4011. document.cookie = name + "=" + value + ";"
  4012. + (expires != -1 ? " expires=" + expires + ";" : "")
  4013. + (path ? "path=" + path : "")
  4014. + (domain ? "; domain=" + domain : "")
  4015. + (secure ? "; secure" : "");
  4016. }
  4017. dojo.io.cookie.set = dojo.io.cookie.setCookie;
  4018. dojo.io.cookie.getCookie = function(name) {
  4019. // FIXME: Which cookie should we return?
  4020. // If there are cookies set for different sub domains in the current
  4021. // scope there could be more than one cookie with the same name.
  4022. // I think taking the last one in the list takes the one from the
  4023. // deepest subdomain, which is what we're doing here.
  4024. var idx = document.cookie.lastIndexOf(name+'=');
  4025. if(idx == -1) { return null; }
  4026. var value = document.cookie.substring(idx+name.length+1);
  4027. var end = value.indexOf(';');
  4028. if(end == -1) { end = value.length; }
  4029. value = value.substring(0, end);
  4030. value = unescape(value);
  4031. return value;
  4032. }
  4033. dojo.io.cookie.get = dojo.io.cookie.getCookie;
  4034. dojo.io.cookie.deleteCookie = function(name) {
  4035. dojo.io.cookie.setCookie(name, "-", 0);
  4036. }
  4037. dojo.io.cookie.setObjectCookie = function(name, obj, days, path, domain, secure, clearCurrent) {
  4038. if(arguments.length == 5) { // for backwards compat
  4039. clearCurrent = domain;
  4040. domain = null;
  4041. secure = null;
  4042. }
  4043. var pairs = [], cookie, value = "";
  4044. if(!clearCurrent) { cookie = dojo.io.cookie.getObjectCookie(name); }
  4045. if(days >= 0) {
  4046. if(!cookie) { cookie = {}; }
  4047. for(var prop in obj) {
  4048. if(prop == null) {
  4049. delete cookie[prop];
  4050. } else if(typeof obj[prop] == "string" || typeof obj[prop] == "number") {
  4051. cookie[prop] = obj[prop];
  4052. }
  4053. }
  4054. prop = null;
  4055. for(var prop in cookie) {
  4056. pairs.push(escape(prop) + "=" + escape(cookie[prop]));
  4057. }
  4058. value = pairs.join("&");
  4059. }
  4060. dojo.io.cookie.setCookie(name, value, days, path, domain, secure);
  4061. }
  4062. dojo.io.cookie.getObjectCookie = function(name) {
  4063. var values = null, cookie = dojo.io.cookie.getCookie(name);
  4064. if(cookie) {
  4065. values = {};
  4066. var pairs = cookie.split("&");
  4067. for(var i = 0; i < pairs.length; i++) {
  4068. var pair = pairs[i].split("=");
  4069. var value = pair[1];
  4070. if( isNaN(value) ) { value = unescape(pair[1]); }
  4071. values[ unescape(pair[0]) ] = value;
  4072. }
  4073. }
  4074. return values;
  4075. }
  4076. dojo.io.cookie.isSupported = function() {
  4077. if(typeof navigator.cookieEnabled != "boolean") {
  4078. dojo.io.cookie.setCookie("__TestingYourBrowserForCookieSupport__",
  4079. "CookiesAllowed", 90, null);
  4080. var cookieVal = dojo.io.cookie.getCookie("__TestingYourBrowserForCookieSupport__");
  4081. navigator.cookieEnabled = (cookieVal == "CookiesAllowed");
  4082. if(navigator.cookieEnabled) {
  4083. // FIXME: should we leave this around?
  4084. this.deleteCookie("__TestingYourBrowserForCookieSupport__");
  4085. }
  4086. }
  4087. return navigator.cookieEnabled;
  4088. }
  4089. // need to leave this in for backwards-compat from 0.1 for when it gets pulled in by dojo.io.*
  4090. if(!dojo.io.cookies) { dojo.io.cookies = dojo.io.cookie; }
  4091. dojo.provide("dojo.io.*");
  4092. dojo.provide("dojo.io");
  4093. dojo.deprecated("dojo.io", "replaced by dojo.io.*", "0.5");
  4094. dojo.provide("dojo.event.common");
  4095. // TODO: connection filter functions
  4096. // these are functions that accept a method invocation (like around
  4097. // advice) and return a boolean based on it. That value determines
  4098. // whether or not the connection proceeds. It could "feel" like around
  4099. // advice for those who know what it is (calling proceed() or not),
  4100. // but I think presenting it as a "filter" and/or calling it with the
  4101. // function args and not the MethodInvocation might make it more
  4102. // palletable to "normal" users than around-advice currently is
  4103. // TODO: execution scope mangling
  4104. // YUI's event facility by default executes listeners in the context
  4105. // of the source object. This is very odd, but should probably be
  4106. // supported as an option (both for the source and for the dest). It
  4107. // can be thought of as a connection-specific hitch().
  4108. // TODO: more resiliency for 4+ arguments to connect()
  4109. dojo.event = new function(){
  4110. this._canTimeout = dojo.lang.isFunction(dj_global["setTimeout"])||dojo.lang.isAlien(dj_global["setTimeout"]);
  4111. // FIXME: where should we put this method (not here!)?
  4112. function interpolateArgs(args, searchForNames){
  4113. var dl = dojo.lang;
  4114. var ao = {
  4115. srcObj: dj_global,
  4116. srcFunc: null,
  4117. adviceObj: dj_global,
  4118. adviceFunc: null,
  4119. aroundObj: null,
  4120. aroundFunc: null,
  4121. adviceType: (args.length>2) ? args[0] : "after",
  4122. precedence: "last",
  4123. once: false,
  4124. delay: null,
  4125. rate: 0,
  4126. adviceMsg: false
  4127. };
  4128. switch(args.length){
  4129. case 0: return;
  4130. case 1: return;
  4131. case 2:
  4132. ao.srcFunc = args[0];
  4133. ao.adviceFunc = args[1];
  4134. break;
  4135. case 3:
  4136. if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isString(args[2]))){
  4137. ao.adviceType = "after";
  4138. ao.srcObj = args[0];
  4139. ao.srcFunc = args[1];
  4140. ao.adviceFunc = args[2];
  4141. }else if((dl.isString(args[1]))&&(dl.isString(args[2]))){
  4142. ao.srcFunc = args[1];
  4143. ao.adviceFunc = args[2];
  4144. }else if((dl.isObject(args[0]))&&(dl.isString(args[1]))&&(dl.isFunction(args[2]))){
  4145. ao.adviceType = "after";
  4146. ao.srcObj = args[0];
  4147. ao.srcFunc = args[1];
  4148. var tmpName = dl.nameAnonFunc(args[2], ao.adviceObj, searchForNames);
  4149. ao.adviceFunc = tmpName;
  4150. }else if((dl.isFunction(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))){
  4151. ao.adviceType = "after";
  4152. ao.srcObj = dj_global;
  4153. var tmpName = dl.nameAnonFunc(args[0], ao.srcObj, searchForNames);
  4154. ao.srcFunc = tmpName;
  4155. ao.adviceObj = args[1];
  4156. ao.adviceFunc = args[2];
  4157. }
  4158. break;
  4159. case 4:
  4160. if((dl.isObject(args[0]))&&(dl.isObject(args[2]))){
  4161. // we can assume that we've got an old-style "connect" from
  4162. // the sigslot school of event attachment. We therefore
  4163. // assume after-advice.
  4164. ao.adviceType = "after";
  4165. ao.srcObj = args[0];
  4166. ao.srcFunc = args[1];
  4167. ao.adviceObj = args[2];
  4168. ao.adviceFunc = args[3];
  4169. }else if((dl.isString(args[0]))&&(dl.isString(args[1]))&&(dl.isObject(args[2]))){
  4170. ao.adviceType = args[0];
  4171. ao.srcObj = dj_global;
  4172. ao.srcFunc = args[1];
  4173. ao.adviceObj = args[2];
  4174. ao.adviceFunc = args[3];
  4175. }else if((dl.isString(args[0]))&&(dl.isFunction(args[1]))&&(dl.isObject(args[2]))){
  4176. ao.adviceType = args[0];
  4177. ao.srcObj = dj_global;
  4178. var tmpName = dl.nameAnonFunc(args[1], dj_global, searchForNames);
  4179. ao.srcFunc = tmpName;
  4180. ao.adviceObj = args[2];
  4181. ao.adviceFunc = args[3];
  4182. }else if((dl.isString(args[0]))&&(dl.isObject(args[1]))&&(dl.isString(args[2]))&&(dl.isFunction(args[3]))){
  4183. ao.srcObj = args[1];
  4184. ao.srcFunc = args[2];
  4185. var tmpName = dl.nameAnonFunc(args[3], dj_global, searchForNames);
  4186. ao.adviceObj = dj_global;
  4187. ao.adviceFunc = tmpName;
  4188. }else if(dl.isObject(args[1])){
  4189. ao.srcObj = args[1];
  4190. ao.srcFunc = args[2];
  4191. ao.adviceObj = dj_global;
  4192. ao.adviceFunc = args[3];
  4193. }else if(dl.isObject(args[2])){
  4194. ao.srcObj = dj_global;
  4195. ao.srcFunc = args[1];
  4196. ao.adviceObj = args[2];
  4197. ao.adviceFunc = args[3];
  4198. }else{
  4199. ao.srcObj = ao.adviceObj = ao.aroundObj = dj_global;
  4200. ao.srcFunc = args[1];
  4201. ao.adviceFunc = args[2];
  4202. ao.aroundFunc = args[3];
  4203. }
  4204. break;
  4205. case 6:
  4206. ao.srcObj = args[1];
  4207. ao.srcFunc = args[2];
  4208. ao.adviceObj = args[3]
  4209. ao.adviceFunc = args[4];
  4210. ao.aroundFunc = args[5];
  4211. ao.aroundObj = dj_global;
  4212. break;
  4213. default:
  4214. ao.srcObj = args[1];
  4215. ao.srcFunc = args[2];
  4216. ao.adviceObj = args[3]
  4217. ao.adviceFunc = args[4];
  4218. ao.aroundObj = args[5];
  4219. ao.aroundFunc = args[6];
  4220. ao.once = args[7];
  4221. ao.delay = args[8];
  4222. ao.rate = args[9];
  4223. ao.adviceMsg = args[10];
  4224. break;
  4225. }
  4226. if(dl.isFunction(ao.aroundFunc)){
  4227. var tmpName = dl.nameAnonFunc(ao.aroundFunc, ao.aroundObj, searchForNames);
  4228. ao.aroundFunc = tmpName;
  4229. }
  4230. if(dl.isFunction(ao.srcFunc)){
  4231. ao.srcFunc = dl.getNameInObj(ao.srcObj, ao.srcFunc);
  4232. }
  4233. if(dl.isFunction(ao.adviceFunc)){
  4234. ao.adviceFunc = dl.getNameInObj(ao.adviceObj, ao.adviceFunc);
  4235. }
  4236. if((ao.aroundObj)&&(dl.isFunction(ao.aroundFunc))){
  4237. ao.aroundFunc = dl.getNameInObj(ao.aroundObj, ao.aroundFunc);
  4238. }
  4239. if(!ao.srcObj){
  4240. dojo.raise("bad srcObj for srcFunc: "+ao.srcFunc);
  4241. }
  4242. if(!ao.adviceObj){
  4243. dojo.raise("bad adviceObj for adviceFunc: "+ao.adviceFunc);
  4244. }
  4245. if(!ao.adviceFunc){
  4246. dojo.debug("bad adviceFunc for srcFunc: "+ao.srcFunc);
  4247. dojo.debugShallow(ao);
  4248. }
  4249. return ao;
  4250. }
  4251. this.connect = function(/*...*/){
  4252. // summary:
  4253. // dojo.event.connect is the glue that holds most Dojo-based
  4254. // applications together. Most combinations of arguments are
  4255. // supported, with the connect() method attempting to disambiguate
  4256. // the implied types of positional parameters. The following will
  4257. // all work:
  4258. // dojo.event.connect("globalFunctionName1", "globalFunctionName2");
  4259. // dojo.event.connect(functionReference1, functionReference2);
  4260. // dojo.event.connect("globalFunctionName1", functionReference2);
  4261. // dojo.event.connect(functionReference1, "globalFunctionName2");
  4262. // dojo.event.connect(scope1, "functionName1", "globalFunctionName2");
  4263. // dojo.event.connect("globalFunctionName1", scope2, "functionName2");
  4264. // dojo.event.connect(scope1, "functionName1", scope2, "functionName2");
  4265. // dojo.event.connect("after", scope1, "functionName1", scope2, "functionName2");
  4266. // dojo.event.connect("before", scope1, "functionName1", scope2, "functionName2");
  4267. // dojo.event.connect("around", scope1, "functionName1",
  4268. // scope2, "functionName2",
  4269. // aroundFunctionReference);
  4270. // dojo.event.connect("around", scope1, "functionName1",
  4271. // scope2, "functionName2",
  4272. // scope3, "aroundFunctionName");
  4273. // dojo.event.connect("before-around", scope1, "functionName1",
  4274. // scope2, "functionName2",
  4275. // aroundFunctionReference);
  4276. // dojo.event.connect("after-around", scope1, "functionName1",
  4277. // scope2, "functionName2",
  4278. // aroundFunctionReference);
  4279. // dojo.event.connect("after-around", scope1, "functionName1",
  4280. // scope2, "functionName2",
  4281. // scope3, "aroundFunctionName");
  4282. // dojo.event.connect("around", scope1, "functionName1",
  4283. // scope2, "functionName2",
  4284. // scope3, "aroundFunctionName", true, 30);
  4285. // dojo.event.connect("around", scope1, "functionName1",
  4286. // scope2, "functionName2",
  4287. // scope3, "aroundFunctionName", null, null, 10);
  4288. // adviceType:
  4289. // Optional. String. One of "before", "after", "around",
  4290. // "before-around", or "after-around". FIXME
  4291. // srcObj:
  4292. // the scope in which to locate/execute the named srcFunc. Along
  4293. // with srcFunc, this creates a way to dereference the function to
  4294. // call. So if the function in question is "foo.bar", the
  4295. // srcObj/srcFunc pair would be foo and "bar", where "bar" is a
  4296. // string and foo is an object reference.
  4297. // srcFunc:
  4298. // the name of the function to connect to. When it is executed,
  4299. // the listener being registered with this call will be called.
  4300. // The adviceType defines the call order between the source and
  4301. // the target functions.
  4302. // adviceObj:
  4303. // the scope in which to locate/execute the named adviceFunc.
  4304. // adviceFunc:
  4305. // the name of the function being conected to srcObj.srcFunc
  4306. // aroundObj:
  4307. // the scope in which to locate/execute the named aroundFunc.
  4308. // aroundFunc:
  4309. // the name of, or a reference to, the function that will be used
  4310. // to mediate the advice call. Around advice requires a special
  4311. // unary function that will be passed a "MethodInvocation" object.
  4312. // These objects have several important properties, namely:
  4313. // - args
  4314. // a mutable array of arguments to be passed into the
  4315. // wrapped function
  4316. // - proceed
  4317. // a function that "continues" the invocation. The result
  4318. // of this function is the return of the wrapped function.
  4319. // You can then manipulate this return before passing it
  4320. // back out (or take further action based on it).
  4321. // once:
  4322. // boolean that determines whether or not this connect() will
  4323. // create a new connection if an identical connect() has already
  4324. // been made. Defaults to "false".
  4325. // delay:
  4326. // an optional delay (in ms), as an integer, for dispatch of a
  4327. // listener after the source has been fired.
  4328. // rate:
  4329. // an optional rate throttling parameter (integer, in ms). When
  4330. // specified, this particular connection will not fire more than
  4331. // once in the interval specified by the rate
  4332. // adviceMsg:
  4333. // boolean. Should the listener have all the parameters passed in
  4334. // as a single argument?
  4335. /*
  4336. ao.adviceType = args[0];
  4337. ao.srcObj = args[1];
  4338. ao.srcFunc = args[2];
  4339. ao.adviceObj = args[3]
  4340. ao.adviceFunc = args[4];
  4341. ao.aroundObj = args[5];
  4342. ao.aroundFunc = args[6];
  4343. ao.once = args[7];
  4344. ao.delay = args[8];
  4345. ao.rate = args[9];
  4346. ao.adviceMsg = args[10];
  4347. */
  4348. if(arguments.length == 1){
  4349. var ao = arguments[0];
  4350. }else{
  4351. var ao = interpolateArgs(arguments, true);
  4352. }
  4353. if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
  4354. if(dojo.render.html.ie){
  4355. ao.srcFunc = "onkeydown";
  4356. this.connect(ao);
  4357. }
  4358. ao.srcFunc = "onkeypress";
  4359. }
  4360. if(dojo.lang.isArray(ao.srcObj) && ao.srcObj!=""){
  4361. var tmpAO = {};
  4362. for(var x in ao){
  4363. tmpAO[x] = ao[x];
  4364. }
  4365. var mjps = [];
  4366. dojo.lang.forEach(ao.srcObj, function(src){
  4367. if((dojo.render.html.capable)&&(dojo.lang.isString(src))){
  4368. src = dojo.byId(src);
  4369. // dojo.debug(src);
  4370. }
  4371. tmpAO.srcObj = src;
  4372. // dojo.debug(tmpAO.srcObj, tmpAO.srcFunc);
  4373. // dojo.debug(tmpAO.adviceObj, tmpAO.adviceFunc);
  4374. mjps.push(dojo.event.connect.call(dojo.event, tmpAO));
  4375. });
  4376. return mjps;
  4377. }
  4378. // FIXME: just doing a "getForMethod()" seems to be enough to put this into infinite recursion!!
  4379. var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
  4380. if(ao.adviceFunc){
  4381. var mjp2 = dojo.event.MethodJoinPoint.getForMethod(ao.adviceObj, ao.adviceFunc);
  4382. }
  4383. mjp.kwAddAdvice(ao);
  4384. // advanced users might want to fsck w/ the join point manually
  4385. return mjp; // a MethodJoinPoint object
  4386. }
  4387. this.log = function(/*object or funcName*/ a1, /*funcName*/ a2){
  4388. // summary:
  4389. // a function that will wrap and log all calls to the specified
  4390. // a1.a2() function. If only a1 is passed, it'll be used as a
  4391. // function or function name on the global context. Logging will
  4392. // be sent to dojo.debug
  4393. // a1:
  4394. // if a2 is passed, this should be an object. If not, it can be a
  4395. // function or function name.
  4396. // a2:
  4397. // a function name
  4398. var kwArgs;
  4399. if((arguments.length == 1)&&(typeof a1 == "object")){
  4400. kwArgs = a1;
  4401. }else{
  4402. kwArgs = {
  4403. srcObj: a1,
  4404. srcFunc: a2
  4405. };
  4406. }
  4407. kwArgs.adviceFunc = function(){
  4408. var argsStr = [];
  4409. for(var x=0; x<arguments.length; x++){
  4410. argsStr.push(arguments[x]);
  4411. }
  4412. dojo.debug("("+kwArgs.srcObj+")."+kwArgs.srcFunc, ":", argsStr.join(", "));
  4413. }
  4414. this.kwConnect(kwArgs);
  4415. }
  4416. this.connectBefore = function(){
  4417. // summary:
  4418. // takes the same parameters as dojo.event.connect(), except that
  4419. // the advice type will always be "before"
  4420. var args = ["before"];
  4421. for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
  4422. return this.connect.apply(this, args); // a MethodJoinPoint object
  4423. }
  4424. this.connectAround = function(){
  4425. // summary:
  4426. // takes the same parameters as dojo.event.connect(), except that
  4427. // the advice type will always be "around"
  4428. var args = ["around"];
  4429. for(var i = 0; i < arguments.length; i++){ args.push(arguments[i]); }
  4430. return this.connect.apply(this, args); // a MethodJoinPoint object
  4431. }
  4432. this.connectOnce = function(){
  4433. // summary:
  4434. // takes the same parameters as dojo.event.connect(), except that
  4435. // the "once" flag will always be set to "true"
  4436. var ao = interpolateArgs(arguments, true);
  4437. ao.once = true;
  4438. return this.connect(ao); // a MethodJoinPoint object
  4439. }
  4440. this._kwConnectImpl = function(kwArgs, disconnect){
  4441. var fn = (disconnect) ? "disconnect" : "connect";
  4442. if(typeof kwArgs["srcFunc"] == "function"){
  4443. kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
  4444. var tmpName = dojo.lang.nameAnonFunc(kwArgs.srcFunc, kwArgs.srcObj, true);
  4445. kwArgs.srcFunc = tmpName;
  4446. }
  4447. if(typeof kwArgs["adviceFunc"] == "function"){
  4448. kwArgs.adviceObj = kwArgs["adviceObj"]||dj_global;
  4449. var tmpName = dojo.lang.nameAnonFunc(kwArgs.adviceFunc, kwArgs.adviceObj, true);
  4450. kwArgs.adviceFunc = tmpName;
  4451. }
  4452. kwArgs.srcObj = kwArgs["srcObj"]||dj_global;
  4453. kwArgs.adviceObj = kwArgs["adviceObj"]||kwArgs["targetObj"]||dj_global;
  4454. kwArgs.adviceFunc = kwArgs["adviceFunc"]||kwArgs["targetFunc"];
  4455. // pass kwargs to avoid unrolling/repacking
  4456. return dojo.event[fn](kwArgs);
  4457. }
  4458. this.kwConnect = function(/*Object*/ kwArgs){
  4459. // summary:
  4460. // A version of dojo.event.connect() that takes a map of named
  4461. // parameters instead of the positional parameters that
  4462. // dojo.event.connect() uses. For many advanced connection types,
  4463. // this can be a much more readable (and potentially faster)
  4464. // alternative.
  4465. // kwArgs:
  4466. // An object that can have the following properties:
  4467. // - adviceType
  4468. // - srcObj
  4469. // - srcFunc
  4470. // - adviceObj
  4471. // - adviceFunc
  4472. // - aroundObj
  4473. // - aroundFunc
  4474. // - once
  4475. // - delay
  4476. // - rate
  4477. // - adviceMsg
  4478. // As with connect, only srcFunc and adviceFunc are generally
  4479. // required
  4480. return this._kwConnectImpl(kwArgs, false); // a MethodJoinPoint object
  4481. }
  4482. this.disconnect = function(){
  4483. // summary:
  4484. // Takes the same parameters as dojo.event.connect() but destroys
  4485. // an existing connection instead of building a new one. For
  4486. // multiple identical connections, multiple disconnect() calls
  4487. // will unroll one each time it's called.
  4488. if(arguments.length == 1){
  4489. var ao = arguments[0];
  4490. }else{
  4491. var ao = interpolateArgs(arguments, true);
  4492. }
  4493. if(!ao.adviceFunc){ return; } // nothing to disconnect
  4494. if(dojo.lang.isString(ao.srcFunc) && (ao.srcFunc.toLowerCase() == "onkey") ){
  4495. if(dojo.render.html.ie){
  4496. ao.srcFunc = "onkeydown";
  4497. this.disconnect(ao);
  4498. }
  4499. ao.srcFunc = "onkeypress";
  4500. }
  4501. var mjp = dojo.event.MethodJoinPoint.getForMethod(ao.srcObj, ao.srcFunc);
  4502. return mjp.removeAdvice(ao.adviceObj, ao.adviceFunc, ao.adviceType, ao.once); // a MethodJoinPoint object
  4503. }
  4504. this.kwDisconnect = function(kwArgs){
  4505. // summary:
  4506. // Takes the same parameters as dojo.event.kwConnect() but
  4507. // destroys an existing connection instead of building a new one.
  4508. return this._kwConnectImpl(kwArgs, true);
  4509. }
  4510. }
  4511. // exactly one of these is created whenever a method with a joint point is run,
  4512. // if there is at least one 'around' advice.
  4513. dojo.event.MethodInvocation = function(/*dojo.event.MethodJoinPoint*/join_point, /*Object*/obj, /*Array*/args){
  4514. // summary:
  4515. // a class the models the call into a function. This is used under the
  4516. // covers for all method invocations on both ends of a
  4517. // connect()-wrapped function dispatch. This allows us to "pickle"
  4518. // calls, such as in the case of around advice.
  4519. // join_point:
  4520. // a dojo.event.MethodJoinPoint object that represents a connection
  4521. // obj:
  4522. // the scope the call will execute in
  4523. // args:
  4524. // an array of parameters that will get passed to the callee
  4525. this.jp_ = join_point;
  4526. this.object = obj;
  4527. this.args = [];
  4528. // make sure we don't lock into a mutable object which can change under us.
  4529. // It's ok if the individual items change, though.
  4530. for(var x=0; x<args.length; x++){
  4531. this.args[x] = args[x];
  4532. }
  4533. // the index of the 'around' that is currently being executed.
  4534. this.around_index = -1;
  4535. }
  4536. dojo.event.MethodInvocation.prototype.proceed = function(){
  4537. // summary:
  4538. // proceed with the method call that's represented by this invocation
  4539. // object
  4540. this.around_index++;
  4541. if(this.around_index >= this.jp_.around.length){
  4542. return this.jp_.object[this.jp_.methodname].apply(this.jp_.object, this.args);
  4543. // return this.jp_.run_before_after(this.object, this.args);
  4544. }else{
  4545. var ti = this.jp_.around[this.around_index];
  4546. var mobj = ti[0]||dj_global;
  4547. var meth = ti[1];
  4548. return mobj[meth].call(mobj, this);
  4549. }
  4550. }
  4551. dojo.event.MethodJoinPoint = function(/*Object*/obj, /*String*/funcName){
  4552. this.object = obj||dj_global;
  4553. this.methodname = funcName;
  4554. this.methodfunc = this.object[funcName];
  4555. this.squelch = false;
  4556. // this.before = [];
  4557. // this.after = [];
  4558. // this.around = [];
  4559. }
  4560. dojo.event.MethodJoinPoint.getForMethod = function(/*Object*/obj, /*String*/funcName){
  4561. // summary:
  4562. // "static" class function for returning a MethodJoinPoint from a
  4563. // scoped function. If one doesn't exist, one is created.
  4564. // obj:
  4565. // the scope to search for the function in
  4566. // funcName:
  4567. // the name of the function to return a MethodJoinPoint for
  4568. if(!obj){ obj = dj_global; }
  4569. if(!obj[funcName]){
  4570. // supply a do-nothing method implementation
  4571. obj[funcName] = function(){};
  4572. if(!obj[funcName]){
  4573. // e.g. cannot add to inbuilt objects in IE6
  4574. dojo.raise("Cannot set do-nothing method on that object "+funcName);
  4575. }
  4576. }else if((!dojo.lang.isFunction(obj[funcName]))&&(!dojo.lang.isAlien(obj[funcName]))){
  4577. // FIXME: should we throw an exception here instead?
  4578. return null;
  4579. }
  4580. // we hide our joinpoint instance in obj[funcName + '$joinpoint']
  4581. var jpname = funcName + "$joinpoint";
  4582. var jpfuncname = funcName + "$joinpoint$method";
  4583. var joinpoint = obj[jpname];
  4584. if(!joinpoint){
  4585. var isNode = false;
  4586. if(dojo.event["browser"]){
  4587. if( (obj["attachEvent"])||
  4588. (obj["nodeType"])||
  4589. (obj["addEventListener"]) ){
  4590. isNode = true;
  4591. dojo.event.browser.addClobberNodeAttrs(obj, [jpname, jpfuncname, funcName]);
  4592. }
  4593. }
  4594. var origArity = obj[funcName].length;
  4595. obj[jpfuncname] = obj[funcName];
  4596. // joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, funcName);
  4597. joinpoint = obj[jpname] = new dojo.event.MethodJoinPoint(obj, jpfuncname);
  4598. obj[funcName] = function(){
  4599. var args = [];
  4600. if((isNode)&&(!arguments.length)){
  4601. var evt = null;
  4602. try{
  4603. if(obj.ownerDocument){
  4604. evt = obj.ownerDocument.parentWindow.event;
  4605. }else if(obj.documentElement){
  4606. evt = obj.documentElement.ownerDocument.parentWindow.event;
  4607. }else if(obj.event){ //obj is a window
  4608. evt = obj.event;
  4609. }else{
  4610. evt = window.event;
  4611. }
  4612. }catch(e){
  4613. evt = window.event;
  4614. }
  4615. if(evt){
  4616. args.push(dojo.event.browser.fixEvent(evt, this));
  4617. }
  4618. }else{
  4619. for(var x=0; x<arguments.length; x++){
  4620. if((x==0)&&(isNode)&&(dojo.event.browser.isEvent(arguments[x]))){
  4621. args.push(dojo.event.browser.fixEvent(arguments[x], this));
  4622. }else{
  4623. args.push(arguments[x]);
  4624. }
  4625. }
  4626. }
  4627. // return joinpoint.run.apply(joinpoint, arguments);
  4628. return joinpoint.run.apply(joinpoint, args);
  4629. }
  4630. obj[funcName].__preJoinArity = origArity;
  4631. }
  4632. return joinpoint; // dojo.event.MethodJoinPoint
  4633. }
  4634. dojo.lang.extend(dojo.event.MethodJoinPoint, {
  4635. unintercept: function(){
  4636. // summary:
  4637. // destroy the connection to all listeners that may have been
  4638. // registered on this joinpoint
  4639. this.object[this.methodname] = this.methodfunc;
  4640. this.before = [];
  4641. this.after = [];
  4642. this.around = [];
  4643. },
  4644. disconnect: dojo.lang.forward("unintercept"),
  4645. run: function(){
  4646. // summary:
  4647. // execute the connection represented by this join point. The
  4648. // arguments passed to run() will be passed to the function and
  4649. // its listeners.
  4650. var obj = this.object||dj_global;
  4651. var args = arguments;
  4652. // optimization. We only compute once the array version of the arguments
  4653. // pseudo-arr in order to prevent building it each time advice is unrolled.
  4654. var aargs = [];
  4655. for(var x=0; x<args.length; x++){
  4656. aargs[x] = args[x];
  4657. }
  4658. var unrollAdvice = function(marr){
  4659. if(!marr){
  4660. dojo.debug("Null argument to unrollAdvice()");
  4661. return;
  4662. }
  4663. var callObj = marr[0]||dj_global;
  4664. var callFunc = marr[1];
  4665. if(!callObj[callFunc]){
  4666. dojo.raise("function \"" + callFunc + "\" does not exist on \"" + callObj + "\"");
  4667. }
  4668. var aroundObj = marr[2]||dj_global;
  4669. var aroundFunc = marr[3];
  4670. var msg = marr[6];
  4671. var undef;
  4672. var to = {
  4673. args: [],
  4674. jp_: this,
  4675. object: obj,
  4676. proceed: function(){
  4677. return callObj[callFunc].apply(callObj, to.args);
  4678. }
  4679. };
  4680. to.args = aargs;
  4681. var delay = parseInt(marr[4]);
  4682. var hasDelay = ((!isNaN(delay))&&(marr[4]!==null)&&(typeof marr[4] != "undefined"));
  4683. if(marr[5]){
  4684. var rate = parseInt(marr[5]);
  4685. var cur = new Date();
  4686. var timerSet = false;
  4687. if((marr["last"])&&((cur-marr.last)<=rate)){
  4688. if(dojo.event._canTimeout){
  4689. if(marr["delayTimer"]){
  4690. clearTimeout(marr.delayTimer);
  4691. }
  4692. var tod = parseInt(rate*2); // is rate*2 naive?
  4693. var mcpy = dojo.lang.shallowCopy(marr);
  4694. marr.delayTimer = setTimeout(function(){
  4695. // FIXME: on IE at least, event objects from the
  4696. // browser can go out of scope. How (or should?) we
  4697. // deal with it?
  4698. mcpy[5] = 0;
  4699. unrollAdvice(mcpy);
  4700. }, tod);
  4701. }
  4702. return;
  4703. }else{
  4704. marr.last = cur;
  4705. }
  4706. }
  4707. // FIXME: need to enforce rates for a connection here!
  4708. if(aroundFunc){
  4709. // NOTE: around advice can't delay since we might otherwise depend
  4710. // on execution order!
  4711. aroundObj[aroundFunc].call(aroundObj, to);
  4712. }else{
  4713. // var tmjp = dojo.event.MethodJoinPoint.getForMethod(obj, methname);
  4714. if((hasDelay)&&((dojo.render.html)||(dojo.render.svg))){ // FIXME: the render checks are grotty!
  4715. dj_global["setTimeout"](function(){
  4716. if(msg){
  4717. callObj[callFunc].call(callObj, to);
  4718. }else{
  4719. callObj[callFunc].apply(callObj, args);
  4720. }
  4721. }, delay);
  4722. }else{ // many environments can't support delay!
  4723. if(msg){
  4724. callObj[callFunc].call(callObj, to);
  4725. }else{
  4726. callObj[callFunc].apply(callObj, args);
  4727. }
  4728. }
  4729. }
  4730. }
  4731. var unRollSquelch = function(){
  4732. if(this.squelch){
  4733. try{
  4734. return unrollAdvice.apply(this, arguments);
  4735. }catch(e){
  4736. dojo.debug(e);
  4737. }
  4738. }else{
  4739. return unrollAdvice.apply(this, arguments);
  4740. }
  4741. }
  4742. if((this["before"])&&(this.before.length>0)){
  4743. // pass a cloned array, if this event disconnects this event forEach on this.before wont work
  4744. dojo.lang.forEach(this.before.concat(new Array()), unRollSquelch);
  4745. }
  4746. var result;
  4747. try{
  4748. if((this["around"])&&(this.around.length>0)){
  4749. var mi = new dojo.event.MethodInvocation(this, obj, args);
  4750. result = mi.proceed();
  4751. }else if(this.methodfunc){
  4752. result = this.object[this.methodname].apply(this.object, args);
  4753. }
  4754. }catch(e){ if(!this.squelch){ dojo.raise(e); } }
  4755. if((this["after"])&&(this.after.length>0)){
  4756. // see comment on this.before above
  4757. dojo.lang.forEach(this.after.concat(new Array()), unRollSquelch);
  4758. }
  4759. return (this.methodfunc) ? result : null;
  4760. },
  4761. getArr: function(/*String*/kind){
  4762. // summary: return a list of listeners of the past "kind"
  4763. // kind:
  4764. // can be one of: "before", "after", "around", "before-around", or
  4765. // "after-around"
  4766. var type = "after";
  4767. // FIXME: we should be able to do this through props or Array.in()
  4768. if((typeof kind == "string")&&(kind.indexOf("before")!=-1)){
  4769. type = "before";
  4770. }else if(kind=="around"){
  4771. type = "around";
  4772. }
  4773. if(!this[type]){ this[type] = []; }
  4774. return this[type]; // Array
  4775. },
  4776. kwAddAdvice: function(/*Object*/args){
  4777. // summary:
  4778. // adds advice to the joinpoint with arguments in a map
  4779. // args:
  4780. // An object that can have the following properties:
  4781. // - adviceType
  4782. // - adviceObj
  4783. // - adviceFunc
  4784. // - aroundObj
  4785. // - aroundFunc
  4786. // - once
  4787. // - delay
  4788. // - rate
  4789. // - adviceMsg
  4790. this.addAdvice( args["adviceObj"], args["adviceFunc"],
  4791. args["aroundObj"], args["aroundFunc"],
  4792. args["adviceType"], args["precedence"],
  4793. args["once"], args["delay"], args["rate"],
  4794. args["adviceMsg"]);
  4795. },
  4796. addAdvice: function( thisAdviceObj, thisAdvice,
  4797. thisAroundObj, thisAround,
  4798. adviceType, precedence,
  4799. once, delay, rate, asMessage){
  4800. // summary:
  4801. // add advice to this joinpoint using positional parameters
  4802. // thisAdviceObj:
  4803. // the scope in which to locate/execute the named adviceFunc.
  4804. // thisAdviceFunc:
  4805. // the name of the function being conected
  4806. // thisAroundObj:
  4807. // the scope in which to locate/execute the named aroundFunc.
  4808. // thisAroundFunc:
  4809. // the name of the function that will be used to mediate the
  4810. // advice call.
  4811. // adviceType:
  4812. // Optional. String. One of "before", "after", "around",
  4813. // "before-around", or "after-around". FIXME
  4814. // once:
  4815. // boolean that determines whether or not this advice will create
  4816. // a new connection if an identical advice set has already been
  4817. // provided. Defaults to "false".
  4818. // delay:
  4819. // an optional delay (in ms), as an integer, for dispatch of a
  4820. // listener after the source has been fired.
  4821. // rate:
  4822. // an optional rate throttling parameter (integer, in ms). When
  4823. // specified, this particular connection will not fire more than
  4824. // once in the interval specified by the rate
  4825. // adviceMsg:
  4826. // boolean. Should the listener have all the parameters passed in
  4827. // as a single argument?
  4828. var arr = this.getArr(adviceType);
  4829. if(!arr){
  4830. dojo.raise("bad this: " + this);
  4831. }
  4832. var ao = [thisAdviceObj, thisAdvice, thisAroundObj, thisAround, delay, rate, asMessage];
  4833. if(once){
  4834. if(this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr) >= 0){
  4835. return;
  4836. }
  4837. }
  4838. if(precedence == "first"){
  4839. arr.unshift(ao);
  4840. }else{
  4841. arr.push(ao);
  4842. }
  4843. },
  4844. hasAdvice: function(thisAdviceObj, thisAdvice, adviceType, arr){
  4845. // summary:
  4846. // returns the array index of the first existing connection
  4847. // betweened the passed advice and this joinpoint. Will be -1 if
  4848. // none exists.
  4849. // thisAdviceObj:
  4850. // the scope in which to locate/execute the named adviceFunc.
  4851. // thisAdviceFunc:
  4852. // the name of the function being conected
  4853. // adviceType:
  4854. // Optional. String. One of "before", "after", "around",
  4855. // "before-around", or "after-around". FIXME
  4856. // arr:
  4857. // Optional. The list of advices to search. Will be found via
  4858. // adviceType if not passed
  4859. if(!arr){ arr = this.getArr(adviceType); }
  4860. var ind = -1;
  4861. for(var x=0; x<arr.length; x++){
  4862. var aao = (typeof thisAdvice == "object") ? (new String(thisAdvice)).toString() : thisAdvice;
  4863. var a1o = (typeof arr[x][1] == "object") ? (new String(arr[x][1])).toString() : arr[x][1];
  4864. if((arr[x][0] == thisAdviceObj)&&(a1o == aao)){
  4865. ind = x;
  4866. }
  4867. }
  4868. return ind; // Integer
  4869. },
  4870. removeAdvice: function(thisAdviceObj, thisAdvice, adviceType, once){
  4871. // summary:
  4872. // returns the array index of the first existing connection
  4873. // betweened the passed advice and this joinpoint. Will be -1 if
  4874. // none exists.
  4875. // thisAdviceObj:
  4876. // the scope in which to locate/execute the named adviceFunc.
  4877. // thisAdviceFunc:
  4878. // the name of the function being conected
  4879. // adviceType:
  4880. // Optional. String. One of "before", "after", "around",
  4881. // "before-around", or "after-around". FIXME
  4882. // once:
  4883. // Optional. Should this only remove the first occurance of the
  4884. // connection?
  4885. var arr = this.getArr(adviceType);
  4886. var ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
  4887. if(ind == -1){
  4888. return false;
  4889. }
  4890. while(ind != -1){
  4891. arr.splice(ind, 1);
  4892. if(once){ break; }
  4893. ind = this.hasAdvice(thisAdviceObj, thisAdvice, adviceType, arr);
  4894. }
  4895. return true;
  4896. }
  4897. });
  4898. dojo.provide("dojo.event.topic");
  4899. dojo.event.topic = new function(){
  4900. this.topics = {};
  4901. this.getTopic = function(/*String*/topic){
  4902. // summary:
  4903. // returns a topic implementation object of type
  4904. // dojo.event.topic.TopicImpl
  4905. // topic:
  4906. // a unique, opaque string that names the topic
  4907. if(!this.topics[topic]){
  4908. this.topics[topic] = new this.TopicImpl(topic);
  4909. }
  4910. return this.topics[topic]; // a dojo.event.topic.TopicImpl object
  4911. }
  4912. this.registerPublisher = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
  4913. // summary:
  4914. // registers a function as a publisher on a topic. Subsequent
  4915. // calls to the function will cause a publish event on the topic
  4916. // with the arguments passed to the function passed to registered
  4917. // listeners.
  4918. // topic:
  4919. // a unique, opaque string that names the topic
  4920. // obj:
  4921. // the scope to locate the function in
  4922. // funcName:
  4923. // the name of the function to register
  4924. var topic = this.getTopic(topic);
  4925. topic.registerPublisher(obj, funcName);
  4926. }
  4927. this.subscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
  4928. // summary:
  4929. // susbscribes the function to the topic. Subsequent events
  4930. // dispached to the topic will create a function call for the
  4931. // obj.funcName() function.
  4932. // topic:
  4933. // a unique, opaque string that names the topic
  4934. // obj:
  4935. // the scope to locate the function in
  4936. // funcName:
  4937. // the name of the function to being registered as a listener
  4938. var topic = this.getTopic(topic);
  4939. topic.subscribe(obj, funcName);
  4940. }
  4941. this.unsubscribe = function(/*String*/topic, /*Object*/obj, /*String*/funcName){
  4942. // summary:
  4943. // unsubscribes the obj.funcName() from the topic
  4944. // topic:
  4945. // a unique, opaque string that names the topic
  4946. // obj:
  4947. // the scope to locate the function in
  4948. // funcName:
  4949. // the name of the function to being unregistered as a listener
  4950. var topic = this.getTopic(topic);
  4951. topic.unsubscribe(obj, funcName);
  4952. }
  4953. this.destroy = function(/*String*/topic){
  4954. // summary:
  4955. // destroys the topic and unregisters all listeners
  4956. // topic:
  4957. // a unique, opaque string that names the topic
  4958. this.getTopic(topic).destroy();
  4959. delete this.topics[topic];
  4960. }
  4961. this.publishApply = function(/*String*/topic, /*Array*/args){
  4962. // summary:
  4963. // dispatches an event to the topic using the args array as the
  4964. // source for the call arguments to each listener. This is similar
  4965. // to JavaScript's built-in Function.apply()
  4966. // topic:
  4967. // a unique, opaque string that names the topic
  4968. // args:
  4969. // the arguments to be passed into listeners of the topic
  4970. var topic = this.getTopic(topic);
  4971. topic.sendMessage.apply(topic, args);
  4972. }
  4973. this.publish = function(/*String*/topic, /*Object*/message){
  4974. // summary:
  4975. // manually "publish" to the passed topic
  4976. // topic:
  4977. // a unique, opaque string that names the topic
  4978. // message:
  4979. // can be an array of parameters (similar to publishApply), or
  4980. // will be treated as one of many arguments to be passed along in
  4981. // a "flat" unrolling
  4982. var topic = this.getTopic(topic);
  4983. // if message is an array, we treat it as a set of arguments,
  4984. // otherwise, we just pass on the arguments passed in as-is
  4985. var args = [];
  4986. // could we use concat instead here?
  4987. for(var x=1; x<arguments.length; x++){
  4988. args.push(arguments[x]);
  4989. }
  4990. topic.sendMessage.apply(topic, args);
  4991. }
  4992. }
  4993. dojo.event.topic.TopicImpl = function(topicName){
  4994. // summary: a class to represent topics
  4995. this.topicName = topicName;
  4996. this.subscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
  4997. // summary:
  4998. // use dojo.event.connect() to attach the passed listener to the
  4999. // topic represented by this object
  5000. // listenerObject:
  5001. // if a string and listenerMethod is ommitted, this is treated as
  5002. // the name of a function in the global namespace. If
  5003. // listenerMethod is provided, this is the scope to find/execute
  5004. // the function in.
  5005. // listenerMethod:
  5006. // Optional. The function to register.
  5007. var tf = listenerMethod||listenerObject;
  5008. var to = (!listenerMethod) ? dj_global : listenerObject;
  5009. return dojo.event.kwConnect({ // dojo.event.MethodJoinPoint
  5010. srcObj: this,
  5011. srcFunc: "sendMessage",
  5012. adviceObj: to,
  5013. adviceFunc: tf
  5014. });
  5015. }
  5016. this.unsubscribe = function(/*Object*/listenerObject, /*Function or String*/listenerMethod){
  5017. // summary:
  5018. // use dojo.event.disconnect() to attach the passed listener to the
  5019. // topic represented by this object
  5020. // listenerObject:
  5021. // if a string and listenerMethod is ommitted, this is treated as
  5022. // the name of a function in the global namespace. If
  5023. // listenerMethod is provided, this is the scope to find the
  5024. // function in.
  5025. // listenerMethod:
  5026. // Optional. The function to unregister.
  5027. var tf = (!listenerMethod) ? listenerObject : listenerMethod;
  5028. var to = (!listenerMethod) ? null : listenerObject;
  5029. return dojo.event.kwDisconnect({ // dojo.event.MethodJoinPoint
  5030. srcObj: this,
  5031. srcFunc: "sendMessage",
  5032. adviceObj: to,
  5033. adviceFunc: tf
  5034. });
  5035. }
  5036. this._getJoinPoint = function(){
  5037. return dojo.event.MethodJoinPoint.getForMethod(this, "sendMessage");
  5038. }
  5039. this.setSquelch = function(/*Boolean*/shouldSquelch){
  5040. // summary:
  5041. // determine whether or not exceptions in the calling of a
  5042. // listener in the chain should stop execution of the chain.
  5043. this._getJoinPoint().squelch = shouldSquelch;
  5044. }
  5045. this.destroy = function(){
  5046. // summary: disconnects all listeners from this topic
  5047. this._getJoinPoint().disconnect();
  5048. }
  5049. this.registerPublisher = function( /*Object*/publisherObject,
  5050. /*Function or String*/publisherMethod){
  5051. // summary:
  5052. // registers the passed function as a publisher on this topic.
  5053. // Each time the function is called, an event will be published on
  5054. // this topic.
  5055. // publisherObject:
  5056. // if a string and listenerMethod is ommitted, this is treated as
  5057. // the name of a function in the global namespace. If
  5058. // listenerMethod is provided, this is the scope to find the
  5059. // function in.
  5060. // publisherMethod:
  5061. // Optional. The function to register.
  5062. dojo.event.connect(publisherObject, publisherMethod, this, "sendMessage");
  5063. }
  5064. this.sendMessage = function(message){
  5065. // summary: a stub to be called when a message is sent to the topic.
  5066. // The message has been propagated
  5067. }
  5068. }
  5069. dojo.provide("dojo.event.browser");
  5070. // FIXME: any particular reason this is in the global scope?
  5071. dojo._ie_clobber = new function(){
  5072. this.clobberNodes = [];
  5073. function nukeProp(node, prop){
  5074. // try{ node.removeAttribute(prop); }catch(e){ /* squelch */ }
  5075. try{ node[prop] = null; }catch(e){ /* squelch */ }
  5076. try{ delete node[prop]; }catch(e){ /* squelch */ }
  5077. // FIXME: JotLive needs this, but I'm not sure if it's too slow or not
  5078. try{ node.removeAttribute(prop); }catch(e){ /* squelch */ }
  5079. }
  5080. this.clobber = function(nodeRef){
  5081. var na;
  5082. var tna;
  5083. if(nodeRef){
  5084. tna = nodeRef.all || nodeRef.getElementsByTagName("*");
  5085. na = [nodeRef];
  5086. for(var x=0; x<tna.length; x++){
  5087. // if we're gonna be clobbering the thing, at least make sure
  5088. // we aren't trying to do it twice
  5089. if(tna[x]["__doClobber__"]){
  5090. na.push(tna[x]);
  5091. }
  5092. }
  5093. }else{
  5094. try{ window.onload = null; }catch(e){}
  5095. na = (this.clobberNodes.length) ? this.clobberNodes : document.all;
  5096. }
  5097. tna = null;
  5098. var basis = {};
  5099. for(var i = na.length-1; i>=0; i=i-1){
  5100. var el = na[i];
  5101. try{
  5102. if(el && el["__clobberAttrs__"]){
  5103. for(var j=0; j<el.__clobberAttrs__.length; j++){
  5104. nukeProp(el, el.__clobberAttrs__[j]);
  5105. }
  5106. nukeProp(el, "__clobberAttrs__");
  5107. nukeProp(el, "__doClobber__");
  5108. }
  5109. }catch(e){ /* squelch! */};
  5110. }
  5111. na = null;
  5112. }
  5113. }
  5114. if(dojo.render.html.ie){
  5115. dojo.addOnUnload(function(){
  5116. dojo._ie_clobber.clobber();
  5117. try{
  5118. if((dojo["widget"])&&(dojo.widget["manager"])){
  5119. dojo.widget.manager.destroyAll();
  5120. }
  5121. }catch(e){}
  5122. try{ window.onload = null; }catch(e){}
  5123. try{ window.onunload = null; }catch(e){}
  5124. dojo._ie_clobber.clobberNodes = [];
  5125. // CollectGarbage();
  5126. });
  5127. }
  5128. dojo.event.browser = new function(){
  5129. var clobberIdx = 0;
  5130. this.normalizedEventName = function(/*String*/eventName){
  5131. switch(eventName){
  5132. case "CheckboxStateChange":
  5133. case "DOMAttrModified":
  5134. case "DOMMenuItemActive":
  5135. case "DOMMenuItemInactive":
  5136. case "DOMMouseScroll":
  5137. case "DOMNodeInserted":
  5138. case "DOMNodeRemoved":
  5139. case "RadioStateChange":
  5140. return eventName;
  5141. break;
  5142. default:
  5143. return eventName.toLowerCase();
  5144. break;
  5145. }
  5146. }
  5147. this.clean = function(/*DOMNode*/node){
  5148. // summary:
  5149. // removes native event handlers so that destruction of the node
  5150. // will not leak memory. On most browsers this is a no-op, but
  5151. // it's critical for manual node removal on IE.
  5152. // node:
  5153. // A DOM node. All of it's children will also be cleaned.
  5154. if(dojo.render.html.ie){
  5155. dojo._ie_clobber.clobber(node);
  5156. }
  5157. }
  5158. this.addClobberNode = function(/*DOMNode*/node){
  5159. // summary:
  5160. // register the passed node to support event stripping
  5161. // node:
  5162. // A DOM node
  5163. if(!dojo.render.html.ie){ return; }
  5164. if(!node["__doClobber__"]){
  5165. node.__doClobber__ = true;
  5166. dojo._ie_clobber.clobberNodes.push(node);
  5167. // this might not be the most efficient thing to do, but it's
  5168. // much less error prone than other approaches which were
  5169. // previously tried and failed
  5170. node.__clobberAttrs__ = [];
  5171. }
  5172. }
  5173. this.addClobberNodeAttrs = function(/*DOMNode*/node, /*Array*/props){
  5174. // summary:
  5175. // register the passed node to support event stripping
  5176. // node:
  5177. // A DOM node to stip properties from later
  5178. // props:
  5179. // A list of propeties to strip from the node
  5180. if(!dojo.render.html.ie){ return; }
  5181. this.addClobberNode(node);
  5182. for(var x=0; x<props.length; x++){
  5183. node.__clobberAttrs__.push(props[x]);
  5184. }
  5185. }
  5186. this.removeListener = function( /*DOMNode*/ node,
  5187. /*String*/ evtName,
  5188. /*Function*/fp,
  5189. /*Boolean*/ capture){
  5190. // summary:
  5191. // clobbers the listener from the node
  5192. // evtName:
  5193. // the name of the handler to remove the function from
  5194. // node:
  5195. // DOM node to attach the event to
  5196. // fp:
  5197. // the function to register
  5198. // capture:
  5199. // Optional. should this listener prevent propigation?
  5200. if(!capture){ var capture = false; }
  5201. evtName = dojo.event.browser.normalizedEventName(evtName);
  5202. if( (evtName == "onkey") || (evtName == "key") ){
  5203. if(dojo.render.html.ie){
  5204. this.removeListener(node, "onkeydown", fp, capture);
  5205. }
  5206. evtName = "onkeypress";
  5207. }
  5208. if(evtName.substr(0,2)=="on"){ evtName = evtName.substr(2); }
  5209. // FIXME: this is mostly a punt, we aren't actually doing anything on IE
  5210. if(node.removeEventListener){
  5211. node.removeEventListener(evtName, fp, capture);
  5212. }
  5213. }
  5214. this.addListener = function(/*DOMNode*/node, /*String*/evtName, /*Function*/fp, /*Boolean*/capture, /*Boolean*/dontFix){
  5215. // summary:
  5216. // adds a listener to the node
  5217. // evtName:
  5218. // the name of the handler to add the listener to can be either of
  5219. // the form "onclick" or "click"
  5220. // node:
  5221. // DOM node to attach the event to
  5222. // fp:
  5223. // the function to register
  5224. // capture:
  5225. // Optional. Should this listener prevent propigation?
  5226. // dontFix:
  5227. // Optional. Should we avoid registering a new closure around the
  5228. // listener to enable fixEvent for dispatch of the registered
  5229. // function?
  5230. if(!node){ return; } // FIXME: log and/or bail?
  5231. if(!capture){ var capture = false; }
  5232. evtName = dojo.event.browser.normalizedEventName(evtName);
  5233. if( (evtName == "onkey") || (evtName == "key") ){
  5234. if(dojo.render.html.ie){
  5235. this.addListener(node, "onkeydown", fp, capture, dontFix);
  5236. }
  5237. evtName = "onkeypress";
  5238. }
  5239. if(evtName.substr(0,2)!="on"){ evtName = "on"+evtName; }
  5240. if(!dontFix){
  5241. // build yet another closure around fp in order to inject fixEvent
  5242. // around the resulting event
  5243. var newfp = function(evt){
  5244. if(!evt){ evt = window.event; }
  5245. var ret = fp(dojo.event.browser.fixEvent(evt, this));
  5246. if(capture){
  5247. dojo.event.browser.stopEvent(evt);
  5248. }
  5249. return ret;
  5250. }
  5251. }else{
  5252. newfp = fp;
  5253. }
  5254. if(node.addEventListener){
  5255. node.addEventListener(evtName.substr(2), newfp, capture);
  5256. return newfp;
  5257. }else{
  5258. if(typeof node[evtName] == "function" ){
  5259. var oldEvt = node[evtName];
  5260. node[evtName] = function(e){
  5261. oldEvt(e);
  5262. return newfp(e);
  5263. }
  5264. }else{
  5265. node[evtName]=newfp;
  5266. }
  5267. if(dojo.render.html.ie){
  5268. this.addClobberNodeAttrs(node, [evtName]);
  5269. }
  5270. return newfp;
  5271. }
  5272. }
  5273. this.isEvent = function(/*Object*/obj){
  5274. // summary:
  5275. // Tries to determine whether or not the object is a DOM event.
  5276. // FIXME: event detection hack ... could test for additional attributes
  5277. // if necessary
  5278. return (typeof obj != "undefined")&&(typeof Event != "undefined")&&(obj.eventPhase); // Boolean
  5279. // Event does not support instanceof in Opera, otherwise:
  5280. //return (typeof Event != "undefined")&&(obj instanceof Event);
  5281. }
  5282. this.currentEvent = null;
  5283. this.callListener = function(/*Function*/listener, /*DOMNode*/curTarget){
  5284. // summary:
  5285. // calls the specified listener in the context of the passed node
  5286. // with the current DOM event object as the only parameter
  5287. // listener:
  5288. // the function to call
  5289. // curTarget:
  5290. // the Node to call the function in the scope of
  5291. if(typeof listener != 'function'){
  5292. dojo.raise("listener not a function: " + listener);
  5293. }
  5294. dojo.event.browser.currentEvent.currentTarget = curTarget;
  5295. return listener.call(curTarget, dojo.event.browser.currentEvent);
  5296. }
  5297. this._stopPropagation = function(){
  5298. dojo.event.browser.currentEvent.cancelBubble = true;
  5299. }
  5300. this._preventDefault = function(){
  5301. dojo.event.browser.currentEvent.returnValue = false;
  5302. }
  5303. this.keys = {
  5304. KEY_BACKSPACE: 8,
  5305. KEY_TAB: 9,
  5306. KEY_CLEAR: 12,
  5307. KEY_ENTER: 13,
  5308. KEY_SHIFT: 16,
  5309. KEY_CTRL: 17,
  5310. KEY_ALT: 18,
  5311. KEY_PAUSE: 19,
  5312. KEY_CAPS_LOCK: 20,
  5313. KEY_ESCAPE: 27,
  5314. KEY_SPACE: 32,
  5315. KEY_PAGE_UP: 33,
  5316. KEY_PAGE_DOWN: 34,
  5317. KEY_END: 35,
  5318. KEY_HOME: 36,
  5319. KEY_LEFT_ARROW: 37,
  5320. KEY_UP_ARROW: 38,
  5321. KEY_RIGHT_ARROW: 39,
  5322. KEY_DOWN_ARROW: 40,
  5323. KEY_INSERT: 45,
  5324. KEY_DELETE: 46,
  5325. KEY_HELP: 47,
  5326. KEY_LEFT_WINDOW: 91,
  5327. KEY_RIGHT_WINDOW: 92,
  5328. KEY_SELECT: 93,
  5329. KEY_NUMPAD_0: 96,
  5330. KEY_NUMPAD_1: 97,
  5331. KEY_NUMPAD_2: 98,
  5332. KEY_NUMPAD_3: 99,
  5333. KEY_NUMPAD_4: 100,
  5334. KEY_NUMPAD_5: 101,
  5335. KEY_NUMPAD_6: 102,
  5336. KEY_NUMPAD_7: 103,
  5337. KEY_NUMPAD_8: 104,
  5338. KEY_NUMPAD_9: 105,
  5339. KEY_NUMPAD_MULTIPLY: 106,
  5340. KEY_NUMPAD_PLUS: 107,
  5341. KEY_NUMPAD_ENTER: 108,
  5342. KEY_NUMPAD_MINUS: 109,
  5343. KEY_NUMPAD_PERIOD: 110,
  5344. KEY_NUMPAD_DIVIDE: 111,
  5345. KEY_F1: 112,
  5346. KEY_F2: 113,
  5347. KEY_F3: 114,
  5348. KEY_F4: 115,
  5349. KEY_F5: 116,
  5350. KEY_F6: 117,
  5351. KEY_F7: 118,
  5352. KEY_F8: 119,
  5353. KEY_F9: 120,
  5354. KEY_F10: 121,
  5355. KEY_F11: 122,
  5356. KEY_F12: 123,
  5357. KEY_F13: 124,
  5358. KEY_F14: 125,
  5359. KEY_F15: 126,
  5360. KEY_NUM_LOCK: 144,
  5361. KEY_SCROLL_LOCK: 145
  5362. };
  5363. // reverse lookup
  5364. this.revKeys = [];
  5365. for(var key in this.keys){
  5366. this.revKeys[this.keys[key]] = key;
  5367. }
  5368. this.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){
  5369. // summary:
  5370. // normalizes properties on the event object including event
  5371. // bubbling methods, keystroke normalization, and x/y positions
  5372. // evt: the native event object
  5373. // sender: the node to treat as "currentTarget"
  5374. if(!evt){
  5375. if(window["event"]){
  5376. evt = window.event;
  5377. }
  5378. }
  5379. if((evt["type"])&&(evt["type"].indexOf("key") == 0)){ // key events
  5380. evt.keys = this.revKeys;
  5381. // FIXME: how can we eliminate this iteration?
  5382. for(var key in this.keys){
  5383. evt[key] = this.keys[key];
  5384. }
  5385. if(evt["type"] == "keydown" && dojo.render.html.ie){
  5386. switch(evt.keyCode){
  5387. case evt.KEY_SHIFT:
  5388. case evt.KEY_CTRL:
  5389. case evt.KEY_ALT:
  5390. case evt.KEY_CAPS_LOCK:
  5391. case evt.KEY_LEFT_WINDOW:
  5392. case evt.KEY_RIGHT_WINDOW:
  5393. case evt.KEY_SELECT:
  5394. case evt.KEY_NUM_LOCK:
  5395. case evt.KEY_SCROLL_LOCK:
  5396. // I'll get these in keypress after the OS munges them based on numlock
  5397. case evt.KEY_NUMPAD_0:
  5398. case evt.KEY_NUMPAD_1:
  5399. case evt.KEY_NUMPAD_2:
  5400. case evt.KEY_NUMPAD_3:
  5401. case evt.KEY_NUMPAD_4:
  5402. case evt.KEY_NUMPAD_5:
  5403. case evt.KEY_NUMPAD_6:
  5404. case evt.KEY_NUMPAD_7:
  5405. case evt.KEY_NUMPAD_8:
  5406. case evt.KEY_NUMPAD_9:
  5407. case evt.KEY_NUMPAD_PERIOD:
  5408. break; // just ignore the keys that can morph
  5409. case evt.KEY_NUMPAD_MULTIPLY:
  5410. case evt.KEY_NUMPAD_PLUS:
  5411. case evt.KEY_NUMPAD_ENTER:
  5412. case evt.KEY_NUMPAD_MINUS:
  5413. case evt.KEY_NUMPAD_DIVIDE:
  5414. break; // I could handle these but just pick them up in keypress
  5415. case evt.KEY_PAUSE:
  5416. case evt.KEY_TAB:
  5417. case evt.KEY_BACKSPACE:
  5418. case evt.KEY_ENTER:
  5419. case evt.KEY_ESCAPE:
  5420. case evt.KEY_PAGE_UP:
  5421. case evt.KEY_PAGE_DOWN:
  5422. case evt.KEY_END:
  5423. case evt.KEY_HOME:
  5424. case evt.KEY_LEFT_ARROW:
  5425. case evt.KEY_UP_ARROW:
  5426. case evt.KEY_RIGHT_ARROW:
  5427. case evt.KEY_DOWN_ARROW:
  5428. case evt.KEY_INSERT:
  5429. case evt.KEY_DELETE:
  5430. case evt.KEY_F1:
  5431. case evt.KEY_F2:
  5432. case evt.KEY_F3:
  5433. case evt.KEY_F4:
  5434. case evt.KEY_F5:
  5435. case evt.KEY_F6:
  5436. case evt.KEY_F7:
  5437. case evt.KEY_F8:
  5438. case evt.KEY_F9:
  5439. case evt.KEY_F10:
  5440. case evt.KEY_F11:
  5441. case evt.KEY_F12:
  5442. case evt.KEY_F12:
  5443. case evt.KEY_F13:
  5444. case evt.KEY_F14:
  5445. case evt.KEY_F15:
  5446. case evt.KEY_CLEAR:
  5447. case evt.KEY_HELP:
  5448. evt.key = evt.keyCode;
  5449. break;
  5450. default:
  5451. if(evt.ctrlKey || evt.altKey){
  5452. var unifiedCharCode = evt.keyCode;
  5453. // if lower case but keycode is uppercase, convert it
  5454. if(unifiedCharCode >= 65 && unifiedCharCode <= 90 && evt.shiftKey == false){
  5455. unifiedCharCode += 32;
  5456. }
  5457. if(unifiedCharCode >= 1 && unifiedCharCode <= 26 && evt.ctrlKey){
  5458. unifiedCharCode += 96; // 001-032 = ctrl+[a-z]
  5459. }
  5460. evt.key = String.fromCharCode(unifiedCharCode);
  5461. }
  5462. }
  5463. } else if(evt["type"] == "keypress"){
  5464. if(dojo.render.html.opera){
  5465. if(evt.which == 0){
  5466. evt.key = evt.keyCode;
  5467. }else if(evt.which > 0){
  5468. switch(evt.which){
  5469. case evt.KEY_SHIFT:
  5470. case evt.KEY_CTRL:
  5471. case evt.KEY_ALT:
  5472. case evt.KEY_CAPS_LOCK:
  5473. case evt.KEY_NUM_LOCK:
  5474. case evt.KEY_SCROLL_LOCK:
  5475. break;
  5476. case evt.KEY_PAUSE:
  5477. case evt.KEY_TAB:
  5478. case evt.KEY_BACKSPACE:
  5479. case evt.KEY_ENTER:
  5480. case evt.KEY_ESCAPE:
  5481. evt.key = evt.which;
  5482. break;
  5483. default:
  5484. var unifiedCharCode = evt.which;
  5485. if((evt.ctrlKey || evt.altKey || evt.metaKey) && (evt.which >= 65 && evt.which <= 90 && evt.shiftKey == false)){
  5486. unifiedCharCode += 32;
  5487. }
  5488. evt.key = String.fromCharCode(unifiedCharCode);
  5489. }
  5490. }
  5491. }else if(dojo.render.html.ie){ // catch some IE keys that are hard to get in keyDown
  5492. // key combinations were handled in onKeyDown
  5493. if(!evt.ctrlKey && !evt.altKey && evt.keyCode >= evt.KEY_SPACE){
  5494. evt.key = String.fromCharCode(evt.keyCode);
  5495. }
  5496. }else if(dojo.render.html.safari){
  5497. switch(evt.keyCode){
  5498. case 63232: evt.key = evt.KEY_UP_ARROW; break;
  5499. case 63233: evt.key = evt.KEY_DOWN_ARROW; break;
  5500. case 63234: evt.key = evt.KEY_LEFT_ARROW; break;
  5501. case 63235: evt.key = evt.KEY_RIGHT_ARROW; break;
  5502. default:
  5503. evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode;
  5504. }
  5505. }else{
  5506. evt.key = evt.charCode > 0 ? String.fromCharCode(evt.charCode) : evt.keyCode;
  5507. }
  5508. }
  5509. }
  5510. if(dojo.render.html.ie){
  5511. if(!evt.target){ evt.target = evt.srcElement; }
  5512. if(!evt.currentTarget){ evt.currentTarget = (sender ? sender : evt.srcElement); }
  5513. if(!evt.layerX){ evt.layerX = evt.offsetX; }
  5514. if(!evt.layerY){ evt.layerY = evt.offsetY; }
  5515. // FIXME: scroll position query is duped from dojo.html to avoid dependency on that entire module
  5516. // DONOT replace the following to use dojo.body(), in IE, document.documentElement should be used
  5517. // here rather than document.body
  5518. var doc = (evt.srcElement && evt.srcElement.ownerDocument) ? evt.srcElement.ownerDocument : document;
  5519. var docBody = ((dojo.render.html.ie55)||(doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement;
  5520. if(!evt.pageX){ evt.pageX = evt.clientX + (docBody.scrollLeft || 0) }
  5521. if(!evt.pageY){ evt.pageY = evt.clientY + (docBody.scrollTop || 0) }
  5522. // mouseover
  5523. if(evt.type == "mouseover"){ evt.relatedTarget = evt.fromElement; }
  5524. // mouseout
  5525. if(evt.type == "mouseout"){ evt.relatedTarget = evt.toElement; }
  5526. this.currentEvent = evt;
  5527. evt.callListener = this.callListener;
  5528. evt.stopPropagation = this._stopPropagation;
  5529. evt.preventDefault = this._preventDefault;
  5530. }
  5531. return evt; // Event
  5532. }
  5533. this.stopEvent = function(/*Event*/evt){
  5534. // summary:
  5535. // prevents propigation and clobbers the default action of the
  5536. // passed event
  5537. // evt: Optional for IE. The native event object.
  5538. if(window.event){
  5539. evt.returnValue = false;
  5540. evt.cancelBubble = true;
  5541. }else{
  5542. evt.preventDefault();
  5543. evt.stopPropagation();
  5544. }
  5545. }
  5546. }
  5547. dojo.provide("dojo.event.*");
  5548. dojo.provide("dojo.gfx.color");
  5549. // TODO: rewrite the "x2y" methods to take advantage of the parsing
  5550. // abilities of the Color object. Also, beef up the Color
  5551. // object (as possible) to parse most common formats
  5552. // takes an r, g, b, a(lpha) value, [r, g, b, a] array, "rgb(...)" string, hex string (#aaa, #aaaaaa, aaaaaaa)
  5553. dojo.gfx.color.Color = function(r, g, b, a) {
  5554. // dojo.debug("r:", r[0], "g:", r[1], "b:", r[2]);
  5555. if(dojo.lang.isArray(r)){
  5556. this.r = r[0];
  5557. this.g = r[1];
  5558. this.b = r[2];
  5559. this.a = r[3]||1.0;
  5560. }else if(dojo.lang.isString(r)){
  5561. var rgb = dojo.gfx.color.extractRGB(r);
  5562. this.r = rgb[0];
  5563. this.g = rgb[1];
  5564. this.b = rgb[2];
  5565. this.a = g||1.0;
  5566. }else if(r instanceof dojo.gfx.color.Color){
  5567. // why does this create a new instance if we were passed one?
  5568. this.r = r.r;
  5569. this.b = r.b;
  5570. this.g = r.g;
  5571. this.a = r.a;
  5572. }else{
  5573. this.r = r;
  5574. this.g = g;
  5575. this.b = b;
  5576. this.a = a;
  5577. }
  5578. }
  5579. dojo.gfx.color.Color.fromArray = function(arr) {
  5580. return new dojo.gfx.color.Color(arr[0], arr[1], arr[2], arr[3]);
  5581. }
  5582. dojo.extend(dojo.gfx.color.Color, {
  5583. toRgb: function(includeAlpha) {
  5584. if(includeAlpha) {
  5585. return this.toRgba();
  5586. } else {
  5587. return [this.r, this.g, this.b];
  5588. }
  5589. },
  5590. toRgba: function() {
  5591. return [this.r, this.g, this.b, this.a];
  5592. },
  5593. toHex: function() {
  5594. return dojo.gfx.color.rgb2hex(this.toRgb());
  5595. },
  5596. toCss: function() {
  5597. return "rgb(" + this.toRgb().join() + ")";
  5598. },
  5599. toString: function() {
  5600. return this.toHex(); // decent default?
  5601. },
  5602. blend: function(color, weight){
  5603. var rgb = null;
  5604. if(dojo.lang.isArray(color)){
  5605. rgb = color;
  5606. }else if(color instanceof dojo.gfx.color.Color){
  5607. rgb = color.toRgb();
  5608. }else{
  5609. rgb = new dojo.gfx.color.Color(color).toRgb();
  5610. }
  5611. return dojo.gfx.color.blend(this.toRgb(), rgb, weight);
  5612. }
  5613. });
  5614. dojo.gfx.color.named = {
  5615. white: [255,255,255],
  5616. black: [0,0,0],
  5617. red: [255,0,0],
  5618. green: [0,255,0],
  5619. lime: [0,255,0],
  5620. blue: [0,0,255],
  5621. navy: [0,0,128],
  5622. gray: [128,128,128],
  5623. silver: [192,192,192]
  5624. };
  5625. dojo.gfx.color.blend = function(a, b, weight){
  5626. // summary:
  5627. // blend colors a and b (both as RGB array or hex strings) with weight
  5628. // from -1 to +1, 0 being a 50/50 blend
  5629. if(typeof a == "string"){
  5630. return dojo.gfx.color.blendHex(a, b, weight);
  5631. }
  5632. if(!weight){
  5633. weight = 0;
  5634. }
  5635. weight = Math.min(Math.max(-1, weight), 1);
  5636. // alex: this interface blows.
  5637. // map -1 to 1 to the range 0 to 1
  5638. weight = ((weight + 1)/2);
  5639. var c = [];
  5640. // var stop = (1000*weight);
  5641. for(var x = 0; x < 3; x++){
  5642. c[x] = parseInt( b[x] + ( (a[x] - b[x]) * weight) );
  5643. }
  5644. return c;
  5645. }
  5646. // very convenient blend that takes and returns hex values
  5647. // (will get called automatically by blend when blend gets strings)
  5648. dojo.gfx.color.blendHex = function(a, b, weight) {
  5649. return dojo.gfx.color.rgb2hex(dojo.gfx.color.blend(dojo.gfx.color.hex2rgb(a), dojo.gfx.color.hex2rgb(b), weight));
  5650. }
  5651. // get RGB array from css-style color declarations
  5652. dojo.gfx.color.extractRGB = function(color) {
  5653. var hex = "0123456789abcdef";
  5654. color = color.toLowerCase();
  5655. if( color.indexOf("rgb") == 0 ) {
  5656. var matches = color.match(/rgba*\((\d+), *(\d+), *(\d+)/i);
  5657. var ret = matches.splice(1, 3);
  5658. return ret;
  5659. } else {
  5660. var colors = dojo.gfx.color.hex2rgb(color);
  5661. if(colors) {
  5662. return colors;
  5663. } else {
  5664. // named color (how many do we support?)
  5665. return dojo.gfx.color.named[color] || [255, 255, 255];
  5666. }
  5667. }
  5668. }
  5669. dojo.gfx.color.hex2rgb = function(hex) {
  5670. var hexNum = "0123456789ABCDEF";
  5671. var rgb = new Array(3);
  5672. if( hex.indexOf("#") == 0 ) { hex = hex.substring(1); }
  5673. hex = hex.toUpperCase();
  5674. if(hex.replace(new RegExp("["+hexNum+"]", "g"), "") != "") {
  5675. return null;
  5676. }
  5677. if( hex.length == 3 ) {
  5678. rgb[0] = hex.charAt(0) + hex.charAt(0)
  5679. rgb[1] = hex.charAt(1) + hex.charAt(1)
  5680. rgb[2] = hex.charAt(2) + hex.charAt(2);
  5681. } else {
  5682. rgb[0] = hex.substring(0, 2);
  5683. rgb[1] = hex.substring(2, 4);
  5684. rgb[2] = hex.substring(4);
  5685. }
  5686. for(var i = 0; i < rgb.length; i++) {
  5687. rgb[i] = hexNum.indexOf(rgb[i].charAt(0)) * 16 + hexNum.indexOf(rgb[i].charAt(1));
  5688. }
  5689. return rgb;
  5690. }
  5691. dojo.gfx.color.rgb2hex = function(r, g, b) {
  5692. if(dojo.lang.isArray(r)) {
  5693. g = r[1] || 0;
  5694. b = r[2] || 0;
  5695. r = r[0] || 0;
  5696. }
  5697. var ret = dojo.lang.map([r, g, b], function(x) {
  5698. x = new Number(x);
  5699. var s = x.toString(16);
  5700. while(s.length < 2) { s = "0" + s; }
  5701. return s;
  5702. });
  5703. ret.unshift("#");
  5704. return ret.join("");
  5705. }
  5706. dojo.provide("dojo.lfx.Animation");
  5707. /*
  5708. Animation package based on Dan Pupius' work: http://pupius.co.uk/js/Toolkit.Drawing.js
  5709. */
  5710. dojo.lfx.Line = function(/*int*/ start, /*int*/ end){
  5711. // summary: dojo.lfx.Line is the object used to generate values
  5712. // from a start value to an end value
  5713. this.start = start;
  5714. this.end = end;
  5715. if(dojo.lang.isArray(start)){
  5716. /* start: Array
  5717. end: Array
  5718. pId: a */
  5719. var diff = [];
  5720. dojo.lang.forEach(this.start, function(s,i){
  5721. diff[i] = this.end[i] - s;
  5722. }, this);
  5723. this.getValue = function(/*float*/ n){
  5724. var res = [];
  5725. dojo.lang.forEach(this.start, function(s, i){
  5726. res[i] = (diff[i] * n) + s;
  5727. }, this);
  5728. return res; // Array
  5729. }
  5730. }else{
  5731. var diff = end - start;
  5732. this.getValue = function(/*float*/ n){
  5733. // summary: returns the point on the line
  5734. // n: a floating point number greater than 0 and less than 1
  5735. return (diff * n) + this.start; // Decimal
  5736. }
  5737. }
  5738. }
  5739. dojo.lfx.easeDefault = function(/*Decimal?*/ n){
  5740. // summary: Returns the point for point n on a sin wave.
  5741. if(dojo.render.html.khtml){
  5742. // the cool kids are obviously not using konqueror...
  5743. // found a very wierd bug in floats constants, 1.5 evals as 1
  5744. // seems somebody mixed up ints and floats in 3.5.4 ??
  5745. // FIXME: investigate more and post a KDE bug (Fredrik)
  5746. return (parseFloat("0.5")+((Math.sin( (n+parseFloat("1.5")) * Math.PI))/2));
  5747. }else{
  5748. return (0.5+((Math.sin( (n+1.5) * Math.PI))/2));
  5749. }
  5750. }
  5751. dojo.lfx.easeIn = function(/*Decimal?*/ n){
  5752. // summary: returns the point on an easing curve
  5753. // n: a floating point number greater than 0 and less than 1
  5754. return Math.pow(n, 3);
  5755. }
  5756. dojo.lfx.easeOut = function(/*Decimal?*/ n){
  5757. // summary: returns the point on the line
  5758. // n: a floating point number greater than 0 and less than 1
  5759. return ( 1 - Math.pow(1 - n, 3) );
  5760. }
  5761. dojo.lfx.easeInOut = function(/*Decimal?*/ n){
  5762. // summary: returns the point on the line
  5763. // n: a floating point number greater than 0 and less than 1
  5764. return ( (3 * Math.pow(n, 2)) - (2 * Math.pow(n, 3)) );
  5765. }
  5766. dojo.lfx.IAnimation = function(){
  5767. // summary: dojo.lfx.IAnimation is an interface that implements
  5768. // commonly used functions of animation objects
  5769. }
  5770. dojo.lang.extend(dojo.lfx.IAnimation, {
  5771. // public properties
  5772. curve: null,
  5773. duration: 1000,
  5774. easing: null,
  5775. repeatCount: 0,
  5776. rate: 25,
  5777. // events
  5778. handler: null,
  5779. beforeBegin: null,
  5780. onBegin: null,
  5781. onAnimate: null,
  5782. onEnd: null,
  5783. onPlay: null,
  5784. onPause: null,
  5785. onStop: null,
  5786. // public methods
  5787. play: null,
  5788. pause: null,
  5789. stop: null,
  5790. connect: function(/*Event*/ evt, /*Object*/ scope, /*Function*/ newFunc){
  5791. // summary: Convenience function. Quickly connect to an event
  5792. // of this object and save the old functions connected to it.
  5793. // evt: The name of the event to connect to.
  5794. // scope: the scope in which to run newFunc.
  5795. // newFunc: the function to run when evt is fired.
  5796. if(!newFunc){
  5797. /* scope: Function
  5798. newFunc: null
  5799. pId: f */
  5800. newFunc = scope;
  5801. scope = this;
  5802. }
  5803. newFunc = dojo.lang.hitch(scope, newFunc);
  5804. var oldFunc = this[evt]||function(){};
  5805. this[evt] = function(){
  5806. var ret = oldFunc.apply(this, arguments);
  5807. newFunc.apply(this, arguments);
  5808. return ret;
  5809. }
  5810. return this; // dojo.lfx.IAnimation
  5811. },
  5812. fire: function(/*Event*/ evt, /*Array*/ args){
  5813. // summary: Convenience function. Fire event "evt" and pass it
  5814. // the arguments specified in "args".
  5815. // evt: The event to fire.
  5816. // args: The arguments to pass to the event.
  5817. if(this[evt]){
  5818. this[evt].apply(this, (args||[]));
  5819. }
  5820. return this; // dojo.lfx.IAnimation
  5821. },
  5822. repeat: function(/*int*/ count){
  5823. // summary: Set the repeat count of this object.
  5824. // count: How many times to repeat the animation.
  5825. this.repeatCount = count;
  5826. return this; // dojo.lfx.IAnimation
  5827. },
  5828. // private properties
  5829. _active: false,
  5830. _paused: false
  5831. });
  5832. dojo.lfx.Animation = function( /*Object*/ handlers,
  5833. /*int*/ duration,
  5834. /*dojo.lfx.Line*/ curve,
  5835. /*function*/ easing,
  5836. /*int*/ repeatCount,
  5837. /*int*/ rate){
  5838. // summary
  5839. // a generic animation object that fires callbacks into it's handlers
  5840. // object at various states
  5841. // handlers: { handler: Function?, onstart: Function?, onstop: Function?, onanimate: Function? }
  5842. dojo.lfx.IAnimation.call(this);
  5843. if(dojo.lang.isNumber(handlers)||(!handlers && duration.getValue)){
  5844. // no handlers argument:
  5845. rate = repeatCount;
  5846. repeatCount = easing;
  5847. easing = curve;
  5848. curve = duration;
  5849. duration = handlers;
  5850. handlers = null;
  5851. }else if(handlers.getValue||dojo.lang.isArray(handlers)){
  5852. // no handlers or duration:
  5853. rate = easing;
  5854. repeatCount = curve;
  5855. easing = duration;
  5856. curve = handlers;
  5857. duration = null;
  5858. handlers = null;
  5859. }
  5860. if(dojo.lang.isArray(curve)){
  5861. /* curve: Array
  5862. pId: a */
  5863. this.curve = new dojo.lfx.Line(curve[0], curve[1]);
  5864. }else{
  5865. this.curve = curve;
  5866. }
  5867. if(duration != null && duration > 0){ this.duration = duration; }
  5868. if(repeatCount){ this.repeatCount = repeatCount; }
  5869. if(rate){ this.rate = rate; }
  5870. if(handlers){
  5871. dojo.lang.forEach([
  5872. "handler", "beforeBegin", "onBegin",
  5873. "onEnd", "onPlay", "onStop", "onAnimate"
  5874. ], function(item){
  5875. if(handlers[item]){
  5876. this.connect(item, handlers[item]);
  5877. }
  5878. }, this);
  5879. }
  5880. if(easing && dojo.lang.isFunction(easing)){
  5881. this.easing=easing;
  5882. }
  5883. }
  5884. dojo.inherits(dojo.lfx.Animation, dojo.lfx.IAnimation);
  5885. dojo.lang.extend(dojo.lfx.Animation, {
  5886. // "private" properties
  5887. _startTime: null,
  5888. _endTime: null,
  5889. _timer: null,
  5890. _percent: 0,
  5891. _startRepeatCount: 0,
  5892. // public methods
  5893. play: function(/*int?*/ delay, /*bool?*/ gotoStart){
  5894. // summary: Start the animation.
  5895. // delay: How many milliseconds to delay before starting.
  5896. // gotoStart: If true, starts the animation from the beginning; otherwise,
  5897. // starts it from its current position.
  5898. if(gotoStart){
  5899. clearTimeout(this._timer);
  5900. this._active = false;
  5901. this._paused = false;
  5902. this._percent = 0;
  5903. }else if(this._active && !this._paused){
  5904. return this; // dojo.lfx.Animation
  5905. }
  5906. this.fire("handler", ["beforeBegin"]);
  5907. this.fire("beforeBegin");
  5908. if(delay > 0){
  5909. setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
  5910. return this; // dojo.lfx.Animation
  5911. }
  5912. this._startTime = new Date().valueOf();
  5913. if(this._paused){
  5914. this._startTime -= (this.duration * this._percent / 100);
  5915. }
  5916. this._endTime = this._startTime + this.duration;
  5917. this._active = true;
  5918. this._paused = false;
  5919. var step = this._percent / 100;
  5920. var value = this.curve.getValue(step);
  5921. if(this._percent == 0 ){
  5922. if(!this._startRepeatCount){
  5923. this._startRepeatCount = this.repeatCount;
  5924. }
  5925. this.fire("handler", ["begin", value]);
  5926. this.fire("onBegin", [value]);
  5927. }
  5928. this.fire("handler", ["play", value]);
  5929. this.fire("onPlay", [value]);
  5930. this._cycle();
  5931. return this; // dojo.lfx.Animation
  5932. },
  5933. pause: function(){
  5934. // summary: Pauses a running animation.
  5935. clearTimeout(this._timer);
  5936. if(!this._active){ return this; /*dojo.lfx.Animation*/}
  5937. this._paused = true;
  5938. var value = this.curve.getValue(this._percent / 100);
  5939. this.fire("handler", ["pause", value]);
  5940. this.fire("onPause", [value]);
  5941. return this; // dojo.lfx.Animation
  5942. },
  5943. gotoPercent: function(/*Decimal*/ pct, /*bool?*/ andPlay){
  5944. // summary: Sets the progress of the animation.
  5945. // pct: A percentage in decimal notation (between and including 0.0 and 1.0).
  5946. // andPlay: If true, play the animation after setting the progress.
  5947. clearTimeout(this._timer);
  5948. this._active = true;
  5949. this._paused = true;
  5950. this._percent = pct;
  5951. if(andPlay){ this.play(); }
  5952. return this; // dojo.lfx.Animation
  5953. },
  5954. stop: function(/*bool?*/ gotoEnd){
  5955. // summary: Stops a running animation.
  5956. // gotoEnd: If true, the animation will end.
  5957. clearTimeout(this._timer);
  5958. var step = this._percent / 100;
  5959. if(gotoEnd){
  5960. step = 1;
  5961. }
  5962. var value = this.curve.getValue(step);
  5963. this.fire("handler", ["stop", value]);
  5964. this.fire("onStop", [value]);
  5965. this._active = false;
  5966. this._paused = false;
  5967. return this; // dojo.lfx.Animation
  5968. },
  5969. status: function(){
  5970. // summary: Returns a string representation of the status of
  5971. // the animation.
  5972. if(this._active){
  5973. return this._paused ? "paused" : "playing"; // String
  5974. }else{
  5975. return "stopped"; // String
  5976. }
  5977. return this;
  5978. },
  5979. // "private" methods
  5980. _cycle: function(){
  5981. clearTimeout(this._timer);
  5982. if(this._active){
  5983. var curr = new Date().valueOf();
  5984. var step = (curr - this._startTime) / (this._endTime - this._startTime);
  5985. if(step >= 1){
  5986. step = 1;
  5987. this._percent = 100;
  5988. }else{
  5989. this._percent = step * 100;
  5990. }
  5991. // Perform easing
  5992. if((this.easing)&&(dojo.lang.isFunction(this.easing))){
  5993. step = this.easing(step);
  5994. }
  5995. var value = this.curve.getValue(step);
  5996. this.fire("handler", ["animate", value]);
  5997. this.fire("onAnimate", [value]);
  5998. if( step < 1 ){
  5999. this._timer = setTimeout(dojo.lang.hitch(this, "_cycle"), this.rate);
  6000. }else{
  6001. this._active = false;
  6002. this.fire("handler", ["end"]);
  6003. this.fire("onEnd");
  6004. if(this.repeatCount > 0){
  6005. this.repeatCount--;
  6006. this.play(null, true);
  6007. }else if(this.repeatCount == -1){
  6008. this.play(null, true);
  6009. }else{
  6010. if(this._startRepeatCount){
  6011. this.repeatCount = this._startRepeatCount;
  6012. this._startRepeatCount = 0;
  6013. }
  6014. }
  6015. }
  6016. }
  6017. return this; // dojo.lfx.Animation
  6018. }
  6019. });
  6020. dojo.lfx.Combine = function(/*dojo.lfx.IAnimation...*/ animations){
  6021. // summary: An animation object to play animations passed to it at the same time.
  6022. dojo.lfx.IAnimation.call(this);
  6023. this._anims = [];
  6024. this._animsEnded = 0;
  6025. var anims = arguments;
  6026. if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
  6027. /* animations: dojo.lfx.IAnimation[]
  6028. pId: a */
  6029. anims = anims[0];
  6030. }
  6031. dojo.lang.forEach(anims, function(anim){
  6032. this._anims.push(anim);
  6033. anim.connect("onEnd", dojo.lang.hitch(this, "_onAnimsEnded"));
  6034. }, this);
  6035. }
  6036. dojo.inherits(dojo.lfx.Combine, dojo.lfx.IAnimation);
  6037. dojo.lang.extend(dojo.lfx.Combine, {
  6038. // private members
  6039. _animsEnded: 0,
  6040. // public methods
  6041. play: function(/*int?*/ delay, /*bool?*/ gotoStart){
  6042. // summary: Start the animations.
  6043. // delay: How many milliseconds to delay before starting.
  6044. // gotoStart: If true, starts the animations from the beginning; otherwise,
  6045. // starts them from their current position.
  6046. if( !this._anims.length ){ return this; /*dojo.lfx.Combine*/}
  6047. this.fire("beforeBegin");
  6048. if(delay > 0){
  6049. setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
  6050. return this; // dojo.lfx.Combine
  6051. }
  6052. if(gotoStart || this._anims[0].percent == 0){
  6053. this.fire("onBegin");
  6054. }
  6055. this.fire("onPlay");
  6056. this._animsCall("play", null, gotoStart);
  6057. return this; // dojo.lfx.Combine
  6058. },
  6059. pause: function(){
  6060. // summary: Pauses the running animations.
  6061. this.fire("onPause");
  6062. this._animsCall("pause");
  6063. return this; // dojo.lfx.Combine
  6064. },
  6065. stop: function(/*bool?*/ gotoEnd){
  6066. // summary: Stops the running animations.
  6067. // gotoEnd: If true, the animations will end.
  6068. this.fire("onStop");
  6069. this._animsCall("stop", gotoEnd);
  6070. return this; // dojo.lfx.Combine
  6071. },
  6072. // private methods
  6073. _onAnimsEnded: function(){
  6074. this._animsEnded++;
  6075. if(this._animsEnded >= this._anims.length){
  6076. this.fire("onEnd");
  6077. }
  6078. return this; // dojo.lfx.Combine
  6079. },
  6080. _animsCall: function(/*String*/ funcName){
  6081. var args = [];
  6082. if(arguments.length > 1){
  6083. for(var i = 1 ; i < arguments.length ; i++){
  6084. args.push(arguments[i]);
  6085. }
  6086. }
  6087. var _this = this;
  6088. dojo.lang.forEach(this._anims, function(anim){
  6089. anim[funcName](args);
  6090. }, _this);
  6091. return this; // dojo.lfx.Combine
  6092. }
  6093. });
  6094. dojo.lfx.Chain = function(/*dojo.lfx.IAnimation...*/ animations) {
  6095. // summary: An animation object to play animations passed to it
  6096. // one after another.
  6097. dojo.lfx.IAnimation.call(this);
  6098. this._anims = [];
  6099. this._currAnim = -1;
  6100. var anims = arguments;
  6101. if(anims.length == 1 && (dojo.lang.isArray(anims[0]) || dojo.lang.isArrayLike(anims[0]))){
  6102. /* animations: dojo.lfx.IAnimation[]
  6103. pId: a */
  6104. anims = anims[0];
  6105. }
  6106. var _this = this;
  6107. dojo.lang.forEach(anims, function(anim, i, anims_arr){
  6108. this._anims.push(anim);
  6109. if(i < anims_arr.length - 1){
  6110. anim.connect("onEnd", dojo.lang.hitch(this, "_playNext") );
  6111. }else{
  6112. anim.connect("onEnd", dojo.lang.hitch(this, function(){ this.fire("onEnd"); }) );
  6113. }
  6114. }, this);
  6115. }
  6116. dojo.inherits(dojo.lfx.Chain, dojo.lfx.IAnimation);
  6117. dojo.lang.extend(dojo.lfx.Chain, {
  6118. // private members
  6119. _currAnim: -1,
  6120. // public methods
  6121. play: function(/*int?*/ delay, /*bool?*/ gotoStart){
  6122. // summary: Start the animation sequence.
  6123. // delay: How many milliseconds to delay before starting.
  6124. // gotoStart: If true, starts the sequence from the beginning; otherwise,
  6125. // starts it from its current position.
  6126. if( !this._anims.length ) { return this; /*dojo.lfx.Chain*/}
  6127. if( gotoStart || !this._anims[this._currAnim] ) {
  6128. this._currAnim = 0;
  6129. }
  6130. var currentAnimation = this._anims[this._currAnim];
  6131. this.fire("beforeBegin");
  6132. if(delay > 0){
  6133. setTimeout(dojo.lang.hitch(this, function(){ this.play(null, gotoStart); }), delay);
  6134. return this; // dojo.lfx.Chain
  6135. }
  6136. if(currentAnimation){
  6137. if(this._currAnim == 0){
  6138. this.fire("handler", ["begin", this._currAnim]);
  6139. this.fire("onBegin", [this._currAnim]);
  6140. }
  6141. this.fire("onPlay", [this._currAnim]);
  6142. currentAnimation.play(null, gotoStart);
  6143. }
  6144. return this; // dojo.lfx.Chain
  6145. },
  6146. pause: function(){
  6147. // summary: Pauses the running animation sequence.
  6148. if( this._anims[this._currAnim] ) {
  6149. this._anims[this._currAnim].pause();
  6150. this.fire("onPause", [this._currAnim]);
  6151. }
  6152. return this; // dojo.lfx.Chain
  6153. },
  6154. playPause: function(){
  6155. // summary: If the animation sequence is playing, pause it; otherwise,
  6156. // play it.
  6157. if(this._anims.length == 0){ return this; }
  6158. if(this._currAnim == -1){ this._currAnim = 0; }
  6159. var currAnim = this._anims[this._currAnim];
  6160. if( currAnim ) {
  6161. if( !currAnim._active || currAnim._paused ) {
  6162. this.play();
  6163. } else {
  6164. this.pause();
  6165. }
  6166. }
  6167. return this; // dojo.lfx.Chain
  6168. },
  6169. stop: function(){
  6170. // summary: Stops the running animations.
  6171. var currAnim = this._anims[this._currAnim];
  6172. if(currAnim){
  6173. currAnim.stop();
  6174. this.fire("onStop", [this._currAnim]);
  6175. }
  6176. return currAnim; // dojo.lfx.IAnimation
  6177. },
  6178. // private methods
  6179. _playNext: function(){
  6180. if( this._currAnim == -1 || this._anims.length == 0 ) { return this; }
  6181. this._currAnim++;
  6182. if( this._anims[this._currAnim] ){
  6183. this._anims[this._currAnim].play(null, true);
  6184. }
  6185. return this; // dojo.lfx.Chain
  6186. }
  6187. });
  6188. dojo.lfx.combine = function(/*dojo.lfx.IAnimation...*/ animations){
  6189. // summary: Convenience function. Returns a dojo.lfx.Combine created
  6190. // using the animations passed in.
  6191. var anims = arguments;
  6192. if(dojo.lang.isArray(arguments[0])){
  6193. /* animations: dojo.lfx.IAnimation[]
  6194. pId: a */
  6195. anims = arguments[0];
  6196. }
  6197. if(anims.length == 1){ return anims[0]; }
  6198. return new dojo.lfx.Combine(anims); // dojo.lfx.Combine
  6199. }
  6200. dojo.lfx.chain = function(/*dojo.lfx.IAnimation...*/ animations){
  6201. // summary: Convenience function. Returns a dojo.lfx.Chain created
  6202. // using the animations passed in.
  6203. var anims = arguments;
  6204. if(dojo.lang.isArray(arguments[0])){
  6205. /* animations: dojo.lfx.IAnimation[]
  6206. pId: a */
  6207. anims = arguments[0];
  6208. }
  6209. if(anims.length == 1){ return anims[0]; }
  6210. return new dojo.lfx.Chain(anims); // dojo.lfx.Combine
  6211. }
  6212. dojo.provide("dojo.uri.Uri");
  6213. dojo.uri = new function() {
  6214. this.dojoUri = function (/*dojo.uri.Uri||String*/uri) {
  6215. // summary: returns a Uri object resolved relative to the dojo root
  6216. return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri(), uri);
  6217. }
  6218. this.moduleUri = function(/*String*/module, /*dojo.uri.Uri||String*/uri){
  6219. // summary: returns a Uri object relative to a (top-level) module
  6220. // description: Examples: dojo.uri.moduleUri("dojo","Editor"), or dojo.uri.moduleUri("acme","someWidget")
  6221. var loc = dojo.hostenv.getModulePrefix(module);
  6222. if(!loc){return null;}
  6223. if(loc.lastIndexOf("/") != loc.length-1){loc += "/";}
  6224. return new dojo.uri.Uri(dojo.hostenv.getBaseScriptUri()+loc,uri);
  6225. }
  6226. this.Uri = function (/*dojo.uri.Uri||String...*/) {
  6227. // summary: Constructor to create an object representing a URI.
  6228. // description:
  6229. // Each argument is evaluated in order relative to the next until
  6230. // a canonical uri is produced. To get an absolute Uri relative
  6231. // to the current document use
  6232. // new dojo.uri.Uri(document.baseURI, uri)
  6233. // TODO: support for IPv6, see RFC 2732
  6234. // resolve uri components relative to each other
  6235. var uri = arguments[0];
  6236. for (var i = 1; i < arguments.length; i++) {
  6237. if(!arguments[i]) { continue; }
  6238. // Safari doesn't support this.constructor so we have to be explicit
  6239. var relobj = new dojo.uri.Uri(arguments[i].toString());
  6240. var uriobj = new dojo.uri.Uri(uri.toString());
  6241. if ((relobj.path=="")&&(relobj.scheme==null)&&(relobj.authority==null)&&(relobj.query==null)) {
  6242. if (relobj.fragment != null) { uriobj.fragment = relobj.fragment; }
  6243. relobj = uriobj;
  6244. } else if (relobj.scheme == null) {
  6245. relobj.scheme = uriobj.scheme;
  6246. if (relobj.authority == null) {
  6247. relobj.authority = uriobj.authority;
  6248. if (relobj.path.charAt(0) != "/") {
  6249. var path = uriobj.path.substring(0,
  6250. uriobj.path.lastIndexOf("/") + 1) + relobj.path;
  6251. var segs = path.split("/");
  6252. for (var j = 0; j < segs.length; j++) {
  6253. if (segs[j] == ".") {
  6254. if (j == segs.length - 1) { segs[j] = ""; }
  6255. else { segs.splice(j, 1); j--; }
  6256. } else if (j > 0 && !(j == 1 && segs[0] == "") &&
  6257. segs[j] == ".." && segs[j-1] != "..") {
  6258. if (j == segs.length - 1) { segs.splice(j, 1); segs[j - 1] = ""; }
  6259. else { segs.splice(j - 1, 2); j -= 2; }
  6260. }
  6261. }
  6262. relobj.path = segs.join("/");
  6263. }
  6264. }
  6265. }
  6266. uri = "";
  6267. if (relobj.scheme != null) { uri += relobj.scheme + ":"; }
  6268. if (relobj.authority != null) { uri += "//" + relobj.authority; }
  6269. uri += relobj.path;
  6270. if (relobj.query != null) { uri += "?" + relobj.query; }
  6271. if (relobj.fragment != null) { uri += "#" + relobj.fragment; }
  6272. }
  6273. this.uri = uri.toString();
  6274. // break the uri into its main components
  6275. var regexp = "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$";
  6276. var r = this.uri.match(new RegExp(regexp));
  6277. this.scheme = r[2] || (r[1] ? "" : null);
  6278. this.authority = r[4] || (r[3] ? "" : null);
  6279. this.path = r[5]; // can never be undefined
  6280. this.query = r[7] || (r[6] ? "" : null);
  6281. this.fragment = r[9] || (r[8] ? "" : null);
  6282. if (this.authority != null) {
  6283. // server based naming authority
  6284. regexp = "^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$";
  6285. r = this.authority.match(new RegExp(regexp));
  6286. this.user = r[3] || null;
  6287. this.password = r[4] || null;
  6288. this.host = r[5];
  6289. this.port = r[7] || null;
  6290. }
  6291. this.toString = function(){ return this.uri; }
  6292. }
  6293. };
  6294. dojo.provide("dojo.html.style");
  6295. dojo.html.getClass = function(/* HTMLElement */node){
  6296. // summary
  6297. // Returns the string value of the list of CSS classes currently assigned directly
  6298. // to the node in question. Returns an empty string if no class attribute is found;
  6299. node = dojo.byId(node);
  6300. if(!node){ return ""; }
  6301. var cs = "";
  6302. if(node.className){
  6303. cs = node.className;
  6304. }else if(dojo.html.hasAttribute(node, "class")){
  6305. cs = dojo.html.getAttribute(node, "class");
  6306. }
  6307. return cs.replace(/^\s+|\s+$/g, ""); // string
  6308. }
  6309. dojo.html.getClasses = function(/* HTMLElement */node) {
  6310. // summary
  6311. // Returns an array of CSS classes currently assigned directly to the node in question.
  6312. // Returns an empty array if no classes are found;
  6313. var c = dojo.html.getClass(node);
  6314. return (c == "") ? [] : c.split(/\s+/g); // array
  6315. }
  6316. dojo.html.hasClass = function(/* HTMLElement */node, /* string */classname){
  6317. // summary
  6318. // Returns whether or not the specified classname is a portion of the
  6319. // class list currently applied to the node. Does not cover cascaded
  6320. // styles, only classes directly applied to the node.
  6321. return (new RegExp('(^|\\s+)'+classname+'(\\s+|$)')).test(dojo.html.getClass(node)) // boolean
  6322. }
  6323. dojo.html.prependClass = function(/* HTMLElement */node, /* string */classStr){
  6324. // summary
  6325. // Adds the specified class to the beginning of the class list on the
  6326. // passed node. This gives the specified class the highest precidence
  6327. // when style cascading is calculated for the node. Returns true or
  6328. // false; indicating success or failure of the operation, respectively.
  6329. classStr += " " + dojo.html.getClass(node);
  6330. return dojo.html.setClass(node, classStr); // boolean
  6331. }
  6332. dojo.html.addClass = function(/* HTMLElement */node, /* string */classStr){
  6333. // summary
  6334. // Adds the specified class to the end of the class list on the
  6335. // passed &node;. Returns &true; or &false; indicating success or failure.
  6336. if (dojo.html.hasClass(node, classStr)) {
  6337. return false;
  6338. }
  6339. classStr = (dojo.html.getClass(node) + " " + classStr).replace(/^\s+|\s+$/g,"");
  6340. return dojo.html.setClass(node, classStr); // boolean
  6341. }
  6342. dojo.html.setClass = function(/* HTMLElement */node, /* string */classStr){
  6343. // summary
  6344. // Clobbers the existing list of classes for the node, replacing it with
  6345. // the list given in the 2nd argument. Returns true or false
  6346. // indicating success or failure.
  6347. node = dojo.byId(node);
  6348. var cs = new String(classStr);
  6349. try{
  6350. if(typeof node.className == "string"){
  6351. node.className = cs;
  6352. }else if(node.setAttribute){
  6353. node.setAttribute("class", classStr);
  6354. node.className = cs;
  6355. }else{
  6356. return false;
  6357. }
  6358. }catch(e){
  6359. dojo.debug("dojo.html.setClass() failed", e);
  6360. }
  6361. return true;
  6362. }
  6363. dojo.html.removeClass = function(/* HTMLElement */node, /* string */classStr, /* boolean? */allowPartialMatches){
  6364. // summary
  6365. // Removes the className from the node;. Returns true or false indicating success or failure.
  6366. try{
  6367. if (!allowPartialMatches) {
  6368. var newcs = dojo.html.getClass(node).replace(new RegExp('(^|\\s+)'+classStr+'(\\s+|$)'), "$1$2");
  6369. } else {
  6370. var newcs = dojo.html.getClass(node).replace(classStr,'');
  6371. }
  6372. dojo.html.setClass(node, newcs);
  6373. }catch(e){
  6374. dojo.debug("dojo.html.removeClass() failed", e);
  6375. }
  6376. return true; // boolean
  6377. }
  6378. dojo.html.replaceClass = function(/* HTMLElement */node, /* string */newClass, /* string */oldClass) {
  6379. // summary
  6380. // Replaces 'oldClass' and adds 'newClass' to node
  6381. dojo.html.removeClass(node, oldClass);
  6382. dojo.html.addClass(node, newClass);
  6383. }
  6384. // Enum type for getElementsByClass classMatchType arg:
  6385. dojo.html.classMatchType = {
  6386. ContainsAll : 0, // all of the classes are part of the node's class (default)
  6387. ContainsAny : 1, // any of the classes are part of the node's class
  6388. IsOnly : 2 // only all of the classes are part of the node's class
  6389. }
  6390. dojo.html.getElementsByClass = function(
  6391. /* string */classStr,
  6392. /* HTMLElement? */parent,
  6393. /* string? */nodeType,
  6394. /* integer? */classMatchType,
  6395. /* boolean? */useNonXpath
  6396. ){
  6397. // summary
  6398. // Returns an array of nodes for the given classStr, children of a
  6399. // parent, and optionally of a certain nodeType
  6400. // FIXME: temporarily set to false because of several dojo tickets related
  6401. // to the xpath version not working consistently in firefox.
  6402. useNonXpath = false;
  6403. var _document = dojo.doc();
  6404. parent = dojo.byId(parent) || _document;
  6405. var classes = classStr.split(/\s+/g);
  6406. var nodes = [];
  6407. if( classMatchType != 1 && classMatchType != 2 ) classMatchType = 0; // make it enum
  6408. var reClass = new RegExp("(\\s|^)((" + classes.join(")|(") + "))(\\s|$)");
  6409. var srtLength = classes.join(" ").length;
  6410. var candidateNodes = [];
  6411. if(!useNonXpath && _document.evaluate) { // supports dom 3 xpath
  6412. var xpath = ".//" + (nodeType || "*") + "[contains(";
  6413. if(classMatchType != dojo.html.classMatchType.ContainsAny){
  6414. xpath += "concat(' ',@class,' '), ' " +
  6415. classes.join(" ') and contains(concat(' ',@class,' '), ' ") +
  6416. " ')";
  6417. if (classMatchType == 2) {
  6418. xpath += " and string-length(@class)="+srtLength+"]";
  6419. }else{
  6420. xpath += "]";
  6421. }
  6422. }else{
  6423. xpath += "concat(' ',@class,' '), ' " +
  6424. classes.join(" ') or contains(concat(' ',@class,' '), ' ") +
  6425. " ')]";
  6426. }
  6427. var xpathResult = _document.evaluate(xpath, parent, null, XPathResult.ANY_TYPE, null);
  6428. var result = xpathResult.iterateNext();
  6429. while(result){
  6430. try{
  6431. candidateNodes.push(result);
  6432. result = xpathResult.iterateNext();
  6433. }catch(e){ break; }
  6434. }
  6435. return candidateNodes; // NodeList
  6436. }else{
  6437. if(!nodeType){
  6438. nodeType = "*";
  6439. }
  6440. candidateNodes = parent.getElementsByTagName(nodeType);
  6441. var node, i = 0;
  6442. outer:
  6443. while(node = candidateNodes[i++]){
  6444. var nodeClasses = dojo.html.getClasses(node);
  6445. if(nodeClasses.length == 0){ continue outer; }
  6446. var matches = 0;
  6447. for(var j = 0; j < nodeClasses.length; j++){
  6448. if(reClass.test(nodeClasses[j])){
  6449. if(classMatchType == dojo.html.classMatchType.ContainsAny){
  6450. nodes.push(node);
  6451. continue outer;
  6452. }else{
  6453. matches++;
  6454. }
  6455. }else{
  6456. if(classMatchType == dojo.html.classMatchType.IsOnly){
  6457. continue outer;
  6458. }
  6459. }
  6460. }
  6461. if(matches == classes.length){
  6462. if( (classMatchType == dojo.html.classMatchType.IsOnly)&&
  6463. (matches == nodeClasses.length)){
  6464. nodes.push(node);
  6465. }else if(classMatchType == dojo.html.classMatchType.ContainsAll){
  6466. nodes.push(node);
  6467. }
  6468. }
  6469. }
  6470. return nodes; // NodeList
  6471. }
  6472. }
  6473. dojo.html.getElementsByClassName = dojo.html.getElementsByClass;
  6474. dojo.html.toCamelCase = function(/* string */selector){
  6475. // summary
  6476. // Translates a CSS selector string to a camel-cased one.
  6477. var arr = selector.split('-'), cc = arr[0];
  6478. for(var i = 1; i < arr.length; i++) {
  6479. cc += arr[i].charAt(0).toUpperCase() + arr[i].substring(1);
  6480. }
  6481. return cc; // string
  6482. }
  6483. dojo.html.toSelectorCase = function(/* string */selector){
  6484. // summary
  6485. // Translates a camel cased string to a selector cased one.
  6486. return selector.replace(/([A-Z])/g, "-$1" ).toLowerCase(); // string
  6487. }
  6488. dojo.html.getComputedStyle = function(/* HTMLElement */node, /* string */cssSelector, /* integer? */inValue){
  6489. // summary
  6490. // Returns the computed style of cssSelector on node.
  6491. node = dojo.byId(node);
  6492. // cssSelector may actually be in camel case, so force selector version
  6493. var cssSelector = dojo.html.toSelectorCase(cssSelector);
  6494. var property = dojo.html.toCamelCase(cssSelector);
  6495. if(!node || !node.style){
  6496. return inValue;
  6497. } else if (document.defaultView && dojo.html.isDescendantOf(node, node.ownerDocument)){ // W3, gecko, KHTML
  6498. try{
  6499. // mozilla segfaults when margin-* and node is removed from doc
  6500. // FIXME: need to figure out a if there is quicker workaround
  6501. var cs = document.defaultView.getComputedStyle(node, "");
  6502. if(cs){
  6503. return cs.getPropertyValue(cssSelector); // integer
  6504. }
  6505. }catch(e){ // reports are that Safari can throw an exception above
  6506. if(node.style.getPropertyValue){ // W3
  6507. return node.style.getPropertyValue(cssSelector); // integer
  6508. } else {
  6509. return inValue; // integer
  6510. }
  6511. }
  6512. } else if(node.currentStyle){ // IE
  6513. return node.currentStyle[property]; // integer
  6514. }
  6515. if(node.style.getPropertyValue){ // W3
  6516. return node.style.getPropertyValue(cssSelector); // integer
  6517. }else{
  6518. return inValue; // integer
  6519. }
  6520. }
  6521. dojo.html.getStyleProperty = function(/* HTMLElement */node, /* string */cssSelector){
  6522. // summary
  6523. // Returns the value of the passed style
  6524. node = dojo.byId(node);
  6525. return (node && node.style ? node.style[dojo.html.toCamelCase(cssSelector)] : undefined); // string
  6526. }
  6527. dojo.html.getStyle = function(/* HTMLElement */node, /* string */cssSelector){
  6528. // summary
  6529. // Returns the computed value of the passed style
  6530. var value = dojo.html.getStyleProperty(node, cssSelector);
  6531. return (value ? value : dojo.html.getComputedStyle(node, cssSelector)); // string || integer
  6532. }
  6533. dojo.html.setStyle = function(/* HTMLElement */node, /* string */cssSelector, /* string */value){
  6534. // summary
  6535. // Set the value of passed style on node
  6536. node = dojo.byId(node);
  6537. if(node && node.style){
  6538. var camelCased = dojo.html.toCamelCase(cssSelector);
  6539. node.style[camelCased] = value;
  6540. }
  6541. }
  6542. dojo.html.setStyleText = function (/* HTMLElement */target, /* string */text) {
  6543. // summary
  6544. // Try to set the entire cssText property of the passed target; equiv of setting style attribute.
  6545. try {
  6546. target.style.cssText = text;
  6547. } catch (e) {
  6548. target.setAttribute("style", text);
  6549. }
  6550. }
  6551. dojo.html.copyStyle = function(/* HTMLElement */target, /* HTMLElement */source){
  6552. // summary
  6553. // work around for opera which doesn't have cssText, and for IE which fails on setAttribute
  6554. if(!source.style.cssText){
  6555. target.setAttribute("style", source.getAttribute("style"));
  6556. }else{
  6557. target.style.cssText = source.style.cssText;
  6558. }
  6559. dojo.html.addClass(target, dojo.html.getClass(source));
  6560. }
  6561. dojo.html.getUnitValue = function(/* HTMLElement */node, /* string */cssSelector, /* boolean? */autoIsZero){
  6562. // summary
  6563. // Get the value of passed selector, with the specific units used
  6564. var s = dojo.html.getComputedStyle(node, cssSelector);
  6565. if((!s)||((s == 'auto')&&(autoIsZero))){
  6566. return { value: 0, units: 'px' }; // object
  6567. }
  6568. // FIXME: is regex inefficient vs. parseInt or some manual test?
  6569. var match = s.match(/(\-?[\d.]+)([a-z%]*)/i);
  6570. if (!match){return dojo.html.getUnitValue.bad;}
  6571. return { value: Number(match[1]), units: match[2].toLowerCase() }; // object
  6572. }
  6573. dojo.html.getUnitValue.bad = { value: NaN, units: '' };
  6574. dojo.html.getPixelValue = function(/* HTMLElement */node, /* string */cssSelector, /* boolean? */autoIsZero){
  6575. // summary
  6576. // Get the value of passed selector in pixels.
  6577. var result = dojo.html.getUnitValue(node, cssSelector, autoIsZero);
  6578. // FIXME: there is serious debate as to whether or not this is the right solution
  6579. if(isNaN(result.value)){
  6580. return 0; // integer
  6581. }
  6582. // FIXME: code exists for converting other units to px (see Dean Edward's IE7)
  6583. // but there are cross-browser complexities
  6584. if((result.value)&&(result.units != 'px')){
  6585. return NaN; // integer
  6586. }
  6587. return result.value; // integer
  6588. }
  6589. dojo.html.setPositivePixelValue = function(/* HTMLElement */node, /* string */selector, /* integer */value){
  6590. // summary
  6591. // Attempt to set the value of selector on node as a positive pixel value.
  6592. if(isNaN(value)){return false;}
  6593. node.style[selector] = Math.max(0, value) + 'px';
  6594. return true; // boolean
  6595. }
  6596. dojo.html.styleSheet = null;
  6597. // FIXME: this is a really basic stub for adding and removing cssRules, but
  6598. // it assumes that you know the index of the cssRule that you want to add
  6599. // or remove, making it less than useful. So we need something that can
  6600. // search for the selector that you you want to remove.
  6601. dojo.html.insertCssRule = function(/* string */selector, /* string */declaration, /* integer? */index) {
  6602. // summary
  6603. // Attempt to insert declaration as selector on the internal stylesheet; if index try to set it there.
  6604. if (!dojo.html.styleSheet) {
  6605. if (document.createStyleSheet) { // IE
  6606. dojo.html.styleSheet = document.createStyleSheet();
  6607. } else if (document.styleSheets[0]) { // rest
  6608. // FIXME: should create a new style sheet here
  6609. // fall back on an exsiting style sheet
  6610. dojo.html.styleSheet = document.styleSheets[0];
  6611. } else {
  6612. return null; // integer
  6613. } // fail
  6614. }
  6615. if (arguments.length < 3) { // index may == 0
  6616. if (dojo.html.styleSheet.cssRules) { // W3
  6617. index = dojo.html.styleSheet.cssRules.length;
  6618. } else if (dojo.html.styleSheet.rules) { // IE
  6619. index = dojo.html.styleSheet.rules.length;
  6620. } else {
  6621. return null; // integer
  6622. } // fail
  6623. }
  6624. if (dojo.html.styleSheet.insertRule) { // W3
  6625. var rule = selector + " { " + declaration + " }";
  6626. return dojo.html.styleSheet.insertRule(rule, index); // integer
  6627. } else if (dojo.html.styleSheet.addRule) { // IE
  6628. return dojo.html.styleSheet.addRule(selector, declaration, index); // integer
  6629. } else {
  6630. return null; // integer
  6631. } // fail
  6632. }
  6633. dojo.html.removeCssRule = function(/* integer? */index){
  6634. // summary
  6635. // Attempt to remove the rule at index.
  6636. if(!dojo.html.styleSheet){
  6637. dojo.debug("no stylesheet defined for removing rules");
  6638. return false;
  6639. }
  6640. if(dojo.render.html.ie){
  6641. if(!index){
  6642. index = dojo.html.styleSheet.rules.length;
  6643. dojo.html.styleSheet.removeRule(index);
  6644. }
  6645. }else if(document.styleSheets[0]){
  6646. if(!index){
  6647. index = dojo.html.styleSheet.cssRules.length;
  6648. }
  6649. dojo.html.styleSheet.deleteRule(index);
  6650. }
  6651. return true; // boolean
  6652. }
  6653. dojo.html._insertedCssFiles = []; // cache container needed because IE reformats cssText when added to DOM
  6654. dojo.html.insertCssFile = function(/* string */URI, /* HTMLDocument? */doc, /* boolean? */checkDuplicates, /* boolean */fail_ok){
  6655. // summary
  6656. // calls css by XmlHTTP and inserts it into DOM as <style [widgetType="widgetType"]> *downloaded cssText*</style>
  6657. if(!URI){ return; }
  6658. if(!doc){ doc = document; }
  6659. var cssStr = dojo.hostenv.getText(URI, false, fail_ok);
  6660. if(cssStr===null){ return; }
  6661. cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
  6662. if(checkDuplicates){
  6663. var idx = -1, node, ent = dojo.html._insertedCssFiles;
  6664. for(var i = 0; i < ent.length; i++){
  6665. if((ent[i].doc == doc) && (ent[i].cssText == cssStr)){
  6666. idx = i; node = ent[i].nodeRef;
  6667. break;
  6668. }
  6669. }
  6670. // make sure we havent deleted our node
  6671. if(node){
  6672. var styles = doc.getElementsByTagName("style");
  6673. for(var i = 0; i < styles.length; i++){
  6674. if(styles[i] == node){
  6675. return;
  6676. }
  6677. }
  6678. // delete this entry
  6679. dojo.html._insertedCssFiles.shift(idx, 1);
  6680. }
  6681. }
  6682. var style = dojo.html.insertCssText(cssStr);
  6683. dojo.html._insertedCssFiles.push({'doc': doc, 'cssText': cssStr, 'nodeRef': style});
  6684. // insert custom attribute ex dbgHref="../foo.css" usefull when debugging in DOM inspectors, no?
  6685. if(style && djConfig.isDebug){
  6686. style.setAttribute("dbgHref", URI);
  6687. }
  6688. return style; // HTMLStyleElement
  6689. }
  6690. dojo.html.insertCssText = function(/* string */cssStr, /* HTMLDocument? */doc, /* string? */URI){
  6691. // summary
  6692. // Attempt to insert CSS rules into the document through inserting a style element
  6693. // DomNode Style = insertCssText(String ".dojoMenu {color: green;}"[, DomDoc document, dojo.uri.Uri Url ])
  6694. if(!cssStr){
  6695. return; // HTMLStyleElement
  6696. }
  6697. if(!doc){ doc = document; }
  6698. if(URI){// fix paths in cssStr
  6699. cssStr = dojo.html.fixPathsInCssText(cssStr, URI);
  6700. }
  6701. var style = doc.createElement("style");
  6702. style.setAttribute("type", "text/css");
  6703. // IE is b0rken enough to require that we add the element to the doc
  6704. // before changing it's properties
  6705. var head = doc.getElementsByTagName("head")[0];
  6706. if(!head){ // must have a head tag
  6707. dojo.debug("No head tag in document, aborting styles");
  6708. return; // HTMLStyleElement
  6709. }else{
  6710. head.appendChild(style);
  6711. }
  6712. if(style.styleSheet){// IE
  6713. style.styleSheet.cssText = cssStr;
  6714. }else{ // w3c
  6715. var cssText = doc.createTextNode(cssStr);
  6716. style.appendChild(cssText);
  6717. }
  6718. return style; // HTMLStyleElement
  6719. }
  6720. dojo.html.fixPathsInCssText = function(/* string */cssStr, /* string */URI){
  6721. // summary
  6722. // usage: cssText comes from dojoroot/src/widget/templates/Foobar.css
  6723. // it has .dojoFoo { background-image: url(images/bar.png);} then uri should point to dojoroot/src/widget/templates/
  6724. function iefixPathsInCssText() {
  6725. var regexIe = /AlphaImageLoader\(src\=['"]([\t\s\w()\/.\\'"-:#=&?~]*)['"]/;
  6726. while(match = regexIe.exec(cssStr)){
  6727. url = match[1].replace(regexTrim, "$2");
  6728. if(!regexProtocol.exec(url)){
  6729. url = (new dojo.uri.Uri(URI, url).toString());
  6730. }
  6731. str += cssStr.substring(0, match.index) + "AlphaImageLoader(src='" + url + "'";
  6732. cssStr = cssStr.substr(match.index + match[0].length);
  6733. }
  6734. return str + cssStr;
  6735. }
  6736. if(!cssStr || !URI){ return; }
  6737. var match, str = "", url = "";
  6738. var regex = /url\(\s*([\t\s\w()\/.\\'"-:#=&?]+)\s*\)/;
  6739. var regexProtocol = /(file|https?|ftps?):\/\//;
  6740. var regexTrim = /^[\s]*(['"]?)([\w()\/.\\'"-:#=&?]*)\1[\s]*?$/;
  6741. if (dojo.render.html.ie55 || dojo.render.html.ie60) {
  6742. cssStr = iefixPathsInCssText();
  6743. }
  6744. while(match = regex.exec(cssStr)){
  6745. url = match[1].replace(regexTrim, "$2");
  6746. if(!regexProtocol.exec(url)){
  6747. url = (new dojo.uri.Uri(URI, url).toString());
  6748. }
  6749. str += cssStr.substring(0, match.index) + "url(" + url + ")";
  6750. cssStr = cssStr.substr(match.index + match[0].length);
  6751. }
  6752. return str + cssStr; // string
  6753. }
  6754. dojo.html.setActiveStyleSheet = function(/* string */title){
  6755. // summary
  6756. // Activate style sheet with specified title.
  6757. var i = 0, a, els = dojo.doc().getElementsByTagName("link");
  6758. while (a = els[i++]) {
  6759. if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")){
  6760. a.disabled = true;
  6761. if (a.getAttribute("title") == title) { a.disabled = false; }
  6762. }
  6763. }
  6764. }
  6765. dojo.html.getActiveStyleSheet = function(){
  6766. // summary
  6767. // return the title of the currently active stylesheet
  6768. var i = 0, a, els = dojo.doc().getElementsByTagName("link");
  6769. while (a = els[i++]) {
  6770. if (a.getAttribute("rel").indexOf("style") != -1
  6771. && a.getAttribute("title")
  6772. && !a.disabled
  6773. ){
  6774. return a.getAttribute("title"); // string
  6775. }
  6776. }
  6777. return null; // string
  6778. }
  6779. dojo.html.getPreferredStyleSheet = function(){
  6780. // summary
  6781. // Return the preferred stylesheet title (i.e. link without alt attribute)
  6782. var i = 0, a, els = dojo.doc().getElementsByTagName("link");
  6783. while (a = els[i++]) {
  6784. if(a.getAttribute("rel").indexOf("style") != -1
  6785. && a.getAttribute("rel").indexOf("alt") == -1
  6786. && a.getAttribute("title")
  6787. ){
  6788. return a.getAttribute("title"); // string
  6789. }
  6790. }
  6791. return null; // string
  6792. }
  6793. dojo.html.applyBrowserClass = function(/* HTMLElement */node){
  6794. // summary
  6795. // Applies pre-set class names based on browser & version to the passed node.
  6796. // Modified version of Morris' CSS hack.
  6797. var drh=dojo.render.html;
  6798. var classes = {
  6799. dj_ie: drh.ie,
  6800. dj_ie55: drh.ie55,
  6801. dj_ie6: drh.ie60,
  6802. dj_ie7: drh.ie70,
  6803. dj_iequirks: drh.ie && drh.quirks,
  6804. dj_opera: drh.opera,
  6805. dj_opera8: drh.opera && (Math.floor(dojo.render.version)==8),
  6806. dj_opera9: drh.opera && (Math.floor(dojo.render.version)==9),
  6807. dj_khtml: drh.khtml,
  6808. dj_safari: drh.safari,
  6809. dj_gecko: drh.mozilla
  6810. }; // no dojo unsupported browsers
  6811. for(var p in classes){
  6812. if(classes[p]){
  6813. dojo.html.addClass(node, p);
  6814. }
  6815. }
  6816. };
  6817. dojo.provide("dojo.html.display");
  6818. dojo.html._toggle = function(node, tester, setter){
  6819. node = dojo.byId(node);
  6820. setter(node, !tester(node));
  6821. return tester(node);
  6822. }
  6823. dojo.html.show = function(/* HTMLElement */node){
  6824. // summary
  6825. // Show the passed element by reverting display property set by dojo.html.hide
  6826. node = dojo.byId(node);
  6827. if(dojo.html.getStyleProperty(node, 'display')=='none'){
  6828. dojo.html.setStyle(node, 'display', (node.dojoDisplayCache||''));
  6829. node.dojoDisplayCache = undefined; // cannot use delete on a node in IE6
  6830. }
  6831. }
  6832. dojo.html.hide = function(/* HTMLElement */node){
  6833. // summary
  6834. // Hide the passed element by setting display:none
  6835. node = dojo.byId(node);
  6836. if(typeof node["dojoDisplayCache"] == "undefined"){ // it could == '', so we cannot say !node.dojoDisplayCount
  6837. var d = dojo.html.getStyleProperty(node, 'display')
  6838. if(d!='none'){
  6839. node.dojoDisplayCache = d;
  6840. }
  6841. }
  6842. dojo.html.setStyle(node, 'display', 'none');
  6843. }
  6844. dojo.html.setShowing = function(/* HTMLElement */node, /* boolean? */showing){
  6845. // summary
  6846. // Calls show() if showing is true, hide() otherwise
  6847. dojo.html[(showing ? 'show' : 'hide')](node);
  6848. }
  6849. dojo.html.isShowing = function(/* HTMLElement */node){
  6850. // summary
  6851. // Returns whether the element is displayed or not.
  6852. // FIXME: returns true if node is bad, isHidden would be easier to make correct
  6853. return (dojo.html.getStyleProperty(node, 'display') != 'none'); // boolean
  6854. }
  6855. dojo.html.toggleShowing = function(/* HTMLElement */node){
  6856. // summary
  6857. // Call setShowing() on node with the complement of isShowing(), then return the new value of isShowing()
  6858. return dojo.html._toggle(node, dojo.html.isShowing, dojo.html.setShowing); // boolean
  6859. }
  6860. // Simple mapping of tag names to display values
  6861. // FIXME: simplistic
  6862. dojo.html.displayMap = { tr: '', td: '', th: '', img: 'inline', span: 'inline', input: 'inline', button: 'inline' };
  6863. dojo.html.suggestDisplayByTagName = function(/* HTMLElement */node){
  6864. // summary
  6865. // Suggest a value for the display property that will show 'node' based on it's tag
  6866. node = dojo.byId(node);
  6867. if(node && node.tagName){
  6868. var tag = node.tagName.toLowerCase();
  6869. return (tag in dojo.html.displayMap ? dojo.html.displayMap[tag] : 'block'); // string
  6870. }
  6871. }
  6872. dojo.html.setDisplay = function(/* HTMLElement */node, /* string */display){
  6873. // summary
  6874. // Sets the value of style.display to value of 'display' parameter if it is a string.
  6875. // Otherwise, if 'display' is false, set style.display to 'none'.
  6876. // Finally, set 'display' to a suggested display value based on the node's tag
  6877. dojo.html.setStyle(node, 'display', ((display instanceof String || typeof display == "string") ? display : (display ? dojo.html.suggestDisplayByTagName(node) : 'none')));
  6878. }
  6879. dojo.html.isDisplayed = function(/* HTMLElement */node){
  6880. // summary
  6881. // Is true if the the computed display style for node is not 'none'
  6882. // FIXME: returns true if node is bad, isNotDisplayed would be easier to make correct
  6883. return (dojo.html.getComputedStyle(node, 'display') != 'none'); // boolean
  6884. }
  6885. dojo.html.toggleDisplay = function(/* HTMLElement */node){
  6886. // summary
  6887. // Call setDisplay() on node with the complement of isDisplayed(), then
  6888. // return the new value of isDisplayed()
  6889. return dojo.html._toggle(node, dojo.html.isDisplayed, dojo.html.setDisplay); // boolean
  6890. }
  6891. dojo.html.setVisibility = function(/* HTMLElement */node, /* string */visibility){
  6892. // summary
  6893. // Sets the value of style.visibility to value of 'visibility' parameter if it is a string.
  6894. // Otherwise, if 'visibility' is false, set style.visibility to 'hidden'. Finally, set style.visibility to 'visible'.
  6895. dojo.html.setStyle(node, 'visibility', ((visibility instanceof String || typeof visibility == "string") ? visibility : (visibility ? 'visible' : 'hidden')));
  6896. }
  6897. dojo.html.isVisible = function(/* HTMLElement */node){
  6898. // summary
  6899. // Returns true if the the computed visibility style for node is not 'hidden'
  6900. // FIXME: returns true if node is bad, isInvisible would be easier to make correct
  6901. return (dojo.html.getComputedStyle(node, 'visibility') != 'hidden'); // boolean
  6902. }
  6903. dojo.html.toggleVisibility = function(node){
  6904. // summary
  6905. // Call setVisibility() on node with the complement of isVisible(), then return the new value of isVisible()
  6906. return dojo.html._toggle(node, dojo.html.isVisible, dojo.html.setVisibility); // boolean
  6907. }
  6908. dojo.html.setOpacity = function(/* HTMLElement */node, /* float */opacity, /* boolean? */dontFixOpacity){
  6909. // summary
  6910. // Sets the opacity of node in a cross-browser way.
  6911. // float between 0.0 (transparent) and 1.0 (opaque)
  6912. node = dojo.byId(node);
  6913. var h = dojo.render.html;
  6914. if(!dontFixOpacity){
  6915. if( opacity >= 1.0){
  6916. if(h.ie){
  6917. dojo.html.clearOpacity(node);
  6918. return;
  6919. }else{
  6920. opacity = 0.999999;
  6921. }
  6922. }else if( opacity < 0.0){ opacity = 0; }
  6923. }
  6924. if(h.ie){
  6925. if(node.nodeName.toLowerCase() == "tr"){
  6926. // FIXME: is this too naive? will we get more than we want?
  6927. var tds = node.getElementsByTagName("td");
  6928. for(var x=0; x<tds.length; x++){
  6929. tds[x].style.filter = "Alpha(Opacity="+opacity*100+")";
  6930. }
  6931. }
  6932. node.style.filter = "Alpha(Opacity="+opacity*100+")";
  6933. }else if(h.moz){
  6934. node.style.opacity = opacity; // ffox 1.0 directly supports "opacity"
  6935. node.style.MozOpacity = opacity;
  6936. }else if(h.safari){
  6937. node.style.opacity = opacity; // 1.3 directly supports "opacity"
  6938. node.style.KhtmlOpacity = opacity;
  6939. }else{
  6940. node.style.opacity = opacity;
  6941. }
  6942. }
  6943. dojo.html.clearOpacity = function(/* HTMLElement */node){
  6944. // summary
  6945. // Clears any opacity setting on the passed element.
  6946. node = dojo.byId(node);
  6947. var ns = node.style;
  6948. var h = dojo.render.html;
  6949. if(h.ie){
  6950. try {
  6951. if( node.filters && node.filters.alpha ){
  6952. ns.filter = ""; // FIXME: may get rid of other filter effects
  6953. }
  6954. } catch(e) {
  6955. /*
  6956. * IE7 gives error if node.filters not set;
  6957. * don't know why or how to workaround (other than this)
  6958. */
  6959. }
  6960. }else if(h.moz){
  6961. ns.opacity = 1;
  6962. ns.MozOpacity = 1;
  6963. }else if(h.safari){
  6964. ns.opacity = 1;
  6965. ns.KhtmlOpacity = 1;
  6966. }else{
  6967. ns.opacity = 1;
  6968. }
  6969. }
  6970. dojo.html.getOpacity = function(/* HTMLElement */node){
  6971. // summary
  6972. // Returns the opacity of the passed element
  6973. node = dojo.byId(node);
  6974. var h = dojo.render.html;
  6975. if(h.ie){
  6976. var opac = (node.filters && node.filters.alpha &&
  6977. typeof node.filters.alpha.opacity == "number"
  6978. ? node.filters.alpha.opacity : 100) / 100;
  6979. }else{
  6980. var opac = node.style.opacity || node.style.MozOpacity ||
  6981. node.style.KhtmlOpacity || 1;
  6982. }
  6983. return opac >= 0.999999 ? 1.0 : Number(opac); // float
  6984. }
  6985. dojo.provide("dojo.html.color");
  6986. dojo.html.getBackgroundColor = function(/* HTMLElement */node){
  6987. // summary
  6988. // returns the background color of the passed node as a 32-bit color (RGBA)
  6989. node = dojo.byId(node);
  6990. var color;
  6991. do{
  6992. color = dojo.html.getStyle(node, "background-color");
  6993. // Safari doesn't say "transparent"
  6994. if(color.toLowerCase() == "rgba(0, 0, 0, 0)") { color = "transparent"; }
  6995. if(node == document.getElementsByTagName("body")[0]) { node = null; break; }
  6996. node = node.parentNode;
  6997. }while(node && dojo.lang.inArray(["transparent", ""], color));
  6998. if(color == "transparent"){
  6999. color = [255, 255, 255, 0];
  7000. }else{
  7001. color = dojo.gfx.color.extractRGB(color);
  7002. }
  7003. return color; // array
  7004. }
  7005. dojo.provide("dojo.html.common");
  7006. dojo.lang.mixin(dojo.html, dojo.dom);
  7007. dojo.html.body = function(){
  7008. dojo.deprecated("dojo.html.body() moved to dojo.body()", "0.5");
  7009. return dojo.body();
  7010. }
  7011. // FIXME: we are going to assume that we can throw any and every rendering
  7012. // engine into the IE 5.x box model. In Mozilla, we do this w/ CSS.
  7013. // Need to investigate for KHTML and Opera
  7014. dojo.html.getEventTarget = function(/* DOMEvent */evt){
  7015. // summary
  7016. // Returns the target of an event
  7017. if(!evt) { evt = dojo.global().event || {} };
  7018. var t = (evt.srcElement ? evt.srcElement : (evt.target ? evt.target : null));
  7019. while((t)&&(t.nodeType!=1)){ t = t.parentNode; }
  7020. return t; // HTMLElement
  7021. }
  7022. dojo.html.getViewport = function(){
  7023. // summary
  7024. // Returns the dimensions of the viewable area of a browser window
  7025. var _window = dojo.global();
  7026. var _document = dojo.doc();
  7027. var w = 0;
  7028. var h = 0;
  7029. if(dojo.render.html.mozilla){
  7030. // mozilla
  7031. w = _document.documentElement.clientWidth;
  7032. h = _window.innerHeight;
  7033. }else if(!dojo.render.html.opera && _window.innerWidth){
  7034. //in opera9, dojo.body().clientWidth should be used, instead
  7035. //of window.innerWidth/document.documentElement.clientWidth
  7036. //so we have to check whether it is opera
  7037. w = _window.innerWidth;
  7038. h = _window.innerHeight;
  7039. } else if (!dojo.render.html.opera && dojo.exists(_document, "documentElement.clientWidth")){
  7040. // IE6 Strict
  7041. var w2 = _document.documentElement.clientWidth;
  7042. // this lets us account for scrollbars
  7043. if(!w || w2 && w2 < w) {
  7044. w = w2;
  7045. }
  7046. h = _document.documentElement.clientHeight;
  7047. } else if (dojo.body().clientWidth){
  7048. // IE, Opera
  7049. w = dojo.body().clientWidth;
  7050. h = dojo.body().clientHeight;
  7051. }
  7052. return { width: w, height: h }; // object
  7053. }
  7054. dojo.html.getScroll = function(){
  7055. // summary
  7056. // Returns the scroll position of the document
  7057. var _window = dojo.global();
  7058. var _document = dojo.doc();
  7059. var top = _window.pageYOffset || _document.documentElement.scrollTop || dojo.body().scrollTop || 0;
  7060. var left = _window.pageXOffset || _document.documentElement.scrollLeft || dojo.body().scrollLeft || 0;
  7061. return {
  7062. top: top,
  7063. left: left,
  7064. offset:{ x: left, y: top } // note the change, NOT an Array with added properties.
  7065. }; // object
  7066. }
  7067. dojo.html.getParentByType = function(/* HTMLElement */node, /* string */type) {
  7068. // summary
  7069. // Returns the first ancestor of node with tagName type.
  7070. var _document = dojo.doc();
  7071. var parent = dojo.byId(node);
  7072. type = type.toLowerCase();
  7073. while((parent)&&(parent.nodeName.toLowerCase()!=type)){
  7074. if(parent==(_document["body"]||_document["documentElement"])){
  7075. return null;
  7076. }
  7077. parent = parent.parentNode;
  7078. }
  7079. return parent; // HTMLElement
  7080. }
  7081. dojo.html.getAttribute = function(/* HTMLElement */node, /* string */attr){
  7082. // summary
  7083. // Returns the value of attribute attr from node.
  7084. node = dojo.byId(node);
  7085. // FIXME: need to add support for attr-specific accessors
  7086. if((!node)||(!node.getAttribute)){
  7087. // if(attr !== 'nwType'){
  7088. // alert("getAttr of '" + attr + "' with bad node");
  7089. // }
  7090. return null;
  7091. }
  7092. var ta = typeof attr == 'string' ? attr : new String(attr);
  7093. // first try the approach most likely to succeed
  7094. var v = node.getAttribute(ta.toUpperCase());
  7095. if((v)&&(typeof v == 'string')&&(v!="")){
  7096. return v; // string
  7097. }
  7098. // try returning the attributes value, if we couldn't get it as a string
  7099. if(v && v.value){
  7100. return v.value; // string
  7101. }
  7102. // this should work on Opera 7, but it's a little on the crashy side
  7103. if((node.getAttributeNode)&&(node.getAttributeNode(ta))){
  7104. return (node.getAttributeNode(ta)).value; // string
  7105. }else if(node.getAttribute(ta)){
  7106. return node.getAttribute(ta); // string
  7107. }else if(node.getAttribute(ta.toLowerCase())){
  7108. return node.getAttribute(ta.toLowerCase()); // string
  7109. }
  7110. return null; // string
  7111. }
  7112. dojo.html.hasAttribute = function(/* HTMLElement */node, /* string */attr){
  7113. // summary
  7114. // Determines whether or not the specified node carries a value for the attribute in question.
  7115. return dojo.html.getAttribute(dojo.byId(node), attr) ? true : false; // boolean
  7116. }
  7117. dojo.html.getCursorPosition = function(/* DOMEvent */e){
  7118. // summary
  7119. // Returns the mouse position relative to the document (not the viewport).
  7120. // For example, if you have a document that is 10000px tall,
  7121. // but your browser window is only 100px tall,
  7122. // if you scroll to the bottom of the document and call this function it
  7123. // will return {x: 0, y: 10000}
  7124. // NOTE: for events delivered via dojo.event.connect() and/or dojoAttachEvent (for widgets),
  7125. // you can just access evt.pageX and evt.pageY, rather than calling this function.
  7126. e = e || dojo.global().event;
  7127. var cursor = {x:0, y:0};
  7128. if(e.pageX || e.pageY){
  7129. cursor.x = e.pageX;
  7130. cursor.y = e.pageY;
  7131. }else{
  7132. var de = dojo.doc().documentElement;
  7133. var db = dojo.body();
  7134. cursor.x = e.clientX + ((de||db)["scrollLeft"]) - ((de||db)["clientLeft"]);
  7135. cursor.y = e.clientY + ((de||db)["scrollTop"]) - ((de||db)["clientTop"]);
  7136. }
  7137. return cursor; // object
  7138. }
  7139. dojo.html.isTag = function(/* HTMLElement */node) {
  7140. // summary
  7141. // Like dojo.dom.isTag, except case-insensitive
  7142. node = dojo.byId(node);
  7143. if(node && node.tagName) {
  7144. for (var i=1; i<arguments.length; i++){
  7145. if (node.tagName.toLowerCase()==String(arguments[i]).toLowerCase()){
  7146. return String(arguments[i]).toLowerCase(); // string
  7147. }
  7148. }
  7149. }
  7150. return ""; // string
  7151. }
  7152. //define dojo.html.createExternalElement for IE to workaround the annoying activation "feature" in new IE
  7153. //details: http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/overview/activating_activex.asp
  7154. if(dojo.render.html.ie && !dojo.render.html.ie70){
  7155. //only define createExternalElement for IE in none https to avoid "mixed content" warning dialog
  7156. if(window.location.href.substr(0,6).toLowerCase() != "https:"){
  7157. (function(){
  7158. // FIXME: this seems not to work correctly on IE 7!!
  7159. //The trick is to define a function in a script.src property:
  7160. // <script src="javascript:'function createExternalElement(){...}'"></script>,
  7161. //which will be treated as an external javascript file in IE
  7162. var xscript = dojo.doc().createElement('script');
  7163. xscript.src = "javascript:'dojo.html.createExternalElement=function(doc, tag){ return doc.createElement(tag); }'";
  7164. dojo.doc().getElementsByTagName("head")[0].appendChild(xscript);
  7165. })();
  7166. }
  7167. }else{
  7168. //for other browsers, simply use document.createElement
  7169. //is enough
  7170. dojo.html.createExternalElement = function(/* HTMLDocument */doc, /* string */tag){
  7171. // summary
  7172. // Creates an element in the HTML document, here for ActiveX activation workaround.
  7173. return doc.createElement(tag); // HTMLElement
  7174. }
  7175. }
  7176. dojo.html._callDeprecated = function(inFunc, replFunc, args, argName, retValue){
  7177. dojo.deprecated("dojo.html." + inFunc,
  7178. "replaced by dojo.html." + replFunc + "(" + (argName ? "node, {"+ argName + ": " + argName + "}" : "" ) + ")" + (retValue ? "." + retValue : ""), "0.5");
  7179. var newArgs = [];
  7180. if(argName){ var argsIn = {}; argsIn[argName] = args[1]; newArgs.push(args[0]); newArgs.push(argsIn); }
  7181. else { newArgs = args }
  7182. var ret = dojo.html[replFunc].apply(dojo.html, args);
  7183. if(retValue){ return ret[retValue]; }
  7184. else { return ret; }
  7185. }
  7186. dojo.html.getViewportWidth = function(){
  7187. return dojo.html._callDeprecated("getViewportWidth", "getViewport", arguments, null, "width");
  7188. }
  7189. dojo.html.getViewportHeight = function(){
  7190. return dojo.html._callDeprecated("getViewportHeight", "getViewport", arguments, null, "height");
  7191. }
  7192. dojo.html.getViewportSize = function(){
  7193. return dojo.html._callDeprecated("getViewportSize", "getViewport", arguments);
  7194. }
  7195. dojo.html.getScrollTop = function(){
  7196. return dojo.html._callDeprecated("getScrollTop", "getScroll", arguments, null, "top");
  7197. }
  7198. dojo.html.getScrollLeft = function(){
  7199. return dojo.html._callDeprecated("getScrollLeft", "getScroll", arguments, null, "left");
  7200. }
  7201. dojo.html.getScrollOffset = function(){
  7202. return dojo.html._callDeprecated("getScrollOffset", "getScroll", arguments, null, "offset");
  7203. }
  7204. dojo.provide("dojo.html.layout");
  7205. dojo.html.sumAncestorProperties = function(/* HTMLElement */node, /* string */prop){
  7206. // summary
  7207. // Returns the sum of the passed property on all ancestors of node.
  7208. node = dojo.byId(node);
  7209. if(!node){ return 0; } // FIXME: throw an error?
  7210. var retVal = 0;
  7211. while(node){
  7212. if(dojo.html.getComputedStyle(node, 'position') == 'fixed'){
  7213. return 0;
  7214. }
  7215. var val = node[prop];
  7216. if(val){
  7217. retVal += val - 0;
  7218. if(node==dojo.body()){ break; }// opera and khtml #body & #html has the same values, we only need one value
  7219. }
  7220. node = node.parentNode;
  7221. }
  7222. return retVal; // integer
  7223. }
  7224. dojo.html.setStyleAttributes = function(/* HTMLElement */node, /* string */attributes) {
  7225. // summary
  7226. // allows a dev to pass a string similar to what you'd pass in style="", and apply it to a node.
  7227. node = dojo.byId(node);
  7228. var splittedAttribs=attributes.replace(/(;)?\s*$/, "").split(";");
  7229. for(var i=0; i<splittedAttribs.length; i++){
  7230. var nameValue=splittedAttribs[i].split(":");
  7231. var name=nameValue[0].replace(/\s*$/, "").replace(/^\s*/, "").toLowerCase();
  7232. var value=nameValue[1].replace(/\s*$/, "").replace(/^\s*/, "");
  7233. switch(name){
  7234. case "opacity":
  7235. dojo.html.setOpacity(node, value);
  7236. break;
  7237. case "content-height":
  7238. dojo.html.setContentBox(node, {height: value});
  7239. break;
  7240. case "content-width":
  7241. dojo.html.setContentBox(node, {width: value});
  7242. break;
  7243. case "outer-height":
  7244. dojo.html.setMarginBox(node, {height: value});
  7245. break;
  7246. case "outer-width":
  7247. dojo.html.setMarginBox(node, {width: value});
  7248. break;
  7249. default:
  7250. node.style[dojo.html.toCamelCase(name)]=value;
  7251. }
  7252. }
  7253. }
  7254. dojo.html.boxSizing = {
  7255. MARGIN_BOX: "margin-box",
  7256. BORDER_BOX: "border-box",
  7257. PADDING_BOX: "padding-box",
  7258. CONTENT_BOX: "content-box"
  7259. };
  7260. dojo.html.getAbsolutePosition = dojo.html.abs = function(/* HTMLElement */node, /* boolean? */includeScroll, /* string? */boxType){
  7261. // summary
  7262. // Gets the absolute position of the passed element based on the document itself.
  7263. node = dojo.byId(node, node.ownerDocument);
  7264. var ret = {
  7265. x: 0,
  7266. y: 0
  7267. };
  7268. var bs = dojo.html.boxSizing;
  7269. if(!boxType) { boxType = bs.CONTENT_BOX; }
  7270. var nativeBoxType = 2; //BORDER box
  7271. var targetBoxType;
  7272. switch(boxType){
  7273. case bs.MARGIN_BOX:
  7274. targetBoxType = 3;
  7275. break;
  7276. case bs.BORDER_BOX:
  7277. targetBoxType = 2;
  7278. break;
  7279. case bs.PADDING_BOX:
  7280. default:
  7281. targetBoxType = 1;
  7282. break;
  7283. case bs.CONTENT_BOX:
  7284. targetBoxType = 0;
  7285. break;
  7286. }
  7287. var h = dojo.render.html;
  7288. var db = document["body"]||document["documentElement"];
  7289. if(h.ie){
  7290. with(node.getBoundingClientRect()){
  7291. ret.x = left-2;
  7292. ret.y = top-2;
  7293. }
  7294. }else if(document.getBoxObjectFor){
  7295. // mozilla
  7296. nativeBoxType = 1; //getBoxObjectFor return padding box coordinate
  7297. try{
  7298. var bo = document.getBoxObjectFor(node);
  7299. ret.x = bo.x - dojo.html.sumAncestorProperties(node, "scrollLeft");
  7300. ret.y = bo.y - dojo.html.sumAncestorProperties(node, "scrollTop");
  7301. }catch(e){
  7302. // squelch
  7303. }
  7304. }else{
  7305. if(node["offsetParent"]){
  7306. var endNode;
  7307. // in Safari, if the node is an absolutely positioned child of
  7308. // the body and the body has a margin the offset of the child
  7309. // and the body contain the body's margins, so we need to end
  7310. // at the body
  7311. if( (h.safari)&&
  7312. (node.style.getPropertyValue("position") == "absolute")&&
  7313. (node.parentNode == db)){
  7314. endNode = db;
  7315. }else{
  7316. endNode = db.parentNode;
  7317. }
  7318. //TODO: set correct nativeBoxType for safari/konqueror
  7319. if(node.parentNode != db){
  7320. var nd = node;
  7321. if(dojo.render.html.opera){ nd = db; }
  7322. ret.x -= dojo.html.sumAncestorProperties(nd, "scrollLeft");
  7323. ret.y -= dojo.html.sumAncestorProperties(nd, "scrollTop");
  7324. }
  7325. var curnode = node;
  7326. do{
  7327. var n = curnode["offsetLeft"];
  7328. //FIXME: ugly hack to workaround the submenu in
  7329. //popupmenu2 does not shown up correctly in opera.
  7330. //Someone have a better workaround?
  7331. if(!h.opera || n>0){
  7332. ret.x += isNaN(n) ? 0 : n;
  7333. }
  7334. var m = curnode["offsetTop"];
  7335. ret.y += isNaN(m) ? 0 : m;
  7336. curnode = curnode.offsetParent;
  7337. }while((curnode != endNode)&&(curnode != null));
  7338. }else if(node["x"]&&node["y"]){
  7339. ret.x += isNaN(node.x) ? 0 : node.x;
  7340. ret.y += isNaN(node.y) ? 0 : node.y;
  7341. }
  7342. }
  7343. // account for document scrolling!
  7344. if(includeScroll){
  7345. var scroll = dojo.html.getScroll();
  7346. ret.y += scroll.top;
  7347. ret.x += scroll.left;
  7348. }
  7349. var extentFuncArray=[dojo.html.getPaddingExtent, dojo.html.getBorderExtent, dojo.html.getMarginExtent];
  7350. if(nativeBoxType > targetBoxType){
  7351. for(var i=targetBoxType;i<nativeBoxType;++i){
  7352. ret.y += extentFuncArray[i](node, 'top');
  7353. ret.x += extentFuncArray[i](node, 'left');
  7354. }
  7355. }else if(nativeBoxType < targetBoxType){
  7356. for(var i=targetBoxType;i>nativeBoxType;--i){
  7357. ret.y -= extentFuncArray[i-1](node, 'top');
  7358. ret.x -= extentFuncArray[i-1](node, 'left');
  7359. }
  7360. }
  7361. ret.top = ret.y;
  7362. ret.left = ret.x;
  7363. return ret; // object
  7364. }
  7365. dojo.html.isPositionAbsolute = function(/* HTMLElement */node){
  7366. // summary
  7367. // Returns true if the element is absolutely positioned.
  7368. return (dojo.html.getComputedStyle(node, 'position') == 'absolute'); // boolean
  7369. }
  7370. dojo.html._sumPixelValues = function(/* HTMLElement */node, selectors, autoIsZero){
  7371. var total = 0;
  7372. for(var x=0; x<selectors.length; x++){
  7373. total += dojo.html.getPixelValue(node, selectors[x], autoIsZero);
  7374. }
  7375. return total;
  7376. }
  7377. dojo.html.getMargin = function(/* HTMLElement */node){
  7378. // summary
  7379. // Returns the width and height of the passed node's margin
  7380. return {
  7381. width: dojo.html._sumPixelValues(node, ["margin-left", "margin-right"], (dojo.html.getComputedStyle(node, 'position') == 'absolute')),
  7382. height: dojo.html._sumPixelValues(node, ["margin-top", "margin-bottom"], (dojo.html.getComputedStyle(node, 'position') == 'absolute'))
  7383. }; // object
  7384. }
  7385. dojo.html.getBorder = function(/* HTMLElement */node){
  7386. // summary
  7387. // Returns the width and height of the passed node's border
  7388. return {
  7389. width: dojo.html.getBorderExtent(node, 'left') + dojo.html.getBorderExtent(node, 'right'),
  7390. height: dojo.html.getBorderExtent(node, 'top') + dojo.html.getBorderExtent(node, 'bottom')
  7391. }; // object
  7392. }
  7393. dojo.html.getBorderExtent = function(/* HTMLElement */node, /* string */side){
  7394. // summary
  7395. // returns the width of the requested border
  7396. return (dojo.html.getStyle(node, 'border-' + side + '-style') == 'none' ? 0 : dojo.html.getPixelValue(node, 'border-' + side + '-width')); // integer
  7397. }
  7398. dojo.html.getMarginExtent = function(/* HTMLElement */node, /* string */side){
  7399. // summary
  7400. // returns the width of the requested margin
  7401. return dojo.html._sumPixelValues(node, ["margin-" + side], dojo.html.isPositionAbsolute(node)); // integer
  7402. }
  7403. dojo.html.getPaddingExtent = function(/* HTMLElement */node, /* string */side){
  7404. // summary
  7405. // Returns the width of the requested padding
  7406. return dojo.html._sumPixelValues(node, ["padding-" + side], true); // integer
  7407. }
  7408. dojo.html.getPadding = function(/* HTMLElement */node){
  7409. // summary
  7410. // Returns the width and height of the passed node's padding
  7411. return {
  7412. width: dojo.html._sumPixelValues(node, ["padding-left", "padding-right"], true),
  7413. height: dojo.html._sumPixelValues(node, ["padding-top", "padding-bottom"], true)
  7414. }; // object
  7415. }
  7416. dojo.html.getPadBorder = function(/* HTMLElement */node){
  7417. // summary
  7418. // Returns the width and height of the passed node's padding and border
  7419. var pad = dojo.html.getPadding(node);
  7420. var border = dojo.html.getBorder(node);
  7421. return { width: pad.width + border.width, height: pad.height + border.height }; // object
  7422. }
  7423. dojo.html.getBoxSizing = function(/* HTMLElement */node){
  7424. // summary
  7425. // Returns which box model the passed element is working with
  7426. var h = dojo.render.html;
  7427. var bs = dojo.html.boxSizing;
  7428. if((h.ie)||(h.opera)){
  7429. var cm = document["compatMode"];
  7430. if((cm == "BackCompat")||(cm == "QuirksMode")){
  7431. return bs.BORDER_BOX; // string
  7432. }else{
  7433. return bs.CONTENT_BOX; // string
  7434. }
  7435. }else{
  7436. if(arguments.length == 0){ node = document.documentElement; }
  7437. var sizing = dojo.html.getStyle(node, "-moz-box-sizing");
  7438. if(!sizing){ sizing = dojo.html.getStyle(node, "box-sizing"); }
  7439. return (sizing ? sizing : bs.CONTENT_BOX); // string
  7440. }
  7441. }
  7442. dojo.html.isBorderBox = function(/* HTMLElement */node){
  7443. // summary
  7444. // returns whether the passed element is using border box sizing or not.
  7445. return (dojo.html.getBoxSizing(node) == dojo.html.boxSizing.BORDER_BOX); // boolean
  7446. }
  7447. dojo.html.getBorderBox = function(/* HTMLElement */node){
  7448. // summary
  7449. // Returns the dimensions of the passed element based on border-box sizing.
  7450. node = dojo.byId(node);
  7451. return { width: node.offsetWidth, height: node.offsetHeight }; // object
  7452. }
  7453. dojo.html.getPaddingBox = function(/* HTMLElement */node){
  7454. // summary
  7455. // Returns the dimensions of the padding box (see http://www.w3.org/TR/CSS21/box.html)
  7456. var box = dojo.html.getBorderBox(node);
  7457. var border = dojo.html.getBorder(node);
  7458. return {
  7459. width: box.width - border.width,
  7460. height:box.height - border.height
  7461. }; // object
  7462. }
  7463. dojo.html.getContentBox = function(/* HTMLElement */node){
  7464. // summary
  7465. // Returns the dimensions of the content box (see http://www.w3.org/TR/CSS21/box.html)
  7466. node = dojo.byId(node);
  7467. var padborder = dojo.html.getPadBorder(node);
  7468. return {
  7469. width: node.offsetWidth - padborder.width,
  7470. height: node.offsetHeight - padborder.height
  7471. }; // object
  7472. }
  7473. dojo.html.setContentBox = function(/* HTMLElement */node, /* object */args){
  7474. // summary
  7475. // Sets the dimensions of the passed node according to content sizing.
  7476. node = dojo.byId(node);
  7477. var width = 0; var height = 0;
  7478. var isbb = dojo.html.isBorderBox(node);
  7479. var padborder = (isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0});
  7480. var ret = {};
  7481. if(typeof args.width != "undefined"){
  7482. width = args.width + padborder.width;
  7483. ret.width = dojo.html.setPositivePixelValue(node, "width", width);
  7484. }
  7485. if(typeof args.height != "undefined"){
  7486. height = args.height + padborder.height;
  7487. ret.height = dojo.html.setPositivePixelValue(node, "height", height);
  7488. }
  7489. return ret; // object
  7490. }
  7491. dojo.html.getMarginBox = function(/* HTMLElement */node){
  7492. // summary
  7493. // returns the dimensions of the passed node including any margins.
  7494. var borderbox = dojo.html.getBorderBox(node);
  7495. var margin = dojo.html.getMargin(node);
  7496. return { width: borderbox.width + margin.width, height: borderbox.height + margin.height }; // object
  7497. }
  7498. dojo.html.setMarginBox = function(/* HTMLElement */node, /* object */args){
  7499. // summary
  7500. // Sets the dimensions of the passed node using margin box calcs.
  7501. node = dojo.byId(node);
  7502. var width = 0; var height = 0;
  7503. var isbb = dojo.html.isBorderBox(node);
  7504. var padborder = (!isbb ? dojo.html.getPadBorder(node) : { width: 0, height: 0 });
  7505. var margin = dojo.html.getMargin(node);
  7506. var ret = {};
  7507. if(typeof args.width != "undefined"){
  7508. width = args.width - padborder.width;
  7509. width -= margin.width;
  7510. ret.width = dojo.html.setPositivePixelValue(node, "width", width);
  7511. }
  7512. if(typeof args.height != "undefined"){
  7513. height = args.height - padborder.height;
  7514. height -= margin.height;
  7515. ret.height = dojo.html.setPositivePixelValue(node, "height", height);
  7516. }
  7517. return ret; // object
  7518. }
  7519. dojo.html.getElementBox = function(/* HTMLElement */node, /* string */type){
  7520. // summary
  7521. // return dimesions of a node based on the passed box model type.
  7522. var bs = dojo.html.boxSizing;
  7523. switch(type){
  7524. case bs.MARGIN_BOX:
  7525. return dojo.html.getMarginBox(node); // object
  7526. case bs.BORDER_BOX:
  7527. return dojo.html.getBorderBox(node); // object
  7528. case bs.PADDING_BOX:
  7529. return dojo.html.getPaddingBox(node); // object
  7530. case bs.CONTENT_BOX:
  7531. default:
  7532. return dojo.html.getContentBox(node); // object
  7533. }
  7534. }
  7535. // in: coordinate array [x,y,w,h] or dom node
  7536. // return: coordinate object
  7537. dojo.html.toCoordinateObject = dojo.html.toCoordinateArray = function(/* array */coords, /* boolean? */includeScroll, /* string? */boxtype) {
  7538. // summary
  7539. // Converts an array of coordinates into an object of named arguments.
  7540. if(coords instanceof Array || typeof coords == "array"){
  7541. dojo.deprecated("dojo.html.toCoordinateArray", "use dojo.html.toCoordinateObject({left: , top: , width: , height: }) instead", "0.5");
  7542. // coords is already an array (of format [x,y,w,h]), just return it
  7543. while ( coords.length < 4 ) { coords.push(0); }
  7544. while ( coords.length > 4 ) { coords.pop(); }
  7545. var ret = {
  7546. left: coords[0],
  7547. top: coords[1],
  7548. width: coords[2],
  7549. height: coords[3]
  7550. };
  7551. }else if(!coords.nodeType && !(coords instanceof String || typeof coords == "string") &&
  7552. ('width' in coords || 'height' in coords || 'left' in coords ||
  7553. 'x' in coords || 'top' in coords || 'y' in coords)){
  7554. // coords is a coordinate object or at least part of one
  7555. var ret = {
  7556. left: coords.left||coords.x||0,
  7557. top: coords.top||coords.y||0,
  7558. width: coords.width||0,
  7559. height: coords.height||0
  7560. };
  7561. }else{
  7562. // coords is an dom object (or dom object id); return it's coordinates
  7563. var node = dojo.byId(coords);
  7564. var pos = dojo.html.abs(node, includeScroll, boxtype);
  7565. var marginbox = dojo.html.getMarginBox(node);
  7566. var ret = {
  7567. left: pos.left,
  7568. top: pos.top,
  7569. width: marginbox.width,
  7570. height: marginbox.height
  7571. };
  7572. }
  7573. ret.x = ret.left;
  7574. ret.y = ret.top;
  7575. return ret; // object
  7576. }
  7577. dojo.html.setMarginBoxWidth = dojo.html.setOuterWidth = function(node, width){
  7578. return dojo.html._callDeprecated("setMarginBoxWidth", "setMarginBox", arguments, "width");
  7579. }
  7580. dojo.html.setMarginBoxHeight = dojo.html.setOuterHeight = function(){
  7581. return dojo.html._callDeprecated("setMarginBoxHeight", "setMarginBox", arguments, "height");
  7582. }
  7583. dojo.html.getMarginBoxWidth = dojo.html.getOuterWidth = function(){
  7584. return dojo.html._callDeprecated("getMarginBoxWidth", "getMarginBox", arguments, null, "width");
  7585. }
  7586. dojo.html.getMarginBoxHeight = dojo.html.getOuterHeight = function(){
  7587. return dojo.html._callDeprecated("getMarginBoxHeight", "getMarginBox", arguments, null, "height");
  7588. }
  7589. dojo.html.getTotalOffset = function(node, type, includeScroll){
  7590. return dojo.html._callDeprecated("getTotalOffset", "getAbsolutePosition", arguments, null, type);
  7591. }
  7592. dojo.html.getAbsoluteX = function(node, includeScroll){
  7593. return dojo.html._callDeprecated("getAbsoluteX", "getAbsolutePosition", arguments, null, "x");
  7594. }
  7595. dojo.html.getAbsoluteY = function(node, includeScroll){
  7596. return dojo.html._callDeprecated("getAbsoluteY", "getAbsolutePosition", arguments, null, "y");
  7597. }
  7598. dojo.html.totalOffsetLeft = function(node, includeScroll){
  7599. return dojo.html._callDeprecated("totalOffsetLeft", "getAbsolutePosition", arguments, null, "left");
  7600. }
  7601. dojo.html.totalOffsetTop = function(node, includeScroll){
  7602. return dojo.html._callDeprecated("totalOffsetTop", "getAbsolutePosition", arguments, null, "top");
  7603. }
  7604. dojo.html.getMarginWidth = function(node){
  7605. return dojo.html._callDeprecated("getMarginWidth", "getMargin", arguments, null, "width");
  7606. }
  7607. dojo.html.getMarginHeight = function(node){
  7608. return dojo.html._callDeprecated("getMarginHeight", "getMargin", arguments, null, "height");
  7609. }
  7610. dojo.html.getBorderWidth = function(node){
  7611. return dojo.html._callDeprecated("getBorderWidth", "getBorder", arguments, null, "width");
  7612. }
  7613. dojo.html.getBorderHeight = function(node){
  7614. return dojo.html._callDeprecated("getBorderHeight", "getBorder", arguments, null, "height");
  7615. }
  7616. dojo.html.getPaddingWidth = function(node){
  7617. return dojo.html._callDeprecated("getPaddingWidth", "getPadding", arguments, null, "width");
  7618. }
  7619. dojo.html.getPaddingHeight = function(node){
  7620. return dojo.html._callDeprecated("getPaddingHeight", "getPadding", arguments, null, "height");
  7621. }
  7622. dojo.html.getPadBorderWidth = function(node){
  7623. return dojo.html._callDeprecated("getPadBorderWidth", "getPadBorder", arguments, null, "width");
  7624. }
  7625. dojo.html.getPadBorderHeight = function(node){
  7626. return dojo.html._callDeprecated("getPadBorderHeight", "getPadBorder", arguments, null, "height");
  7627. }
  7628. dojo.html.getBorderBoxWidth = dojo.html.getInnerWidth = function(){
  7629. return dojo.html._callDeprecated("getBorderBoxWidth", "getBorderBox", arguments, null, "width");
  7630. }
  7631. dojo.html.getBorderBoxHeight = dojo.html.getInnerHeight = function(){
  7632. return dojo.html._callDeprecated("getBorderBoxHeight", "getBorderBox", arguments, null, "height");
  7633. }
  7634. dojo.html.getContentBoxWidth = dojo.html.getContentWidth = function(){
  7635. return dojo.html._callDeprecated("getContentBoxWidth", "getContentBox", arguments, null, "width");
  7636. }
  7637. dojo.html.getContentBoxHeight = dojo.html.getContentHeight = function(){
  7638. return dojo.html._callDeprecated("getContentBoxHeight", "getContentBox", arguments, null, "height");
  7639. }
  7640. dojo.html.setContentBoxWidth = dojo.html.setContentWidth = function(node, width){
  7641. return dojo.html._callDeprecated("setContentBoxWidth", "setContentBox", arguments, "width");
  7642. }
  7643. dojo.html.setContentBoxHeight = dojo.html.setContentHeight = function(node, height){
  7644. return dojo.html._callDeprecated("setContentBoxHeight", "setContentBox", arguments, "height");
  7645. }
  7646. dojo.provide("dojo.lfx.html");
  7647. dojo.lfx.html._byId = function(nodes){
  7648. if(!nodes){ return []; }
  7649. if(dojo.lang.isArrayLike(nodes)){
  7650. if(!nodes.alreadyChecked){
  7651. var n = [];
  7652. dojo.lang.forEach(nodes, function(node){
  7653. n.push(dojo.byId(node));
  7654. });
  7655. n.alreadyChecked = true;
  7656. return n;
  7657. }else{
  7658. return nodes;
  7659. }
  7660. }else{
  7661. var n = [];
  7662. n.push(dojo.byId(nodes));
  7663. n.alreadyChecked = true;
  7664. return n;
  7665. }
  7666. }
  7667. dojo.lfx.html.propertyAnimation = function( /*DOMNode[]*/ nodes,
  7668. /*Object[]*/ propertyMap,
  7669. /*int*/ duration,
  7670. /*function*/ easing,
  7671. /*Object*/ handlers){
  7672. // summary: Returns an animation that will transition the properties of "nodes"
  7673. // depending how they are defined in "propertyMap".
  7674. // nodes: An array of DOMNodes or one DOMNode.
  7675. // propertyMap: { property: String, start: Decimal?, end: Decimal?, units: String? }
  7676. // An array of objects defining properties to change.
  7677. // duration: Duration of the animation in milliseconds.
  7678. // easing: An easing function.
  7679. // handlers: { handler: Function?, onstart: Function?, onstop: Function?, onanimate: Function? }
  7680. nodes = dojo.lfx.html._byId(nodes);
  7681. var targs = {
  7682. "propertyMap": propertyMap,
  7683. "nodes": nodes,
  7684. "duration": duration,
  7685. "easing": easing||dojo.lfx.easeDefault
  7686. };
  7687. var setEmUp = function(args){
  7688. if(args.nodes.length==1){
  7689. // FIXME: we're only supporting start-value filling when one node is
  7690. // passed
  7691. var pm = args.propertyMap;
  7692. if(!dojo.lang.isArray(args.propertyMap)){
  7693. // it's stupid to have to pack an array with a set of objects
  7694. // when you can just pass in an object list
  7695. var parr = [];
  7696. for(var pname in pm){
  7697. pm[pname].property = pname;
  7698. parr.push(pm[pname]);
  7699. }
  7700. pm = args.propertyMap = parr;
  7701. }
  7702. dojo.lang.forEach(pm, function(prop){
  7703. if(dj_undef("start", prop)){
  7704. if(prop.property != "opacity"){
  7705. prop.start = parseInt(dojo.html.getComputedStyle(args.nodes[0], prop.property));
  7706. }else{
  7707. prop.start = dojo.html.getOpacity(args.nodes[0]);
  7708. }
  7709. }
  7710. });
  7711. }
  7712. }
  7713. var coordsAsInts = function(coords){
  7714. var cints = [];
  7715. dojo.lang.forEach(coords, function(c){
  7716. cints.push(Math.round(c));
  7717. });
  7718. return cints;
  7719. }
  7720. var setStyle = function(n, style){
  7721. n = dojo.byId(n);
  7722. if(!n || !n.style){ return; }
  7723. for(var s in style){
  7724. if(s == "opacity"){
  7725. dojo.html.setOpacity(n, style[s]);
  7726. }else{
  7727. n.style[s] = style[s];
  7728. }
  7729. }
  7730. }
  7731. var propLine = function(properties){
  7732. this._properties = properties;
  7733. this.diffs = new Array(properties.length);
  7734. dojo.lang.forEach(properties, function(prop, i){
  7735. // calculate the end - start to optimize a bit
  7736. if(dojo.lang.isFunction(prop.start)){
  7737. prop.start = prop.start(prop, i);
  7738. }
  7739. if(dojo.lang.isFunction(prop.end)){
  7740. prop.end = prop.end(prop, i);
  7741. }
  7742. if(dojo.lang.isArray(prop.start)){
  7743. // don't loop through the arrays
  7744. this.diffs[i] = null;
  7745. }else if(prop.start instanceof dojo.gfx.color.Color){
  7746. // save these so we don't have to call toRgb() every getValue() call
  7747. prop.startRgb = prop.start.toRgb();
  7748. prop.endRgb = prop.end.toRgb();
  7749. }else{
  7750. this.diffs[i] = prop.end - prop.start;
  7751. }
  7752. }, this);
  7753. this.getValue = function(n){
  7754. var ret = {};
  7755. dojo.lang.forEach(this._properties, function(prop, i){
  7756. var value = null;
  7757. if(dojo.lang.isArray(prop.start)){
  7758. // FIXME: what to do here?
  7759. }else if(prop.start instanceof dojo.gfx.color.Color){
  7760. value = (prop.units||"rgb") + "(";
  7761. for(var j = 0 ; j < prop.startRgb.length ; j++){
  7762. value += Math.round(((prop.endRgb[j] - prop.startRgb[j]) * n) + prop.startRgb[j]) + (j < prop.startRgb.length - 1 ? "," : "");
  7763. }
  7764. value += ")";
  7765. }else{
  7766. value = ((this.diffs[i]) * n) + prop.start + (prop.property != "opacity" ? prop.units||"px" : "");
  7767. }
  7768. ret[dojo.html.toCamelCase(prop.property)] = value;
  7769. }, this);
  7770. return ret;
  7771. }
  7772. }
  7773. var anim = new dojo.lfx.Animation({
  7774. beforeBegin: function(){
  7775. setEmUp(targs);
  7776. anim.curve = new propLine(targs.propertyMap);
  7777. },
  7778. onAnimate: function(propValues){
  7779. dojo.lang.forEach(targs.nodes, function(node){
  7780. setStyle(node, propValues);
  7781. });
  7782. }
  7783. },
  7784. targs.duration,
  7785. null,
  7786. targs.easing
  7787. );
  7788. if(handlers){
  7789. for(var x in handlers){
  7790. if(dojo.lang.isFunction(handlers[x])){
  7791. anim.connect(x, anim, handlers[x]);
  7792. }
  7793. }
  7794. }
  7795. return anim; // dojo.lfx.Animation
  7796. }
  7797. dojo.lfx.html._makeFadeable = function(nodes){
  7798. var makeFade = function(node){
  7799. if(dojo.render.html.ie){
  7800. // only set the zoom if the "tickle" value would be the same as the
  7801. // default
  7802. if( (node.style.zoom.length == 0) &&
  7803. (dojo.html.getStyle(node, "zoom") == "normal") ){
  7804. // make sure the node "hasLayout"
  7805. // NOTE: this has been tested with larger and smaller user-set text
  7806. // sizes and works fine
  7807. node.style.zoom = "1";
  7808. // node.style.zoom = "normal";
  7809. }
  7810. // don't set the width to auto if it didn't already cascade that way.
  7811. // We don't want to f anyones designs
  7812. if( (node.style.width.length == 0) &&
  7813. (dojo.html.getStyle(node, "width") == "auto") ){
  7814. node.style.width = "auto";
  7815. }
  7816. }
  7817. }
  7818. if(dojo.lang.isArrayLike(nodes)){
  7819. dojo.lang.forEach(nodes, makeFade);
  7820. }else{
  7821. makeFade(nodes);
  7822. }
  7823. }
  7824. dojo.lfx.html.fade = function(/*DOMNode[]*/ nodes,
  7825. /*Object*/values,
  7826. /*int?*/ duration,
  7827. /*Function?*/ easing,
  7828. /*Function?*/ callback){
  7829. // summary:Returns an animation that will fade the "nodes" from the start to end values passed.
  7830. // nodes: An array of DOMNodes or one DOMNode.
  7831. // values: { start: Decimal?, end: Decimal? }
  7832. // duration: Duration of the animation in milliseconds.
  7833. // easing: An easing function.
  7834. // callback: Function to run at the end of the animation.
  7835. nodes = dojo.lfx.html._byId(nodes);
  7836. var props = { property: "opacity" };
  7837. if(!dj_undef("start", values)){
  7838. props.start = values.start;
  7839. }else{
  7840. props.start = function(){ return dojo.html.getOpacity(nodes[0]); };
  7841. }
  7842. if(!dj_undef("end", values)){
  7843. props.end = values.end;
  7844. }else{
  7845. dojo.raise("dojo.lfx.html.fade needs an end value");
  7846. }
  7847. var anim = dojo.lfx.propertyAnimation(nodes, [ props ], duration, easing);
  7848. anim.connect("beforeBegin", function(){
  7849. dojo.lfx.html._makeFadeable(nodes);
  7850. });
  7851. if(callback){
  7852. anim.connect("onEnd", function(){ callback(nodes, anim); });
  7853. }
  7854. return anim; // dojo.lfx.Animation
  7855. }
  7856. dojo.lfx.html.fadeIn = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
  7857. // summary: Returns an animation that will fade "nodes" from its current opacity to fully opaque.
  7858. // nodes: An array of DOMNodes or one DOMNode.
  7859. // duration: Duration of the animation in milliseconds.
  7860. // easing: An easing function.
  7861. // callback: Function to run at the end of the animation.
  7862. return dojo.lfx.html.fade(nodes, { end: 1 }, duration, easing, callback); // dojo.lfx.Animation
  7863. }
  7864. dojo.lfx.html.fadeOut = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
  7865. // summary: Returns an animation that will fade "nodes" from its current opacity to fully transparent.
  7866. // nodes: An array of DOMNodes or one DOMNode.
  7867. // duration: Duration of the animation in milliseconds.
  7868. // easing: An easing function.
  7869. // callback: Function to run at the end of the animation.
  7870. return dojo.lfx.html.fade(nodes, { end: 0 }, duration, easing, callback); // dojo.lfx.Animation
  7871. }
  7872. dojo.lfx.html.fadeShow = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
  7873. // summary: Returns an animation that will fade "nodes" from transparent to opaque and shows
  7874. // "nodes" at the end if it is hidden.
  7875. // nodes: An array of DOMNodes or one DOMNode.
  7876. // duration: Duration of the animation in milliseconds.
  7877. // easing: An easing function.
  7878. // callback: Function to run at the end of the animation.
  7879. nodes=dojo.lfx.html._byId(nodes);
  7880. dojo.lang.forEach(nodes, function(node){
  7881. dojo.html.setOpacity(node, 0.0);
  7882. });
  7883. var anim = dojo.lfx.html.fadeIn(nodes, duration, easing, callback);
  7884. anim.connect("beforeBegin", function(){
  7885. if(dojo.lang.isArrayLike(nodes)){
  7886. dojo.lang.forEach(nodes, dojo.html.show);
  7887. }else{
  7888. dojo.html.show(nodes);
  7889. }
  7890. });
  7891. return anim; // dojo.lfx.Animation
  7892. }
  7893. dojo.lfx.html.fadeHide = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
  7894. // summary: Returns an animation that will fade "nodes" from its current opacity to opaque and hides
  7895. // "nodes" at the end.
  7896. // nodes: An array of DOMNodes or one DOMNode.
  7897. // duration: Duration of the animation in milliseconds.
  7898. // easing: An easing function.
  7899. // callback: Function to run at the end of the animation.
  7900. var anim = dojo.lfx.html.fadeOut(nodes, duration, easing, function(){
  7901. if(dojo.lang.isArrayLike(nodes)){
  7902. dojo.lang.forEach(nodes, dojo.html.hide);
  7903. }else{
  7904. dojo.html.hide(nodes);
  7905. }
  7906. if(callback){ callback(nodes, anim); }
  7907. });
  7908. return anim; // dojo.lfx.Animation
  7909. }
  7910. dojo.lfx.html.wipeIn = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
  7911. // summary: Returns an animation that will show and wipe in "nodes".
  7912. // nodes: An array of DOMNodes or one DOMNode.
  7913. // duration: Duration of the animation in milliseconds.
  7914. // easing: An easing function.
  7915. // callback: Function to run at the end of the animation.
  7916. nodes = dojo.lfx.html._byId(nodes);
  7917. var anims = [];
  7918. dojo.lang.forEach(nodes, function(node){
  7919. var oprop = { }; // old properties of node (before we mucked w/them)
  7920. // get node height, either it's natural height or it's height specified via style or class attributes
  7921. // (for FF, the node has to be (temporarily) rendered to measure height)
  7922. dojo.html.show(node);
  7923. var height = dojo.html.getBorderBox(node).height;
  7924. dojo.html.hide(node);
  7925. var anim = dojo.lfx.propertyAnimation(node,
  7926. { "height": {
  7927. start: 1, // 0 causes IE to display the whole panel
  7928. end: function(){ return height; }
  7929. }
  7930. },
  7931. duration,
  7932. easing);
  7933. anim.connect("beforeBegin", function(){
  7934. oprop.overflow = node.style.overflow;
  7935. oprop.height = node.style.height;
  7936. with(node.style){
  7937. overflow = "hidden";
  7938. height = "1px"; // 0 causes IE to display the whole panel
  7939. }
  7940. dojo.html.show(node);
  7941. });
  7942. anim.connect("onEnd", function(){
  7943. with(node.style){
  7944. overflow = oprop.overflow;
  7945. height = oprop.height;
  7946. }
  7947. if(callback){ callback(node, anim); }
  7948. });
  7949. anims.push(anim);
  7950. });
  7951. return dojo.lfx.combine(anims); // dojo.lfx.Combine
  7952. }
  7953. dojo.lfx.html.wipeOut = function(/*DOMNode[]*/ nodes, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
  7954. // summary: Returns an animation that will wipe out and hide "nodes".
  7955. // nodes: An array of DOMNodes or one DOMNode.
  7956. // duration: Duration of the animation in milliseconds.
  7957. // easing: An easing function.
  7958. // callback: Function to run at the end of the animation.
  7959. nodes = dojo.lfx.html._byId(nodes);
  7960. var anims = [];
  7961. dojo.lang.forEach(nodes, function(node){
  7962. var oprop = { }; // old properties of node (before we mucked w/them)
  7963. var anim = dojo.lfx.propertyAnimation(node,
  7964. { "height": {
  7965. start: function(){ return dojo.html.getContentBox(node).height; },
  7966. end: 1 // 0 causes IE to display the whole panel
  7967. }
  7968. },
  7969. duration,
  7970. easing,
  7971. {
  7972. "beforeBegin": function(){
  7973. oprop.overflow = node.style.overflow;
  7974. oprop.height = node.style.height;
  7975. with(node.style){
  7976. overflow = "hidden";
  7977. }
  7978. dojo.html.show(node);
  7979. },
  7980. "onEnd": function(){
  7981. dojo.html.hide(node);
  7982. with(node.style){
  7983. overflow = oprop.overflow;
  7984. height = oprop.height;
  7985. }
  7986. if(callback){ callback(node, anim); }
  7987. }
  7988. }
  7989. );
  7990. anims.push(anim);
  7991. });
  7992. return dojo.lfx.combine(anims); // dojo.lfx.Combine
  7993. }
  7994. dojo.lfx.html.slideTo = function(/*DOMNode*/ nodes,
  7995. /*Object*/ coords,
  7996. /*int?*/ duration,
  7997. /*Function?*/ easing,
  7998. /*Function?*/ callback){
  7999. // summary: Returns an animation that will slide "nodes" from its current position to
  8000. // the position defined in "coords".
  8001. // nodes: An array of DOMNodes or one DOMNode.
  8002. // coords: { top: Decimal?, left: Decimal? }
  8003. // duration: Duration of the animation in milliseconds.
  8004. // easing: An easing function.
  8005. // callback: Function to run at the end of the animation.
  8006. nodes = dojo.lfx.html._byId(nodes);
  8007. var anims = [];
  8008. var compute = dojo.html.getComputedStyle;
  8009. if(dojo.lang.isArray(coords)){
  8010. /* coords: Array
  8011. pId: a */
  8012. dojo.deprecated('dojo.lfx.html.slideTo(node, array)', 'use dojo.lfx.html.slideTo(node, {top: value, left: value});', '0.5');
  8013. coords = { top: coords[0], left: coords[1] };
  8014. }
  8015. dojo.lang.forEach(nodes, function(node){
  8016. var top = null;
  8017. var left = null;
  8018. var init = (function(){
  8019. var innerNode = node;
  8020. return function(){
  8021. var pos = compute(innerNode, 'position');
  8022. top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0);
  8023. left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0);
  8024. if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
  8025. var ret = dojo.html.abs(innerNode, true);
  8026. dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
  8027. top = ret.y;
  8028. left = ret.x;
  8029. }
  8030. }
  8031. })();
  8032. init();
  8033. var anim = dojo.lfx.propertyAnimation(node,
  8034. { "top": { start: top, end: (coords.top||0) },
  8035. "left": { start: left, end: (coords.left||0) }
  8036. },
  8037. duration,
  8038. easing,
  8039. { "beforeBegin": init }
  8040. );
  8041. if(callback){
  8042. anim.connect("onEnd", function(){ callback(nodes, anim); });
  8043. }
  8044. anims.push(anim);
  8045. });
  8046. return dojo.lfx.combine(anims); // dojo.lfx.Combine
  8047. }
  8048. dojo.lfx.html.slideBy = function(/*DOMNode*/ nodes, /*Object*/ coords, /*int?*/ duration, /*Function?*/ easing, /*Function?*/ callback){
  8049. // summary: Returns an animation that will slide "nodes" from its current position
  8050. // to its current position plus the numbers defined in "coords".
  8051. // nodes: An array of DOMNodes or one DOMNode.
  8052. // coords: { top: Decimal?, left: Decimal? }
  8053. // duration: Duration of the animation in milliseconds.
  8054. // easing: An easing function.
  8055. // callback: Function to run at the end of the animation.
  8056. nodes = dojo.lfx.html._byId(nodes);
  8057. var anims = [];
  8058. var compute = dojo.html.getComputedStyle;
  8059. if(dojo.lang.isArray(coords)){
  8060. /* coords: Array
  8061. pId: a */
  8062. dojo.deprecated('dojo.lfx.html.slideBy(node, array)', 'use dojo.lfx.html.slideBy(node, {top: value, left: value});', '0.5');
  8063. coords = { top: coords[0], left: coords[1] };
  8064. }
  8065. dojo.lang.forEach(nodes, function(node){
  8066. var top = null;
  8067. var left = null;
  8068. var init = (function(){
  8069. var innerNode = node;
  8070. return function(){
  8071. var pos = compute(innerNode, 'position');
  8072. top = (pos == 'absolute' ? node.offsetTop : parseInt(compute(node, 'top')) || 0);
  8073. left = (pos == 'absolute' ? node.offsetLeft : parseInt(compute(node, 'left')) || 0);
  8074. if (!dojo.lang.inArray(['absolute', 'relative'], pos)) {
  8075. var ret = dojo.html.abs(innerNode, true);
  8076. dojo.html.setStyleAttributes(innerNode, "position:absolute;top:"+ret.y+"px;left:"+ret.x+"px;");
  8077. top = ret.y;
  8078. left = ret.x;
  8079. }
  8080. }
  8081. })();
  8082. init();
  8083. var anim = dojo.lfx.propertyAnimation(node,
  8084. {
  8085. "top": { start: top, end: top+(coords.top||0) },
  8086. "left": { start: left, end: left+(coords.left||0) }
  8087. },
  8088. duration,
  8089. easing).connect("beforeBegin", init);
  8090. if(callback){
  8091. anim.connect("onEnd", function(){ callback(nodes, anim); });
  8092. }
  8093. anims.push(anim);
  8094. });
  8095. return dojo.lfx.combine(anims); // dojo.lfx.Combine
  8096. }
  8097. dojo.lfx.html.explode = function(/*DOMNode*/ start,
  8098. /*DOMNode*/ endNode,
  8099. /*int?*/ duration,
  8100. /*Function?*/ easing,
  8101. /*Function?*/ callback){
  8102. // summary: Returns an animation that will
  8103. // start:
  8104. // endNode:
  8105. // duration: Duration of the animation in milliseconds.
  8106. // easing: An easing function.
  8107. // callback: Function to run at the end of the animation.
  8108. var h = dojo.html;
  8109. start = dojo.byId(start);
  8110. endNode = dojo.byId(endNode);
  8111. var startCoords = h.toCoordinateObject(start, true);
  8112. var outline = document.createElement("div");
  8113. h.copyStyle(outline, endNode);
  8114. if (endNode.explodeClassName) { outline.className = endNode.explodeClassName; }
  8115. with(outline.style){
  8116. position = "absolute";
  8117. display = "none";
  8118. // border = "1px solid black";
  8119. }
  8120. dojo.body().appendChild(outline);
  8121. with(endNode.style){
  8122. visibility = "hidden";
  8123. display = "block";
  8124. }
  8125. var endCoords = h.toCoordinateObject(endNode, true);
  8126. with(endNode.style){
  8127. display = "none";
  8128. visibility = "visible";
  8129. }
  8130. var props = { opacity: { start: 0.5, end: 1.0 } };
  8131. dojo.lang.forEach(["height", "width", "top", "left"], function(type){
  8132. props[type] = { start: startCoords[type], end: endCoords[type] }
  8133. });
  8134. var anim = new dojo.lfx.propertyAnimation(outline,
  8135. props,
  8136. duration,
  8137. easing,
  8138. {
  8139. "beforeBegin": function(){
  8140. h.setDisplay(outline, "block");
  8141. },
  8142. "onEnd": function(){
  8143. h.setDisplay(endNode, "block");
  8144. outline.parentNode.removeChild(outline);
  8145. }
  8146. }
  8147. );
  8148. if(callback){
  8149. anim.connect("onEnd", function(){ callback(endNode, anim); });
  8150. }
  8151. return anim; // dojo.lfx.Animation
  8152. }
  8153. dojo.lfx.html.implode = function(/*DOMNode*/ startNode,
  8154. /*DOMNode*/ end,
  8155. /*int?*/ duration,
  8156. /*Function?*/ easing,
  8157. /*Function?*/ callback){
  8158. // summary: Returns an animation that will
  8159. // startNode:
  8160. // end:
  8161. // duration: Duration of the animation in milliseconds.
  8162. // easing: An easing function.
  8163. // callback: Function to run at the end of the animation.
  8164. var h = dojo.html;
  8165. startNode = dojo.byId(startNode);
  8166. end = dojo.byId(end);
  8167. var startCoords = dojo.html.toCoordinateObject(startNode, true);
  8168. var endCoords = dojo.html.toCoordinateObject(end, true);
  8169. var outline = document.createElement("div");
  8170. dojo.html.copyStyle(outline, startNode);
  8171. if (startNode.explodeClassName) { outline.className = startNode.explodeClassName; }
  8172. dojo.html.setOpacity(outline, 0.3);
  8173. with(outline.style){
  8174. position = "absolute";
  8175. display = "none";
  8176. backgroundColor = h.getStyle(startNode, "background-color").toLowerCase();
  8177. }
  8178. dojo.body().appendChild(outline);
  8179. var props = { opacity: { start: 1.0, end: 0.5 } };
  8180. dojo.lang.forEach(["height", "width", "top", "left"], function(type){
  8181. props[type] = { start: startCoords[type], end: endCoords[type] }
  8182. });
  8183. var anim = new dojo.lfx.propertyAnimation(outline,
  8184. props,
  8185. duration,
  8186. easing,
  8187. {
  8188. "beforeBegin": function(){
  8189. dojo.html.hide(startNode);
  8190. dojo.html.show(outline);
  8191. },
  8192. "onEnd": function(){
  8193. outline.parentNode.removeChild(outline);
  8194. }
  8195. }
  8196. );
  8197. if(callback){
  8198. anim.connect("onEnd", function(){ callback(startNode, anim); });
  8199. }
  8200. return anim; // dojo.lfx.Animation
  8201. }
  8202. dojo.lfx.html.highlight = function(/*DOMNode[]*/ nodes,
  8203. /*dojo.gfx.color.Color*/ startColor,
  8204. /*int?*/ duration,
  8205. /*Function?*/ easing,
  8206. /*Function?*/ callback){
  8207. // summary: Returns an animation that will set the background color
  8208. // of "nodes" to startColor and transition it to "nodes"
  8209. // original color.
  8210. // startColor: Color to transition from.
  8211. // duration: Duration of the animation in milliseconds.
  8212. // easing: An easing function.
  8213. // callback: Function to run at the end of the animation.
  8214. nodes = dojo.lfx.html._byId(nodes);
  8215. var anims = [];
  8216. dojo.lang.forEach(nodes, function(node){
  8217. var color = dojo.html.getBackgroundColor(node);
  8218. var bg = dojo.html.getStyle(node, "background-color").toLowerCase();
  8219. var bgImage = dojo.html.getStyle(node, "background-image");
  8220. var wasTransparent = (bg == "transparent" || bg == "rgba(0, 0, 0, 0)");
  8221. while(color.length > 3) { color.pop(); }
  8222. var rgb = new dojo.gfx.color.Color(startColor);
  8223. var endRgb = new dojo.gfx.color.Color(color);
  8224. var anim = dojo.lfx.propertyAnimation(node,
  8225. { "background-color": { start: rgb, end: endRgb } },
  8226. duration,
  8227. easing,
  8228. {
  8229. "beforeBegin": function(){
  8230. if(bgImage){
  8231. node.style.backgroundImage = "none";
  8232. }
  8233. node.style.backgroundColor = "rgb(" + rgb.toRgb().join(",") + ")";
  8234. },
  8235. "onEnd": function(){
  8236. if(bgImage){
  8237. node.style.backgroundImage = bgImage;
  8238. }
  8239. if(wasTransparent){
  8240. node.style.backgroundColor = "transparent";
  8241. }
  8242. if(callback){
  8243. callback(node, anim);
  8244. }
  8245. }
  8246. }
  8247. );
  8248. anims.push(anim);
  8249. });
  8250. return dojo.lfx.combine(anims); // dojo.lfx.Combine
  8251. }
  8252. dojo.lfx.html.unhighlight = function(/*DOMNode[]*/ nodes,
  8253. /*dojo.gfx.color.Color*/ endColor,
  8254. /*int?*/ duration,
  8255. /*Function?*/ easing,
  8256. /*Function?*/ callback){
  8257. // summary: Returns an animation that will transition "nodes" background color
  8258. // from its current color to "endColor".
  8259. // endColor: Color to transition to.
  8260. // duration: Duration of the animation in milliseconds.
  8261. // easing: An easing function.
  8262. // callback: Function to run at the end of the animation.
  8263. nodes = dojo.lfx.html._byId(nodes);
  8264. var anims = [];
  8265. dojo.lang.forEach(nodes, function(node){
  8266. var color = new dojo.gfx.color.Color(dojo.html.getBackgroundColor(node));
  8267. var rgb = new dojo.gfx.color.Color(endColor);
  8268. var bgImage = dojo.html.getStyle(node, "background-image");
  8269. var anim = dojo.lfx.propertyAnimation(node,
  8270. { "background-color": { start: color, end: rgb } },
  8271. duration,
  8272. easing,
  8273. {
  8274. "beforeBegin": function(){
  8275. if(bgImage){
  8276. node.style.backgroundImage = "none";
  8277. }
  8278. node.style.backgroundColor = "rgb(" + color.toRgb().join(",") + ")";
  8279. },
  8280. "onEnd": function(){
  8281. if(callback){
  8282. callback(node, anim);
  8283. }
  8284. }
  8285. }
  8286. );
  8287. anims.push(anim);
  8288. });
  8289. return dojo.lfx.combine(anims); // dojo.lfx.Combine
  8290. }
  8291. dojo.lang.mixin(dojo.lfx, dojo.lfx.html);
  8292. dojo.provide("dojo.lfx.*");