event.js 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100
  1. /*
  2. This file is part of the DITA Open Toolkit project.
  3. See the accompanying LICENSE file for applicable license.
  4. Copyright (c) 2006, Yahoo! Inc. All rights reserved.
  5. Code licensed under the BSD License:
  6. http://developer.yahoo.net/yui/license.txt
  7. version: 0.10.0
  8. */
  9. /**
  10. * The CustomEvent class lets you define events for your application
  11. * that can be subscribed to by one or more independent component.
  12. *
  13. * @param {String} type The type of event, which is passed to the callback
  14. * when the event fires
  15. * @param {Object} oScope The context the event will fire from. "this" will
  16. * refer to this object in the callback. Default value:
  17. * the window object. The listener can override this.
  18. * @constructor
  19. */
  20. YAHOO.util.CustomEvent = function(type, oScope) {
  21. /**
  22. * The type of event, returned to subscribers when the event fires
  23. * @type string
  24. */
  25. this.type = type;
  26. /**
  27. * The scope the the event will fire from by default. Defaults to the window
  28. * obj
  29. * @type object
  30. */
  31. this.scope = oScope || window;
  32. /**
  33. * The subscribers to this event
  34. * @type Subscriber[]
  35. */
  36. this.subscribers = [];
  37. // Register with the event utility for automatic cleanup. Made optional
  38. // so that CustomEvent can be used independently of pe.event
  39. if (YAHOO.util.Event) {
  40. YAHOO.util.Event.regCE(this);
  41. }
  42. };
  43. YAHOO.util.CustomEvent.prototype = {
  44. /**
  45. * Subscribes the caller to this event
  46. * @param {Function} fn The function to execute
  47. * @param {Object} obj An object to be passed along when the event fires
  48. * @param {boolean} bOverride If true, the obj passed in becomes the execution
  49. * scope of the listener
  50. */
  51. subscribe: function(fn, obj, bOverride) {
  52. this.subscribers.push( new YAHOO.util.Subscriber(fn, obj, bOverride) );
  53. },
  54. /**
  55. * Unsubscribes the caller from this event
  56. * @param {Function} fn The function to execute
  57. * @param {Object} obj An object to be passed along when the event fires
  58. * @return {boolean} True if the subscriber was found and detached.
  59. */
  60. unsubscribe: function(fn, obj) {
  61. var found = false;
  62. for (var i=0, len=this.subscribers.length; i<len; ++i) {
  63. var s = this.subscribers[i];
  64. if (s && s.contains(fn, obj)) {
  65. this._delete(i);
  66. found = true;
  67. }
  68. }
  69. return found;
  70. },
  71. /**
  72. * Notifies the subscribers. The callback functions will be executed
  73. * from the scope specified when the event was created, and with the following
  74. * parameters:
  75. * <pre>
  76. * - The type of event
  77. * - All of the arguments fire() was executed with as an array
  78. * - The custom object (if any) that was passed into the subscribe() method
  79. * </pre>
  80. *
  81. * @param {Array} an arbitrary set of parameters to pass to the handler
  82. */
  83. fire: function() {
  84. for (var i=0, len=this.subscribers.length; i<len; ++i) {
  85. var s = this.subscribers[i];
  86. if (s) {
  87. var scope = (s.override) ? s.obj : this.scope;
  88. s.fn.call(scope, this.type, arguments, s.obj);
  89. }
  90. }
  91. },
  92. /**
  93. * Removes all listeners
  94. */
  95. unsubscribeAll: function() {
  96. for (var i=0, len=this.subscribers.length; i<len; ++i) {
  97. this._delete(i);
  98. }
  99. },
  100. /**
  101. * @private
  102. */
  103. _delete: function(index) {
  104. var s = this.subscribers[index];
  105. if (s) {
  106. delete s.fn;
  107. delete s.obj;
  108. }
  109. delete this.subscribers[index];
  110. }
  111. };
  112. /////////////////////////////////////////////////////////////////////
  113. /**
  114. * @class Stores the subscriber information to be used when the event fires.
  115. * @param {Function} fn The function to execute
  116. * @param {Object} obj An object to be passed along when the event fires
  117. * @param {boolean} bOverride If true, the obj passed in becomes the execution
  118. * scope of the listener
  119. * @constructor
  120. */
  121. YAHOO.util.Subscriber = function(fn, obj, bOverride) {
  122. /**
  123. * The callback that will be execute when the event fires
  124. * @type function
  125. */
  126. this.fn = fn;
  127. /**
  128. * An optional custom object that will passed to the callback when
  129. * the event fires
  130. * @type object
  131. */
  132. this.obj = obj || null;
  133. /**
  134. * The default execution scope for the event listener is defined when the
  135. * event is created (usually the object which contains the event).
  136. * By setting override to true, the execution scope becomes the custom
  137. * object passed in by the subscriber
  138. * @type boolean
  139. */
  140. this.override = (bOverride);
  141. };
  142. /**
  143. * Returns true if the fn and obj match this objects properties.
  144. * Used by the unsubscribe method to match the right subscriber.
  145. *
  146. * @param {Function} fn the function to execute
  147. * @param {Object} obj an object to be passed along when the event fires
  148. * @return {boolean} true if the supplied arguments match this
  149. * subscriber's signature.
  150. */
  151. YAHOO.util.Subscriber.prototype.contains = function(fn, obj) {
  152. return (this.fn == fn && this.obj == obj);
  153. };
  154. /* Copyright (c) 2006 Yahoo! Inc. All rights reserved. */
  155. // Only load this library once. If it is loaded a second time, existing
  156. // events cannot be detached.
  157. if (!YAHOO.util.Event) {
  158. /**
  159. * @class
  160. * The event utility provides functions to add and remove event listeners,
  161. * event cleansing. It also tries to automatically remove listeners it
  162. * registers during the unload event.
  163. * @constructor
  164. */
  165. YAHOO.util.Event = function() {
  166. /**
  167. * True after the onload event has fired
  168. * @type boolean
  169. * @private
  170. */
  171. var loadComplete = false;
  172. /**
  173. * Cache of wrapped listeners
  174. * @type array
  175. * @private
  176. */
  177. var listeners = [];
  178. /**
  179. * Listeners that will be attached during the onload event
  180. * @type array
  181. * @private
  182. */
  183. var delayedListeners = [];
  184. /**
  185. * User-defined unload function that will be fired before all events
  186. * are detached
  187. * @type array
  188. * @private
  189. */
  190. var unloadListeners = [];
  191. /**
  192. * Cache of the custom events that have been defined. Used for
  193. * automatic cleanup
  194. * @type array
  195. * @private
  196. */
  197. var customEvents = [];
  198. /**
  199. * Cache of DOM0 event handlers to work around issues with DOM2 events
  200. * in Safari
  201. * @private
  202. */
  203. var legacyEvents = [];
  204. /**
  205. * Listener stack for DOM0 events
  206. * @private
  207. */
  208. var legacyHandlers = [];
  209. /**
  210. * The number of times to poll after window.onload. This number is
  211. * increased if additional late-bound handlers are requested after
  212. * the page load.
  213. * @private
  214. */
  215. var retryCount = 0;
  216. /**
  217. * onAvailable listeners
  218. * @private
  219. */
  220. var onAvailStack = [];
  221. /**
  222. * Lookup table for legacy events
  223. * @private
  224. */
  225. var legacyMap = [];
  226. /**
  227. * Counter for auto id generation
  228. * @private
  229. */
  230. var counter = 0;
  231. return { // PREPROCESS
  232. /**
  233. * The number of times we should look for elements that are not
  234. * in the DOM at the time the event is requested after the document
  235. * has been loaded. The default is 200@50 ms, so it will poll
  236. * for 10 seconds or until all outstanding handlers are bound
  237. * (whichever comes first).
  238. * @type int
  239. */
  240. POLL_RETRYS: 200,
  241. /**
  242. * The poll interval in milliseconds
  243. * @type int
  244. */
  245. POLL_INTERVAL: 50,
  246. /**
  247. * Element to bind, int constant
  248. * @type int
  249. */
  250. EL: 0,
  251. /**
  252. * Type of event, int constant
  253. * @type int
  254. */
  255. TYPE: 1,
  256. /**
  257. * Function to execute, int constant
  258. * @type int
  259. */
  260. FN: 2,
  261. /**
  262. * Function wrapped for scope correction and cleanup, int constant
  263. * @type int
  264. */
  265. WFN: 3,
  266. /**
  267. * Object passed in by the user that will be returned as a
  268. * parameter to the callback, int constant
  269. * @type int
  270. */
  271. SCOPE: 3,
  272. /**
  273. * Adjusted scope, either the element we are registering the event
  274. * on or the custom object passed in by the listener, int constant
  275. * @type int
  276. */
  277. ADJ_SCOPE: 4,
  278. /**
  279. * Safari detection is necessary to work around the preventDefault
  280. * bug that makes it so you can't cancel a href click from the
  281. * handler. There is not a capabilities check we can use here.
  282. * @private
  283. */
  284. isSafari: (/Safari|Konqueror|KHTML/gi).test(navigator.userAgent),
  285. /**
  286. * IE detection needed to properly calculate pageX and pageY.
  287. * capabilities checking didn't seem to work because another
  288. * browser that does not provide the properties have the values
  289. * calculated in a different manner than IE.
  290. * @private
  291. */
  292. isIE: (!this.isSafari && !navigator.userAgent.match(/opera/gi) &&
  293. navigator.userAgent.match(/msie/gi)),
  294. /**
  295. * @private
  296. */
  297. addDelayedListener: function(el, sType, fn, oScope, bOverride) {
  298. delayedListeners[delayedListeners.length] =
  299. [el, sType, fn, oScope, bOverride];
  300. // If this happens after the inital page load, we need to
  301. // reset the poll counter so that we continue to search for
  302. // the element for a fixed period of time.
  303. if (loadComplete) {
  304. retryCount = this.POLL_RETRYS;
  305. this.startTimeout(0);
  306. // this._tryPreloadAttach();
  307. }
  308. },
  309. /**
  310. * @private
  311. */
  312. startTimeout: function(interval) {
  313. var i = (interval || interval === 0) ? interval : this.POLL_INTERVAL;
  314. var self = this;
  315. var callback = function() { self._tryPreloadAttach(); };
  316. this.timeout = setTimeout(callback, i);
  317. },
  318. /**
  319. * Executes the supplied callback when the item with the supplied
  320. * id is found. This is meant to be used to execute behavior as
  321. * soon as possible as the page loads. If you use this after the
  322. * initial page load it will poll for a fixed time for the element.
  323. * The number of times it will poll and the frequency are
  324. * configurable. By default it will poll for 10 seconds.
  325. * @param {string} p_id the id of the element to look for.
  326. * @param {function} p_fn what to execute when the element is found.
  327. * @param {object} p_obj an optional object to be passed back as
  328. * a parameter to p_fn.
  329. * @param {boolean} p_override If set to true, p_fn will execute
  330. * in the scope of p_obj
  331. *
  332. */
  333. onAvailable: function(p_id, p_fn, p_obj, p_override) {
  334. onAvailStack.push( { id: p_id,
  335. fn: p_fn,
  336. obj: p_obj,
  337. override: p_override } );
  338. retryCount = this.POLL_RETRYS;
  339. this.startTimeout(0);
  340. // this._tryPreloadAttach();
  341. },
  342. /**
  343. * Appends an event handler
  344. *
  345. * @param {Object} el The html element to assign the
  346. * event to
  347. * @param {String} sType The type of event to append
  348. * @param {Function} fn The method the event invokes
  349. * @param {Object} oScope An arbitrary object that will be
  350. * passed as a parameter to the handler
  351. * @param {boolean} bOverride If true, the obj passed in becomes
  352. * the execution scope of the listener
  353. * @return {boolean} True if the action was successful or defered,
  354. * false if one or more of the elements
  355. * could not have the event bound to it.
  356. */
  357. addListener: function(el, sType, fn, oScope, bOverride) {
  358. if (!fn || !fn.call) {
  359. return false;
  360. }
  361. // The el argument can be an array of elements or element ids.
  362. if ( this._isValidCollection(el)) {
  363. var ok = true;
  364. for (var i=0,len=el.length; i<len; ++i) {
  365. ok = ( this.on(el[i],
  366. sType,
  367. fn,
  368. oScope,
  369. bOverride) && ok );
  370. }
  371. return ok;
  372. } else if (typeof el == "string") {
  373. var oEl = this.getEl(el);
  374. // If the el argument is a string, we assume it is
  375. // actually the id of the element. If the page is loaded
  376. // we convert el to the actual element, otherwise we
  377. // defer attaching the event until onload event fires
  378. // check to see if we need to delay hooking up the event
  379. // until after the page loads.
  380. if (loadComplete && oEl) {
  381. el = oEl;
  382. } else {
  383. // defer adding the event until onload fires
  384. this.addDelayedListener(el,
  385. sType,
  386. fn,
  387. oScope,
  388. bOverride);
  389. return true;
  390. }
  391. }
  392. // Element should be an html element or an array if we get
  393. // here.
  394. if (!el) {
  395. return false;
  396. }
  397. // we need to make sure we fire registered unload events
  398. // prior to automatically unhooking them. So we hang on to
  399. // these instead of attaching them to the window and fire the
  400. // handles explicitly during our one unload event.
  401. if ("unload" == sType && oScope !== this) {
  402. unloadListeners[unloadListeners.length] =
  403. [el, sType, fn, oScope, bOverride];
  404. return true;
  405. }
  406. // if the user chooses to override the scope, we use the custom
  407. // object passed in, otherwise the executing scope will be the
  408. // HTML element that the event is registered on
  409. var scope = (bOverride) ? oScope : el;
  410. // wrap the function so we can return the oScope object when
  411. // the event fires;
  412. var wrappedFn = function(e) {
  413. return fn.call(scope, YAHOO.util.Event.getEvent(e),
  414. oScope);
  415. };
  416. var li = [el, sType, fn, wrappedFn, scope];
  417. var index = listeners.length;
  418. // cache the listener so we can try to automatically unload
  419. listeners[index] = li;
  420. if (this.useLegacyEvent(el, sType)) {
  421. var legacyIndex = this.getLegacyIndex(el, sType);
  422. if (legacyIndex == -1) {
  423. legacyIndex = legacyEvents.length;
  424. legacyMap[el.id + sType] = legacyIndex;
  425. // cache the signature for the DOM0 event, and
  426. // include the existing handler for the event, if any
  427. legacyEvents[legacyIndex] =
  428. [el, sType, el["on" + sType]];
  429. legacyHandlers[legacyIndex] = [];
  430. el["on" + sType] =
  431. function(e) {
  432. YAHOO.util.Event.fireLegacyEvent(
  433. YAHOO.util.Event.getEvent(e), legacyIndex);
  434. };
  435. }
  436. // add a reference to the wrapped listener to our custom
  437. // stack of events
  438. legacyHandlers[legacyIndex].push(index);
  439. // DOM2 Event model
  440. } else if (el.addEventListener) {
  441. el.addEventListener(sType, wrappedFn, false);
  442. // Internet Explorer abstraction
  443. } else if (el.attachEvent) {
  444. el.attachEvent("on" + sType, wrappedFn);
  445. }
  446. return true;
  447. },
  448. /**
  449. * Shorthand for YAHOO.util.Event.addListener
  450. * @type function
  451. */
  452. // on: this.addListener,
  453. /**
  454. * When using legacy events, the handler is routed to this object
  455. * so we can fire our custom listener stack.
  456. * @private
  457. */
  458. fireLegacyEvent: function(e, legacyIndex) {
  459. var ok = true;
  460. var le = legacyHandlers[legacyIndex];
  461. for (var i=0,len=le.length; i<len; ++i) {
  462. var index = le[i];
  463. if (index) {
  464. var li = listeners[index];
  465. if ( li && li[this.WFN] ) {
  466. var scope = li[this.ADJ_SCOPE];
  467. var ret = li[this.WFN].call(scope, e);
  468. ok = (ok && ret);
  469. } else {
  470. // This listener was removed, so delete it from
  471. // the array
  472. delete le[i];
  473. }
  474. }
  475. }
  476. return ok;
  477. },
  478. /**
  479. * Returns the legacy event index that matches the supplied
  480. * signature
  481. * @private
  482. */
  483. getLegacyIndex: function(el, sType) {
  484. /*
  485. for (var i=0,len=legacyEvents.length; i<len; ++i) {
  486. var le = legacyEvents[i];
  487. if (le && le[0] === el && le[1] === sType) {
  488. return i;
  489. }
  490. }
  491. return -1;
  492. */
  493. var key = this.generateId(el) + sType;
  494. if (typeof legacyMap[key] == "undefined") {
  495. return -1;
  496. } else {
  497. return legacyMap[key];
  498. }
  499. },
  500. /**
  501. * Logic that determines when we should automatically use legacy
  502. * events instead of DOM2 events.
  503. * @private
  504. */
  505. useLegacyEvent: function(el, sType) {
  506. if (!el.addEventListener && !el.attachEvent) {
  507. return true;
  508. } else if (this.isSafari) {
  509. if ("click" == sType || "dblclick" == sType) {
  510. return true;
  511. }
  512. }
  513. return false;
  514. },
  515. /**
  516. * Removes an event handler
  517. *
  518. * @param {Object} el the html element or the id of the element to
  519. * assign the event to.
  520. * @param {String} sType the type of event to remove
  521. * @param {Function} fn the method the event invokes
  522. * @return {boolean} true if the unbind was successful, false
  523. * otherwise
  524. */
  525. removeListener: function(el, sType, fn, index) {
  526. if (!fn || !fn.call) {
  527. return false;
  528. }
  529. // The el argument can be a string
  530. if (typeof el == "string") {
  531. el = this.getEl(el);
  532. // The el argument can be an array of elements or element ids.
  533. } else if ( this._isValidCollection(el)) {
  534. var ok = true;
  535. for (var i=0,len=el.length; i<len; ++i) {
  536. ok = ( this.removeListener(el[i], sType, fn) && ok );
  537. }
  538. return ok;
  539. }
  540. if ("unload" == sType) {
  541. for (i=0, len=unloadListeners.length; i<len; i++) {
  542. var li = unloadListeners[i];
  543. if (li &&
  544. li[0] == el &&
  545. li[1] == sType &&
  546. li[2] == fn) {
  547. delete unloadListeners[i];
  548. return true;
  549. }
  550. }
  551. return false;
  552. }
  553. var cacheItem = null;
  554. if ("undefined" == typeof index) {
  555. index = this._getCacheIndex(el, sType, fn);
  556. }
  557. if (index >= 0) {
  558. cacheItem = listeners[index];
  559. }
  560. if (!el || !cacheItem) {
  561. return false;
  562. }
  563. if (el.removeEventListener) {
  564. el.removeEventListener(sType, cacheItem[this.WFN], false);
  565. } else if (el.detachEvent) {
  566. el.detachEvent("on" + sType, cacheItem[this.WFN]);
  567. }
  568. // removed the wrapped handler
  569. delete listeners[index][this.WFN];
  570. delete listeners[index][this.FN];
  571. delete listeners[index];
  572. return true;
  573. },
  574. /**
  575. * Returns the event's target element
  576. * @param {Event} ev the event
  577. * @param {boolean} resolveTextNode when set to true the target's
  578. * parent will be returned if the target is a
  579. * text node
  580. * @return {HTMLElement} the event's target
  581. */
  582. getTarget: function(ev, resolveTextNode) {
  583. var t = ev.target || ev.srcElement;
  584. if (resolveTextNode && t && "#text" == t.nodeName) {
  585. return t.parentNode;
  586. } else {
  587. return t;
  588. }
  589. },
  590. /**
  591. * Returns the event's pageX
  592. * @param {Event} ev the event
  593. * @return {int} the event's pageX
  594. */
  595. getPageX: function(ev) {
  596. var x = ev.pageX;
  597. if (!x && 0 !== x) {
  598. x = ev.clientX || 0;
  599. if ( this.isIE ) {
  600. x += this._getScrollLeft();
  601. }
  602. }
  603. return x;
  604. },
  605. /**
  606. * Returns the event's pageY
  607. * @param {Event} ev the event
  608. * @return {int} the event's pageY
  609. */
  610. getPageY: function(ev) {
  611. var y = ev.pageY;
  612. if (!y && 0 !== y) {
  613. y = ev.clientY || 0;
  614. if ( this.isIE ) {
  615. y += this._getScrollTop();
  616. }
  617. }
  618. return y;
  619. },
  620. /**
  621. * Returns the pageX and pageY properties as an indexed array.
  622. * @type int[]
  623. */
  624. getXY: function(ev) {
  625. return [this.getPageX(ev), this.getPageY(ev)];
  626. },
  627. /**
  628. * Returns the event's related target
  629. * @param {Event} ev the event
  630. * @return {HTMLElement} the event's relatedTarget
  631. */
  632. getRelatedTarget: function(ev) {
  633. var t = ev.relatedTarget;
  634. if (!t) {
  635. if (ev.type == "mouseout") {
  636. t = ev.toElement;
  637. } else if (ev.type == "mouseover") {
  638. t = ev.fromElement;
  639. }
  640. }
  641. return t;
  642. },
  643. /**
  644. * Returns the time of the event. If the time is not included, the
  645. * event is modified using the current time.
  646. * @param {Event} ev the event
  647. * @return {Date} the time of the event
  648. */
  649. getTime: function(ev) {
  650. if (!ev.time) {
  651. var t = new Date().getTime();
  652. try {
  653. ev.time = t;
  654. } catch(e) {
  655. // can't set the time property
  656. return t;
  657. }
  658. }
  659. return ev.time;
  660. },
  661. /**
  662. * Convenience method for stopPropagation + preventDefault
  663. * @param {Event} ev the event
  664. */
  665. stopEvent: function(ev) {
  666. this.stopPropagation(ev);
  667. this.preventDefault(ev);
  668. },
  669. /**
  670. * Stops event propagation
  671. * @param {Event} ev the event
  672. */
  673. stopPropagation: function(ev) {
  674. if (ev.stopPropagation) {
  675. ev.stopPropagation();
  676. } else {
  677. ev.cancelBubble = true;
  678. }
  679. },
  680. /**
  681. * Prevents the default behavior of the event
  682. * @param {Event} ev the event
  683. */
  684. preventDefault: function(ev) {
  685. if (ev.preventDefault) {
  686. ev.preventDefault();
  687. } else {
  688. ev.returnValue = false;
  689. }
  690. },
  691. /**
  692. * Finds the event in the window object, the caller's arguments, or
  693. * in the arguments of another method in the callstack. This is
  694. * executed automatically for events registered through the event
  695. * manager, so the implementer should not normally need to execute
  696. * this function at all.
  697. * @param {Event} the event parameter from the handler
  698. * @return {Event} the event
  699. */
  700. getEvent: function(e) {
  701. var ev = e || window.event;
  702. if (!ev) {
  703. var c = this.getEvent.caller;
  704. while (c) {
  705. ev = c.arguments[0];
  706. if (ev && Event == ev.constructor) {
  707. break;
  708. }
  709. c = c.caller;
  710. }
  711. }
  712. return ev;
  713. },
  714. /**
  715. * Returns the charcode for an event
  716. * @param {Event} ev the event
  717. * @return {int} the event's charCode
  718. */
  719. getCharCode: function(ev) {
  720. return ev.charCode || ((ev.type == "keypress") ? ev.keyCode : 0);
  721. },
  722. /**
  723. * @private
  724. * Locating the saved event handler data by function ref
  725. */
  726. _getCacheIndex: function(el, sType, fn) {
  727. for (var i=0,len=listeners.length; i<len; ++i) {
  728. var li = listeners[i];
  729. if ( li &&
  730. li[this.FN] == fn &&
  731. li[this.EL] == el &&
  732. li[this.TYPE] == sType ) {
  733. return i;
  734. }
  735. }
  736. return -1;
  737. },
  738. /**
  739. * Generates an unique ID for the element if it does not already
  740. * have one.
  741. * @param el the element
  742. * @return {string} the id of the element
  743. */
  744. generateId: function(el) {
  745. var id = el.id;
  746. if (!id) {
  747. id = "yuievtautoid-" + (counter++);
  748. el.id = id;
  749. }
  750. return id;
  751. },
  752. /**
  753. * We want to be able to use getElementsByTagName as a collection
  754. * to attach a group of events to. Unfortunately, different
  755. * browsers return different types of collections. This function
  756. * tests to determine if the object is array-like. It will also
  757. * fail if the object is an array, but is empty.
  758. * @param o the object to test
  759. * @return {boolean} true if the object is array-like and populated
  760. * @private
  761. */
  762. _isValidCollection: function(o) {
  763. return ( o && // o is something
  764. o.length && // o is indexed
  765. typeof o != "string" && // o is not a string
  766. !o.tagName && // o is not an HTML element
  767. !o.alert && // o is not a window
  768. typeof o[0] != "undefined" );
  769. },
  770. /**
  771. * @private
  772. * DOM element cache
  773. */
  774. elCache: {},
  775. /**
  776. * We cache elements bound by id because when the unload event
  777. * fires, we can no longer use document.getElementById
  778. * @private
  779. */
  780. getEl: function(id) {
  781. return document.getElementById(id);
  782. },
  783. /**
  784. * Clears the element cache
  785. * @deprecated
  786. * @private
  787. */
  788. clearCache: function() { },
  789. /**
  790. * Called by CustomEvent instances to provide a handle to the
  791. * event * that can be removed later on. Should be package
  792. * protected.
  793. * @private
  794. */
  795. regCE: function(ce) {
  796. customEvents.push(ce);
  797. },
  798. /**
  799. * @private
  800. * hook up any deferred listeners
  801. */
  802. _load: function(e) {
  803. loadComplete = true;
  804. },
  805. /**
  806. * Polling function that runs before the onload event fires,
  807. * attempting * to attach to DOM Nodes as soon as they are
  808. * available
  809. * @private
  810. */
  811. _tryPreloadAttach: function() {
  812. if (this.locked) {
  813. return false;
  814. }
  815. this.locked = true;
  816. // keep trying until after the page is loaded. We need to
  817. // check the page load state prior to trying to bind the
  818. // elements so that we can be certain all elements have been
  819. // tested appropriately
  820. var tryAgain = !loadComplete;
  821. if (!tryAgain) {
  822. tryAgain = (retryCount > 0);
  823. }
  824. // Delayed listeners
  825. var stillDelayed = [];
  826. for (var i=0,len=delayedListeners.length; i<len; ++i) {
  827. var d = delayedListeners[i];
  828. // There may be a race condition here, so we need to
  829. // verify the array element is usable.
  830. if (d) {
  831. // el will be null if document.getElementById did not
  832. // work
  833. var el = this.getEl(d[this.EL]);
  834. if (el) {
  835. this.on(el, d[this.TYPE], d[this.FN],
  836. d[this.SCOPE], d[this.ADJ_SCOPE]);
  837. delete delayedListeners[i];
  838. } else {
  839. stillDelayed.push(d);
  840. }
  841. }
  842. }
  843. delayedListeners = stillDelayed;
  844. // onAvailable
  845. notAvail = [];
  846. for (i=0,len=onAvailStack.length; i<len ; ++i) {
  847. var item = onAvailStack[i];
  848. if (item) {
  849. el = this.getEl(item.id);
  850. if (el) {
  851. var scope = (item.override) ? item.obj : el;
  852. item.fn.call(scope, item.obj);
  853. delete onAvailStack[i];
  854. } else {
  855. notAvail.push(item);
  856. }
  857. }
  858. }
  859. retryCount = (stillDelayed.length === 0 &&
  860. notAvail.length === 0) ? 0 : retryCount - 1;
  861. if (tryAgain) {
  862. this.startTimeout();
  863. }
  864. this.locked = false;
  865. },
  866. /**
  867. * Removes all listeners registered by pe.event. Called
  868. * automatically during the unload event.
  869. * @private
  870. */
  871. _unload: function(e, me) {
  872. for (var i=0,len=unloadListeners.length; i<len; ++i) {
  873. var l = unloadListeners[i];
  874. if (l) {
  875. var scope = (l[this.ADJ_SCOPE]) ? l[this.SCOPE]: window;
  876. l[this.FN].call(scope, this.getEvent(e), l[this.SCOPE] );
  877. }
  878. }
  879. if (listeners && listeners.length > 0) {
  880. for (i=0,len=listeners.length; i<len ; ++i) {
  881. l = listeners[i];
  882. if (l) {
  883. this.removeListener(l[this.EL], l[this.TYPE],
  884. l[this.FN], i);
  885. }
  886. }
  887. this.clearCache();
  888. }
  889. for (i=0,len=customEvents.length; i<len; ++i) {
  890. customEvents[i].unsubscribeAll();
  891. delete customEvents[i];
  892. }
  893. for (i=0,len=legacyEvents.length; i<len; ++i) {
  894. // dereference the element
  895. delete legacyEvents[i][0];
  896. // delete the array item
  897. delete legacyEvents[i];
  898. }
  899. },
  900. /**
  901. * Returns scrollLeft
  902. * @private
  903. */
  904. _getScrollLeft: function() {
  905. return this._getScroll()[1];
  906. },
  907. /**
  908. * Returns scrollTop
  909. * @private
  910. */
  911. _getScrollTop: function() {
  912. return this._getScroll()[0];
  913. },
  914. /**
  915. * Returns the scrollTop and scrollLeft. Used to calculate the
  916. * pageX and pageY in Internet Explorer
  917. * @private
  918. */
  919. _getScroll: function() {
  920. var dd = document.documentElement; db = document.body;
  921. if (dd && dd.scrollTop) {
  922. return [dd.scrollTop, dd.scrollLeft];
  923. } else if (db) {
  924. return [db.scrollTop, db.scrollLeft];
  925. } else {
  926. return [0, 0];
  927. }
  928. }
  929. };
  930. } ();
  931. /**
  932. * @private
  933. */
  934. YAHOO.util.Event.on = YAHOO.util.Event.addListener;
  935. if (document && document.body) {
  936. YAHOO.util.Event._load();
  937. } else {
  938. YAHOO.util.Event.on(window, "load", YAHOO.util.Event._load,
  939. YAHOO.util.Event, true);
  940. }
  941. YAHOO.util.Event.on(window, "unload", YAHOO.util.Event._unload,
  942. YAHOO.util.Event, true);
  943. YAHOO.util.Event._tryPreloadAttach();
  944. }