Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

event-base.js 40KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362
  1. /*
  2. Copyright (c) 2010, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.com/yui/license.html
  5. version: 3.4.0
  6. build: nightly
  7. */
  8. (function () {
  9. var GLOBAL_ENV = YUI.Env;
  10. if (!GLOBAL_ENV._ready) {
  11. GLOBAL_ENV._ready = function() {
  12. GLOBAL_ENV.DOMReady = true;
  13. GLOBAL_ENV.remove(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready);
  14. };
  15. GLOBAL_ENV.add(YUI.config.doc, 'DOMContentLoaded', GLOBAL_ENV._ready);
  16. }
  17. })();
  18. YUI.add('event-base', function(Y) {
  19. /*
  20. * DOM event listener abstraction layer
  21. * @module event
  22. * @submodule event-base
  23. */
  24. /**
  25. * The domready event fires at the moment the browser's DOM is
  26. * usable. In most cases, this is before images are fully
  27. * downloaded, allowing you to provide a more responsive user
  28. * interface.
  29. *
  30. * In YUI 3, domready subscribers will be notified immediately if
  31. * that moment has already passed when the subscription is created.
  32. *
  33. * One exception is if the yui.js file is dynamically injected into
  34. * the page. If this is done, you must tell the YUI instance that
  35. * you did this in order for DOMReady (and window load events) to
  36. * fire normally. That configuration option is 'injected' -- set
  37. * it to true if the yui.js script is not included inline.
  38. *
  39. * This method is part of the 'event-ready' module, which is a
  40. * submodule of 'event'.
  41. *
  42. * @event domready
  43. * @for YUI
  44. */
  45. Y.publish('domready', {
  46. fireOnce: true,
  47. async: true
  48. });
  49. if (YUI.Env.DOMReady) {
  50. Y.fire('domready');
  51. } else {
  52. Y.Do.before(function() { Y.fire('domready'); }, YUI.Env, '_ready');
  53. }
  54. /**
  55. * Custom event engine, DOM event listener abstraction layer, synthetic DOM
  56. * events.
  57. * @module event
  58. * @submodule event-base
  59. */
  60. /**
  61. * Wraps a DOM event, properties requiring browser abstraction are
  62. * fixed here. Provids a security layer when required.
  63. * @class DOMEventFacade
  64. * @param ev {Event} the DOM event
  65. * @param currentTarget {HTMLElement} the element the listener was attached to
  66. * @param wrapper {Event.Custom} the custom event wrapper for this DOM event
  67. */
  68. var ua = Y.UA,
  69. EMPTY = {},
  70. /**
  71. * webkit key remapping required for Safari < 3.1
  72. * @property webkitKeymap
  73. * @private
  74. */
  75. webkitKeymap = {
  76. 63232: 38, // up
  77. 63233: 40, // down
  78. 63234: 37, // left
  79. 63235: 39, // right
  80. 63276: 33, // page up
  81. 63277: 34, // page down
  82. 25: 9, // SHIFT-TAB (Safari provides a different key code in
  83. // this case, even though the shiftKey modifier is set)
  84. 63272: 46, // delete
  85. 63273: 36, // home
  86. 63275: 35 // end
  87. },
  88. /**
  89. * Returns a wrapped node. Intended to be used on event targets,
  90. * so it will return the node's parent if the target is a text
  91. * node.
  92. *
  93. * If accessing a property of the node throws an error, this is
  94. * probably the anonymous div wrapper Gecko adds inside text
  95. * nodes. This likely will only occur when attempting to access
  96. * the relatedTarget. In this case, we now return null because
  97. * the anonymous div is completely useless and we do not know
  98. * what the related target was because we can't even get to
  99. * the element's parent node.
  100. *
  101. * @method resolve
  102. * @private
  103. */
  104. resolve = function(n) {
  105. if (!n) {
  106. return n;
  107. }
  108. try {
  109. if (n && 3 == n.nodeType) {
  110. n = n.parentNode;
  111. }
  112. } catch(e) {
  113. return null;
  114. }
  115. return Y.one(n);
  116. },
  117. DOMEventFacade = function(ev, currentTarget, wrapper) {
  118. this._event = ev;
  119. this._currentTarget = currentTarget;
  120. this._wrapper = wrapper || EMPTY;
  121. // if not lazy init
  122. this.init();
  123. };
  124. Y.extend(DOMEventFacade, Object, {
  125. init: function() {
  126. var e = this._event,
  127. overrides = this._wrapper.overrides,
  128. x = e.pageX,
  129. y = e.pageY,
  130. c,
  131. currentTarget = this._currentTarget;
  132. this.altKey = e.altKey;
  133. this.ctrlKey = e.ctrlKey;
  134. this.metaKey = e.metaKey;
  135. this.shiftKey = e.shiftKey;
  136. this.type = (overrides && overrides.type) || e.type;
  137. this.clientX = e.clientX;
  138. this.clientY = e.clientY;
  139. this.pageX = x;
  140. this.pageY = y;
  141. c = e.keyCode || e.charCode;
  142. if (ua.webkit && (c in webkitKeymap)) {
  143. c = webkitKeymap[c];
  144. }
  145. this.keyCode = c;
  146. this.charCode = c;
  147. this.which = e.which || e.charCode || c;
  148. // this.button = e.button;
  149. this.button = this.which;
  150. this.target = resolve(e.target);
  151. this.currentTarget = resolve(currentTarget);
  152. this.relatedTarget = resolve(e.relatedTarget);
  153. if (e.type == "mousewheel" || e.type == "DOMMouseScroll") {
  154. this.wheelDelta = (e.detail) ? (e.detail * -1) : Math.round(e.wheelDelta / 80) || ((e.wheelDelta < 0) ? -1 : 1);
  155. }
  156. if (this._touch) {
  157. this._touch(e, currentTarget, this._wrapper);
  158. }
  159. },
  160. stopPropagation: function() {
  161. this._event.stopPropagation();
  162. this._wrapper.stopped = 1;
  163. this.stopped = 1;
  164. },
  165. stopImmediatePropagation: function() {
  166. var e = this._event;
  167. if (e.stopImmediatePropagation) {
  168. e.stopImmediatePropagation();
  169. } else {
  170. this.stopPropagation();
  171. }
  172. this._wrapper.stopped = 2;
  173. this.stopped = 2;
  174. },
  175. preventDefault: function(returnValue) {
  176. var e = this._event;
  177. e.preventDefault();
  178. e.returnValue = returnValue || false;
  179. this._wrapper.prevented = 1;
  180. this.prevented = 1;
  181. },
  182. halt: function(immediate) {
  183. if (immediate) {
  184. this.stopImmediatePropagation();
  185. } else {
  186. this.stopPropagation();
  187. }
  188. this.preventDefault();
  189. }
  190. });
  191. DOMEventFacade.resolve = resolve;
  192. Y.DOM2EventFacade = DOMEventFacade;
  193. Y.DOMEventFacade = DOMEventFacade;
  194. /**
  195. * The native event
  196. * @property _event
  197. * @type {Native DOM Event}
  198. * @private
  199. */
  200. /**
  201. The name of the event (e.g. "click")
  202. @property type
  203. @type {String}
  204. **/
  205. /**
  206. `true` if the "alt" or "option" key is pressed.
  207. @property altKey
  208. @type {Boolean}
  209. **/
  210. /**
  211. `true` if the shift key is pressed.
  212. @property shiftKey
  213. @type {Boolean}
  214. **/
  215. /**
  216. `true` if the "Windows" key on a Windows keyboard, "command" key on an
  217. Apple keyboard, or "meta" key on other keyboards is pressed.
  218. @property metaKey
  219. @type {Boolean}
  220. **/
  221. /**
  222. `true` if the "Ctrl" or "control" key is pressed.
  223. @property ctrlKey
  224. @type {Boolean}
  225. **/
  226. /**
  227. * The X location of the event on the page (including scroll)
  228. * @property pageX
  229. * @type {Number}
  230. */
  231. /**
  232. * The Y location of the event on the page (including scroll)
  233. * @property pageY
  234. * @type {Number}
  235. */
  236. /**
  237. * The X location of the event in the viewport
  238. * @property clientX
  239. * @type {Number}
  240. */
  241. /**
  242. * The Y location of the event in the viewport
  243. * @property clientY
  244. * @type {Number}
  245. */
  246. /**
  247. * The keyCode for key events. Uses charCode if keyCode is not available
  248. * @property keyCode
  249. * @type {Number}
  250. */
  251. /**
  252. * The charCode for key events. Same as keyCode
  253. * @property charCode
  254. * @type {Number}
  255. */
  256. /**
  257. * The button that was pushed. 1 for left click, 2 for middle click, 3 for
  258. * right click. This is only reliably populated on `mouseup` events.
  259. * @property button
  260. * @type {Number}
  261. */
  262. /**
  263. * The button that was pushed. Same as button.
  264. * @property which
  265. * @type {Number}
  266. */
  267. /**
  268. * Node reference for the targeted element
  269. * @property target
  270. * @type {Node}
  271. */
  272. /**
  273. * Node reference for the element that the listener was attached to.
  274. * @property currentTarget
  275. * @type {Node}
  276. */
  277. /**
  278. * Node reference to the relatedTarget
  279. * @property relatedTarget
  280. * @type {Node}
  281. */
  282. /**
  283. * Number representing the direction and velocity of the movement of the mousewheel.
  284. * Negative is down, the higher the number, the faster. Applies to the mousewheel event.
  285. * @property wheelDelta
  286. * @type {Number}
  287. */
  288. /**
  289. * Stops the propagation to the next bubble target
  290. * @method stopPropagation
  291. */
  292. /**
  293. * Stops the propagation to the next bubble target and
  294. * prevents any additional listeners from being exectued
  295. * on the current target.
  296. * @method stopImmediatePropagation
  297. */
  298. /**
  299. * Prevents the event's default behavior
  300. * @method preventDefault
  301. * @param returnValue {string} sets the returnValue of the event to this value
  302. * (rather than the default false value). This can be used to add a customized
  303. * confirmation query to the beforeunload event).
  304. */
  305. /**
  306. * Stops the event propagation and prevents the default
  307. * event behavior.
  308. * @method halt
  309. * @param immediate {boolean} if true additional listeners
  310. * on the current target will not be executed
  311. */
  312. (function() {
  313. /**
  314. * The event utility provides functions to add and remove event listeners,
  315. * event cleansing. It also tries to automatically remove listeners it
  316. * registers during the unload event.
  317. * @module event
  318. * @main event
  319. * @submodule event-base
  320. */
  321. /**
  322. * The event utility provides functions to add and remove event listeners,
  323. * event cleansing. It also tries to automatically remove listeners it
  324. * registers during the unload event.
  325. *
  326. * @class Event
  327. * @static
  328. */
  329. Y.Env.evt.dom_wrappers = {};
  330. Y.Env.evt.dom_map = {};
  331. var _eventenv = Y.Env.evt,
  332. config = Y.config,
  333. win = config.win,
  334. add = YUI.Env.add,
  335. remove = YUI.Env.remove,
  336. onLoad = function() {
  337. YUI.Env.windowLoaded = true;
  338. Y.Event._load();
  339. remove(win, "load", onLoad);
  340. },
  341. onUnload = function() {
  342. Y.Event._unload();
  343. },
  344. EVENT_READY = 'domready',
  345. COMPAT_ARG = '~yui|2|compat~',
  346. shouldIterate = function(o) {
  347. try {
  348. return (o && typeof o !== "string" && Y.Lang.isNumber(o.length) &&
  349. !o.tagName && !o.alert);
  350. } catch(ex) {
  351. return false;
  352. }
  353. },
  354. // aliases to support DOM event subscription clean up when the last
  355. // subscriber is detached. deleteAndClean overrides the DOM event's wrapper
  356. // CustomEvent _delete method.
  357. _ceProtoDelete = Y.CustomEvent.prototype._delete,
  358. _deleteAndClean = function(s) {
  359. var ret = _ceProtoDelete.apply(this, arguments);
  360. if (!this.hasSubs()) {
  361. Y.Event._clean(this);
  362. }
  363. return ret;
  364. },
  365. Event = function() {
  366. /**
  367. * True after the onload event has fired
  368. * @property _loadComplete
  369. * @type boolean
  370. * @static
  371. * @private
  372. */
  373. var _loadComplete = false,
  374. /**
  375. * The number of times to poll after window.onload. This number is
  376. * increased if additional late-bound handlers are requested after
  377. * the page load.
  378. * @property _retryCount
  379. * @static
  380. * @private
  381. */
  382. _retryCount = 0,
  383. /**
  384. * onAvailable listeners
  385. * @property _avail
  386. * @static
  387. * @private
  388. */
  389. _avail = [],
  390. /**
  391. * Custom event wrappers for DOM events. Key is
  392. * 'event:' + Element uid stamp + event type
  393. * @property _wrappers
  394. * @type Y.Event.Custom
  395. * @static
  396. * @private
  397. */
  398. _wrappers = _eventenv.dom_wrappers,
  399. _windowLoadKey = null,
  400. /**
  401. * Custom event wrapper map DOM events. Key is
  402. * Element uid stamp. Each item is a hash of custom event
  403. * wrappers as provided in the _wrappers collection. This
  404. * provides the infrastructure for getListeners.
  405. * @property _el_events
  406. * @static
  407. * @private
  408. */
  409. _el_events = _eventenv.dom_map;
  410. return {
  411. /**
  412. * The number of times we should look for elements that are not
  413. * in the DOM at the time the event is requested after the document
  414. * has been loaded. The default is 1000@amp;40 ms, so it will poll
  415. * for 40 seconds or until all outstanding handlers are bound
  416. * (whichever comes first).
  417. * @property POLL_RETRYS
  418. * @type int
  419. * @static
  420. * @final
  421. */
  422. POLL_RETRYS: 1000,
  423. /**
  424. * The poll interval in milliseconds
  425. * @property POLL_INTERVAL
  426. * @type int
  427. * @static
  428. * @final
  429. */
  430. POLL_INTERVAL: 40,
  431. /**
  432. * addListener/removeListener can throw errors in unexpected scenarios.
  433. * These errors are suppressed, the method returns false, and this property
  434. * is set
  435. * @property lastError
  436. * @static
  437. * @type Error
  438. */
  439. lastError: null,
  440. /**
  441. * poll handle
  442. * @property _interval
  443. * @static
  444. * @private
  445. */
  446. _interval: null,
  447. /**
  448. * document readystate poll handle
  449. * @property _dri
  450. * @static
  451. * @private
  452. */
  453. _dri: null,
  454. /**
  455. * True when the document is initially usable
  456. * @property DOMReady
  457. * @type boolean
  458. * @static
  459. */
  460. DOMReady: false,
  461. /**
  462. * @method startInterval
  463. * @static
  464. * @private
  465. */
  466. startInterval: function() {
  467. if (!Event._interval) {
  468. Event._interval = setInterval(Event._poll, Event.POLL_INTERVAL);
  469. }
  470. },
  471. /**
  472. * Executes the supplied callback when the item with the supplied
  473. * id is found. This is meant to be used to execute behavior as
  474. * soon as possible as the page loads. If you use this after the
  475. * initial page load it will poll for a fixed time for the element.
  476. * The number of times it will poll and the frequency are
  477. * configurable. By default it will poll for 10 seconds.
  478. *
  479. * <p>The callback is executed with a single parameter:
  480. * the custom object parameter, if provided.</p>
  481. *
  482. * @method onAvailable
  483. *
  484. * @param {string||string[]} id the id of the element, or an array
  485. * of ids to look for.
  486. * @param {function} fn what to execute when the element is found.
  487. * @param {object} p_obj an optional object to be passed back as
  488. * a parameter to fn.
  489. * @param {boolean|object} p_override If set to true, fn will execute
  490. * in the context of p_obj, if set to an object it
  491. * will execute in the context of that object
  492. * @param checkContent {boolean} check child node readiness (onContentReady)
  493. * @static
  494. * @deprecated Use Y.on("available")
  495. */
  496. // @TODO fix arguments
  497. onAvailable: function(id, fn, p_obj, p_override, checkContent, compat) {
  498. var a = Y.Array(id), i, availHandle;
  499. for (i=0; i<a.length; i=i+1) {
  500. _avail.push({
  501. id: a[i],
  502. fn: fn,
  503. obj: p_obj,
  504. override: p_override,
  505. checkReady: checkContent,
  506. compat: compat
  507. });
  508. }
  509. _retryCount = this.POLL_RETRYS;
  510. // We want the first test to be immediate, but async
  511. setTimeout(Event._poll, 0);
  512. availHandle = new Y.EventHandle({
  513. _delete: function() {
  514. // set by the event system for lazy DOM listeners
  515. if (availHandle.handle) {
  516. availHandle.handle.detach();
  517. return;
  518. }
  519. var i, j;
  520. // otherwise try to remove the onAvailable listener(s)
  521. for (i = 0; i < a.length; i++) {
  522. for (j = 0; j < _avail.length; j++) {
  523. if (a[i] === _avail[j].id) {
  524. _avail.splice(j, 1);
  525. }
  526. }
  527. }
  528. }
  529. });
  530. return availHandle;
  531. },
  532. /**
  533. * Works the same way as onAvailable, but additionally checks the
  534. * state of sibling elements to determine if the content of the
  535. * available element is safe to modify.
  536. *
  537. * <p>The callback is executed with a single parameter:
  538. * the custom object parameter, if provided.</p>
  539. *
  540. * @method onContentReady
  541. *
  542. * @param {string} id the id of the element to look for.
  543. * @param {function} fn what to execute when the element is ready.
  544. * @param {object} obj an optional object to be passed back as
  545. * a parameter to fn.
  546. * @param {boolean|object} override If set to true, fn will execute
  547. * in the context of p_obj. If an object, fn will
  548. * exectute in the context of that object
  549. *
  550. * @static
  551. * @deprecated Use Y.on("contentready")
  552. */
  553. // @TODO fix arguments
  554. onContentReady: function(id, fn, obj, override, compat) {
  555. return Event.onAvailable(id, fn, obj, override, true, compat);
  556. },
  557. /**
  558. * Adds an event listener
  559. *
  560. * @method attach
  561. *
  562. * @param {String} type The type of event to append
  563. * @param {Function} fn The method the event invokes
  564. * @param {String|HTMLElement|Array|NodeList} el An id, an element
  565. * reference, or a collection of ids and/or elements to assign the
  566. * listener to.
  567. * @param {Object} context optional context object
  568. * @param {Boolean|object} args 0..n arguments to pass to the callback
  569. * @return {EventHandle} an object to that can be used to detach the listener
  570. *
  571. * @static
  572. */
  573. attach: function(type, fn, el, context) {
  574. return Event._attach(Y.Array(arguments, 0, true));
  575. },
  576. _createWrapper: function (el, type, capture, compat, facade) {
  577. var cewrapper,
  578. ek = Y.stamp(el),
  579. key = 'event:' + ek + type;
  580. if (false === facade) {
  581. key += 'native';
  582. }
  583. if (capture) {
  584. key += 'capture';
  585. }
  586. cewrapper = _wrappers[key];
  587. if (!cewrapper) {
  588. // create CE wrapper
  589. cewrapper = Y.publish(key, {
  590. silent: true,
  591. bubbles: false,
  592. contextFn: function() {
  593. if (compat) {
  594. return cewrapper.el;
  595. } else {
  596. cewrapper.nodeRef = cewrapper.nodeRef || Y.one(cewrapper.el);
  597. return cewrapper.nodeRef;
  598. }
  599. }
  600. });
  601. cewrapper.overrides = {};
  602. // for later removeListener calls
  603. cewrapper.el = el;
  604. cewrapper.key = key;
  605. cewrapper.domkey = ek;
  606. cewrapper.type = type;
  607. cewrapper.fn = function(e) {
  608. cewrapper.fire(Event.getEvent(e, el, (compat || (false === facade))));
  609. };
  610. cewrapper.capture = capture;
  611. if (el == win && type == "load") {
  612. // window load happens once
  613. cewrapper.fireOnce = true;
  614. _windowLoadKey = key;
  615. }
  616. cewrapper._delete = _deleteAndClean;
  617. _wrappers[key] = cewrapper;
  618. _el_events[ek] = _el_events[ek] || {};
  619. _el_events[ek][key] = cewrapper;
  620. add(el, type, cewrapper.fn, capture);
  621. }
  622. return cewrapper;
  623. },
  624. _attach: function(args, conf) {
  625. var compat,
  626. handles, oEl, cewrapper, context,
  627. fireNow = false, ret,
  628. type = args[0],
  629. fn = args[1],
  630. el = args[2] || win,
  631. facade = conf && conf.facade,
  632. capture = conf && conf.capture,
  633. overrides = conf && conf.overrides;
  634. if (args[args.length-1] === COMPAT_ARG) {
  635. compat = true;
  636. }
  637. if (!fn || !fn.call) {
  638. // throw new TypeError(type + " attach call failed, callback undefined");
  639. return false;
  640. }
  641. // The el argument can be an array of elements or element ids.
  642. if (shouldIterate(el)) {
  643. handles=[];
  644. Y.each(el, function(v, k) {
  645. args[2] = v;
  646. handles.push(Event._attach(args.slice(), conf));
  647. });
  648. // return (handles.length === 1) ? handles[0] : handles;
  649. return new Y.EventHandle(handles);
  650. // If the el argument is a string, we assume it is
  651. // actually the id of the element. If the page is loaded
  652. // we convert el to the actual element, otherwise we
  653. // defer attaching the event until the element is
  654. // ready
  655. } else if (Y.Lang.isString(el)) {
  656. // oEl = (compat) ? Y.DOM.byId(el) : Y.Selector.query(el);
  657. if (compat) {
  658. oEl = Y.DOM.byId(el);
  659. } else {
  660. oEl = Y.Selector.query(el);
  661. switch (oEl.length) {
  662. case 0:
  663. oEl = null;
  664. break;
  665. case 1:
  666. oEl = oEl[0];
  667. break;
  668. default:
  669. args[2] = oEl;
  670. return Event._attach(args, conf);
  671. }
  672. }
  673. if (oEl) {
  674. el = oEl;
  675. // Not found = defer adding the event until the element is available
  676. } else {
  677. ret = Event.onAvailable(el, function() {
  678. ret.handle = Event._attach(args, conf);
  679. }, Event, true, false, compat);
  680. return ret;
  681. }
  682. }
  683. // Element should be an html element or node
  684. if (!el) {
  685. return false;
  686. }
  687. if (Y.Node && Y.instanceOf(el, Y.Node)) {
  688. el = Y.Node.getDOMNode(el);
  689. }
  690. cewrapper = Event._createWrapper(el, type, capture, compat, facade);
  691. if (overrides) {
  692. Y.mix(cewrapper.overrides, overrides);
  693. }
  694. if (el == win && type == "load") {
  695. // if the load is complete, fire immediately.
  696. // all subscribers, including the current one
  697. // will be notified.
  698. if (YUI.Env.windowLoaded) {
  699. fireNow = true;
  700. }
  701. }
  702. if (compat) {
  703. args.pop();
  704. }
  705. context = args[3];
  706. // set context to the Node if not specified
  707. // ret = cewrapper.on.apply(cewrapper, trimmedArgs);
  708. ret = cewrapper._on(fn, context, (args.length > 4) ? args.slice(4) : null);
  709. if (fireNow) {
  710. cewrapper.fire();
  711. }
  712. return ret;
  713. },
  714. /**
  715. * Removes an event listener. Supports the signature the event was bound
  716. * with, but the preferred way to remove listeners is using the handle
  717. * that is returned when using Y.on
  718. *
  719. * @method detach
  720. *
  721. * @param {String} type the type of event to remove.
  722. * @param {Function} fn the method the event invokes. If fn is
  723. * undefined, then all event handlers for the type of event are
  724. * removed.
  725. * @param {String|HTMLElement|Array|NodeList|EventHandle} el An
  726. * event handle, an id, an element reference, or a collection
  727. * of ids and/or elements to remove the listener from.
  728. * @return {boolean} true if the unbind was successful, false otherwise.
  729. * @static
  730. */
  731. detach: function(type, fn, el, obj) {
  732. var args=Y.Array(arguments, 0, true), compat, l, ok, i,
  733. id, ce;
  734. if (args[args.length-1] === COMPAT_ARG) {
  735. compat = true;
  736. // args.pop();
  737. }
  738. if (type && type.detach) {
  739. return type.detach();
  740. }
  741. // The el argument can be a string
  742. if (typeof el == "string") {
  743. // el = (compat) ? Y.DOM.byId(el) : Y.all(el);
  744. if (compat) {
  745. el = Y.DOM.byId(el);
  746. } else {
  747. el = Y.Selector.query(el);
  748. l = el.length;
  749. if (l < 1) {
  750. el = null;
  751. } else if (l == 1) {
  752. el = el[0];
  753. }
  754. }
  755. // return Event.detach.apply(Event, args);
  756. }
  757. if (!el) {
  758. return false;
  759. }
  760. if (el.detach) {
  761. args.splice(2, 1);
  762. return el.detach.apply(el, args);
  763. // The el argument can be an array of elements or element ids.
  764. } else if (shouldIterate(el)) {
  765. ok = true;
  766. for (i=0, l=el.length; i<l; ++i) {
  767. args[2] = el[i];
  768. ok = ( Y.Event.detach.apply(Y.Event, args) && ok );
  769. }
  770. return ok;
  771. }
  772. if (!type || !fn || !fn.call) {
  773. return Event.purgeElement(el, false, type);
  774. }
  775. id = 'event:' + Y.stamp(el) + type;
  776. ce = _wrappers[id];
  777. if (ce) {
  778. return ce.detach(fn);
  779. } else {
  780. return false;
  781. }
  782. },
  783. /**
  784. * Finds the event in the window object, the caller's arguments, or
  785. * in the arguments of another method in the callstack. This is
  786. * executed automatically for events registered through the event
  787. * manager, so the implementer should not normally need to execute
  788. * this function at all.
  789. * @method getEvent
  790. * @param {Event} e the event parameter from the handler
  791. * @param {HTMLElement} el the element the listener was attached to
  792. * @return {Event} the event
  793. * @static
  794. */
  795. getEvent: function(e, el, noFacade) {
  796. var ev = e || win.event;
  797. return (noFacade) ? ev :
  798. new Y.DOMEventFacade(ev, el, _wrappers['event:' + Y.stamp(el) + e.type]);
  799. },
  800. /**
  801. * Generates an unique ID for the element if it does not already
  802. * have one.
  803. * @method generateId
  804. * @param el the element to create the id for
  805. * @return {string} the resulting id of the element
  806. * @static
  807. */
  808. generateId: function(el) {
  809. return Y.DOM.generateID(el);
  810. },
  811. /**
  812. * We want to be able to use getElementsByTagName as a collection
  813. * to attach a group of events to. Unfortunately, different
  814. * browsers return different types of collections. This function
  815. * tests to determine if the object is array-like. It will also
  816. * fail if the object is an array, but is empty.
  817. * @method _isValidCollection
  818. * @param o the object to test
  819. * @return {boolean} true if the object is array-like and populated
  820. * @deprecated was not meant to be used directly
  821. * @static
  822. * @private
  823. */
  824. _isValidCollection: shouldIterate,
  825. /**
  826. * hook up any deferred listeners
  827. * @method _load
  828. * @static
  829. * @private
  830. */
  831. _load: function(e) {
  832. if (!_loadComplete) {
  833. _loadComplete = true;
  834. // Just in case DOMReady did not go off for some reason
  835. // E._ready();
  836. if (Y.fire) {
  837. Y.fire(EVENT_READY);
  838. }
  839. // Available elements may not have been detected before the
  840. // window load event fires. Try to find them now so that the
  841. // the user is more likely to get the onAvailable notifications
  842. // before the window load notification
  843. Event._poll();
  844. }
  845. },
  846. /**
  847. * Polling function that runs before the onload event fires,
  848. * attempting to attach To DoM Nodes as soon as they are
  849. * available
  850. * @method _poll
  851. * @static
  852. * @private
  853. */
  854. _poll: function() {
  855. if (Event.locked) {
  856. return;
  857. }
  858. if (Y.UA.ie && !YUI.Env.DOMReady) {
  859. // Hold off if DOMReady has not fired and check current
  860. // readyState to protect against the IE operation aborted
  861. // issue.
  862. Event.startInterval();
  863. return;
  864. }
  865. Event.locked = true;
  866. // keep trying until after the page is loaded. We need to
  867. // check the page load state prior to trying to bind the
  868. // elements so that we can be certain all elements have been
  869. // tested appropriately
  870. var i, len, item, el, notAvail, executeItem,
  871. tryAgain = !_loadComplete;
  872. if (!tryAgain) {
  873. tryAgain = (_retryCount > 0);
  874. }
  875. // onAvailable
  876. notAvail = [];
  877. executeItem = function (el, item) {
  878. var context, ov = item.override;
  879. if (item.compat) {
  880. if (item.override) {
  881. if (ov === true) {
  882. context = item.obj;
  883. } else {
  884. context = ov;
  885. }
  886. } else {
  887. context = el;
  888. }
  889. item.fn.call(context, item.obj);
  890. } else {
  891. context = item.obj || Y.one(el);
  892. item.fn.apply(context, (Y.Lang.isArray(ov)) ? ov : []);
  893. }
  894. };
  895. // onAvailable
  896. for (i=0,len=_avail.length; i<len; ++i) {
  897. item = _avail[i];
  898. if (item && !item.checkReady) {
  899. // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
  900. el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
  901. if (el) {
  902. executeItem(el, item);
  903. _avail[i] = null;
  904. } else {
  905. notAvail.push(item);
  906. }
  907. }
  908. }
  909. // onContentReady
  910. for (i=0,len=_avail.length; i<len; ++i) {
  911. item = _avail[i];
  912. if (item && item.checkReady) {
  913. // el = (item.compat) ? Y.DOM.byId(item.id) : Y.one(item.id);
  914. el = (item.compat) ? Y.DOM.byId(item.id) : Y.Selector.query(item.id, null, true);
  915. if (el) {
  916. // The element is available, but not necessarily ready
  917. // @todo should we test parentNode.nextSibling?
  918. if (_loadComplete || (el.get && el.get('nextSibling')) || el.nextSibling) {
  919. executeItem(el, item);
  920. _avail[i] = null;
  921. }
  922. } else {
  923. notAvail.push(item);
  924. }
  925. }
  926. }
  927. _retryCount = (notAvail.length === 0) ? 0 : _retryCount - 1;
  928. if (tryAgain) {
  929. // we may need to strip the nulled out items here
  930. Event.startInterval();
  931. } else {
  932. clearInterval(Event._interval);
  933. Event._interval = null;
  934. }
  935. Event.locked = false;
  936. return;
  937. },
  938. /**
  939. * Removes all listeners attached to the given element via addListener.
  940. * Optionally, the node's children can also be purged.
  941. * Optionally, you can specify a specific type of event to remove.
  942. * @method purgeElement
  943. * @param {HTMLElement} el the element to purge
  944. * @param {boolean} recurse recursively purge this element's children
  945. * as well. Use with caution.
  946. * @param {string} type optional type of listener to purge. If
  947. * left out, all listeners will be removed
  948. * @static
  949. */
  950. purgeElement: function(el, recurse, type) {
  951. // var oEl = (Y.Lang.isString(el)) ? Y.one(el) : el,
  952. var oEl = (Y.Lang.isString(el)) ? Y.Selector.query(el, null, true) : el,
  953. lis = Event.getListeners(oEl, type), i, len, children, child;
  954. if (recurse && oEl) {
  955. lis = lis || [];
  956. children = Y.Selector.query('*', oEl);
  957. i = 0;
  958. len = children.length;
  959. for (; i < len; ++i) {
  960. child = Event.getListeners(children[i], type);
  961. if (child) {
  962. lis = lis.concat(child);
  963. }
  964. }
  965. }
  966. if (lis) {
  967. for (i = 0, len = lis.length; i < len; ++i) {
  968. lis[i].detachAll();
  969. }
  970. }
  971. },
  972. /**
  973. * Removes all object references and the DOM proxy subscription for
  974. * a given event for a DOM node.
  975. *
  976. * @method _clean
  977. * @param wrapper {CustomEvent} Custom event proxy for the DOM
  978. * subscription
  979. * @private
  980. * @static
  981. * @since 3.4.0
  982. */
  983. _clean: function (wrapper) {
  984. var key = wrapper.key,
  985. domkey = wrapper.domkey;
  986. remove(wrapper.el, wrapper.type, wrapper.fn, wrapper.capture);
  987. delete _wrappers[key];
  988. delete Y._yuievt.events[key];
  989. if (_el_events[domkey]) {
  990. delete _el_events[domkey][key];
  991. if (!Y.Object.size(_el_events[domkey])) {
  992. delete _el_events[domkey];
  993. }
  994. }
  995. },
  996. /**
  997. * Returns all listeners attached to the given element via addListener.
  998. * Optionally, you can specify a specific type of event to return.
  999. * @method getListeners
  1000. * @param el {HTMLElement|string} the element or element id to inspect
  1001. * @param type {string} optional type of listener to return. If
  1002. * left out, all listeners will be returned
  1003. * @return {CustomEvent} the custom event wrapper for the DOM event(s)
  1004. * @static
  1005. */
  1006. getListeners: function(el, type) {
  1007. var ek = Y.stamp(el, true), evts = _el_events[ek],
  1008. results=[] , key = (type) ? 'event:' + ek + type : null,
  1009. adapters = _eventenv.plugins;
  1010. if (!evts) {
  1011. return null;
  1012. }
  1013. if (key) {
  1014. // look for synthetic events
  1015. if (adapters[type] && adapters[type].eventDef) {
  1016. key += '_synth';
  1017. }
  1018. if (evts[key]) {
  1019. results.push(evts[key]);
  1020. }
  1021. // get native events as well
  1022. key += 'native';
  1023. if (evts[key]) {
  1024. results.push(evts[key]);
  1025. }
  1026. } else {
  1027. Y.each(evts, function(v, k) {
  1028. results.push(v);
  1029. });
  1030. }
  1031. return (results.length) ? results : null;
  1032. },
  1033. /**
  1034. * Removes all listeners registered by pe.event. Called
  1035. * automatically during the unload event.
  1036. * @method _unload
  1037. * @static
  1038. * @private
  1039. */
  1040. _unload: function(e) {
  1041. Y.each(_wrappers, function(v, k) {
  1042. if (v.type == 'unload') {
  1043. v.fire(e);
  1044. }
  1045. v.detachAll();
  1046. });
  1047. remove(win, "unload", onUnload);
  1048. },
  1049. /**
  1050. * Adds a DOM event directly without the caching, cleanup, context adj, etc
  1051. *
  1052. * @method nativeAdd
  1053. * @param {HTMLElement} el the element to bind the handler to
  1054. * @param {string} type the type of event handler
  1055. * @param {function} fn the callback to invoke
  1056. * @param {boolen} capture capture or bubble phase
  1057. * @static
  1058. * @private
  1059. */
  1060. nativeAdd: add,
  1061. /**
  1062. * Basic remove listener
  1063. *
  1064. * @method nativeRemove
  1065. * @param {HTMLElement} el the element to bind the handler to
  1066. * @param {string} type the type of event handler
  1067. * @param {function} fn the callback to invoke
  1068. * @param {boolen} capture capture or bubble phase
  1069. * @static
  1070. * @private
  1071. */
  1072. nativeRemove: remove
  1073. };
  1074. }();
  1075. Y.Event = Event;
  1076. if (config.injected || YUI.Env.windowLoaded) {
  1077. onLoad();
  1078. } else {
  1079. add(win, "load", onLoad);
  1080. }
  1081. // Process onAvailable/onContentReady items when when the DOM is ready in IE
  1082. if (Y.UA.ie) {
  1083. Y.on(EVENT_READY, Event._poll);
  1084. }
  1085. add(win, "unload", onUnload);
  1086. Event.Custom = Y.CustomEvent;
  1087. Event.Subscriber = Y.Subscriber;
  1088. Event.Target = Y.EventTarget;
  1089. Event.Handle = Y.EventHandle;
  1090. Event.Facade = Y.EventFacade;
  1091. Event._poll();
  1092. })();
  1093. /**
  1094. * DOM event listener abstraction layer
  1095. * @module event
  1096. * @submodule event-base
  1097. */
  1098. /**
  1099. * Executes the callback as soon as the specified element
  1100. * is detected in the DOM. This function expects a selector
  1101. * string for the element(s) to detect. If you already have
  1102. * an element reference, you don't need this event.
  1103. * @event available
  1104. * @param type {string} 'available'
  1105. * @param fn {function} the callback function to execute.
  1106. * @param el {string} an selector for the element(s) to attach
  1107. * @param context optional argument that specifies what 'this' refers to.
  1108. * @param args* 0..n additional arguments to pass on to the callback function.
  1109. * These arguments will be added after the event object.
  1110. * @return {EventHandle} the detach handle
  1111. * @for YUI
  1112. */
  1113. Y.Env.evt.plugins.available = {
  1114. on: function(type, fn, id, o) {
  1115. var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : null;
  1116. return Y.Event.onAvailable.call(Y.Event, id, fn, o, a);
  1117. }
  1118. };
  1119. /**
  1120. * Executes the callback as soon as the specified element
  1121. * is detected in the DOM with a nextSibling property
  1122. * (indicating that the element's children are available).
  1123. * This function expects a selector
  1124. * string for the element(s) to detect. If you already have
  1125. * an element reference, you don't need this event.
  1126. * @event contentready
  1127. * @param type {string} 'contentready'
  1128. * @param fn {function} the callback function to execute.
  1129. * @param el {string} an selector for the element(s) to attach.
  1130. * @param context optional argument that specifies what 'this' refers to.
  1131. * @param args* 0..n additional arguments to pass on to the callback function.
  1132. * These arguments will be added after the event object.
  1133. * @return {EventHandle} the detach handle
  1134. * @for YUI
  1135. */
  1136. Y.Env.evt.plugins.contentready = {
  1137. on: function(type, fn, id, o) {
  1138. var a = arguments.length > 4 ? Y.Array(arguments, 4, true) : null;
  1139. return Y.Event.onContentReady.call(Y.Event, id, fn, o, a);
  1140. }
  1141. };
  1142. }, '3.4.0' ,{requires:['event-custom-base']});