/ext-4.1.0_b3/src/dom/Element.js

https://bitbucket.org/srogerf/javascript · JavaScript · 1333 lines · 544 code · 98 blank · 691 comment · 145 complexity · 2bdc37abd5ba30ab33a54681ada490b2 MD5 · raw file

  1. /**
  2. * @class Ext.dom.Element
  3. * @alternateClassName Ext.Element
  4. * @alternateClassName Ext.core.Element
  5. * @extend Ext.dom.AbstractElement
  6. *
  7. * Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.
  8. *
  9. * All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all
  10. * DOM elements.
  11. *
  12. * Note that the events documented in this class are not Ext events, they encapsulate browser events. Some older browsers
  13. * may not support the full range of events. Which events are supported is beyond the control of Ext JS.
  14. *
  15. * Usage:
  16. *
  17. * // by id
  18. * var el = Ext.get("my-div");
  19. *
  20. * // by DOM element reference
  21. * var el = Ext.get(myDivElement);
  22. *
  23. * # Animations
  24. *
  25. * When an element is manipulated, by default there is no animation.
  26. *
  27. * var el = Ext.get("my-div");
  28. *
  29. * // no animation
  30. * el.setWidth(100);
  31. *
  32. * Many of the functions for manipulating an element have an optional "animate" parameter. This parameter can be
  33. * specified as boolean (true) for default animation effects.
  34. *
  35. * // default animation
  36. * el.setWidth(100, true);
  37. *
  38. * To configure the effects, an object literal with animation options to use as the Element animation configuration
  39. * object can also be specified. Note that the supported Element animation configuration options are a subset of the
  40. * {@link Ext.fx.Anim} animation options specific to Fx effects. The supported Element animation configuration options
  41. * are:
  42. *
  43. * Option Default Description
  44. * --------- -------- ---------------------------------------------
  45. * {@link Ext.fx.Anim#duration duration} .35 The duration of the animation in seconds
  46. * {@link Ext.fx.Anim#easing easing} easeOut The easing method
  47. * {@link Ext.fx.Anim#callback callback} none A function to execute when the anim completes
  48. * {@link Ext.fx.Anim#scope scope} this The scope (this) of the callback function
  49. *
  50. * Usage:
  51. *
  52. * // Element animation options object
  53. * var opt = {
  54. * {@link Ext.fx.Anim#duration duration}: 1,
  55. * {@link Ext.fx.Anim#easing easing}: 'elasticIn',
  56. * {@link Ext.fx.Anim#callback callback}: this.foo,
  57. * {@link Ext.fx.Anim#scope scope}: this
  58. * };
  59. * // animation with some options set
  60. * el.setWidth(100, opt);
  61. *
  62. * The Element animation object being used for the animation will be set on the options object as "anim", which allows
  63. * you to stop or manipulate the animation. Here is an example:
  64. *
  65. * // using the "anim" property to get the Anim object
  66. * if(opt.anim.isAnimated()){
  67. * opt.anim.stop();
  68. * }
  69. *
  70. * # Composite (Collections of) Elements
  71. *
  72. * For working with collections of Elements, see {@link Ext.CompositeElement}
  73. *
  74. * @constructor
  75. * Creates new Element directly.
  76. * @param {String/HTMLElement} element
  77. * @param {Boolean} [forceNew] By default the constructor checks to see if there is already an instance of this
  78. * element in the cache and if there is it returns the same instance. This will skip that check (useful for extending
  79. * this class).
  80. * @return {Object}
  81. */
  82. (function() {
  83. var HIDDEN = 'hidden',
  84. DOC = document,
  85. VISIBILITY = "visibility",
  86. DISPLAY = "display",
  87. NONE = "none",
  88. XMASKED = Ext.baseCSSPrefix + "masked",
  89. XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
  90. EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
  91. bodyRe = /^body/i,
  92. // speedy lookup for elements never to box adjust
  93. noBoxAdjust = Ext.isStrict ? {
  94. select: 1
  95. }: {
  96. input: 1,
  97. select: 1,
  98. textarea: 1
  99. },
  100. Element = Ext.define('Ext.dom.Element', {
  101. extend: 'Ext.dom.AbstractElement',
  102. alternateClassName: ['Ext.Element', 'Ext.core.Element'],
  103. addUnits: function() {
  104. return this.self.addUnits.apply(this.self, arguments);
  105. },
  106. /**
  107. * Tries to focus the element. Any exceptions are caught and ignored.
  108. * @param {Number} [defer] Milliseconds to defer the focus
  109. * @return {Ext.dom.Element} this
  110. */
  111. focus: function(defer, /* private */ dom) {
  112. var me = this,
  113. scrollTop,
  114. body;
  115. dom = dom || me.dom;
  116. body = (dom.ownerDocument || DOC).body || DOC.body;
  117. try {
  118. if (Number(defer)) {
  119. Ext.defer(me.focus, defer, me, [null, dom]);
  120. } else {
  121. // Focusing a large element, the browser attempts to scroll as much of it into view
  122. // as possible. We need to override this behaviour.
  123. if (dom.offsetHeight > Element.getViewHeight()) {
  124. scrollTop = body.scrollTop;
  125. }
  126. dom.focus();
  127. if (scrollTop !== undefined) {
  128. body.scrollTop = scrollTop;
  129. }
  130. }
  131. } catch(e) {
  132. }
  133. return me;
  134. },
  135. /**
  136. * Tries to blur the element. Any exceptions are caught and ignored.
  137. * @return {Ext.dom.Element} this
  138. */
  139. blur: function() {
  140. try {
  141. this.dom.blur();
  142. } catch(e) {
  143. }
  144. return this;
  145. },
  146. /**
  147. * Tests various css rules/browsers to determine if this element uses a border box
  148. * @return {Boolean}
  149. */
  150. isBorderBox: function() {
  151. var box = Ext.isBorderBox;
  152. if (box) {
  153. box = !((this.dom.tagName || "").toLowerCase() in noBoxAdjust);
  154. }
  155. return box;
  156. },
  157. /**
  158. * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
  159. * @param {Function} overFn The function to call when the mouse enters the Element.
  160. * @param {Function} outFn The function to call when the mouse leaves the Element.
  161. * @param {Object} [scope] The scope (`this` reference) in which the functions are executed. Defaults
  162. * to the Element's DOM element.
  163. * @param {Object} [options] Options for the listener. See {@link Ext.util.Observable#addListener the
  164. * options parameter}.
  165. * @return {Ext.dom.Element} this
  166. */
  167. hover: function(overFn, outFn, scope, options) {
  168. var me = this;
  169. me.on('mouseenter', overFn, scope || me.dom, options);
  170. me.on('mouseleave', outFn, scope || me.dom, options);
  171. return me;
  172. },
  173. /**
  174. * Returns the value of a namespaced attribute from the element's underlying DOM node.
  175. * @param {String} namespace The namespace in which to look for the attribute
  176. * @param {String} name The attribute name
  177. * @return {String} The attribute value
  178. */
  179. getAttributeNS: function(ns, name) {
  180. return this.getAttribute(name, ns);
  181. },
  182. getAttribute: (Ext.isIE && !(Ext.isIE9 && DOC.documentMode === 9)) ?
  183. function(name, ns) {
  184. var d = this.dom,
  185. type;
  186. if (ns) {
  187. type = typeof d[ns + ":" + name];
  188. if (type != 'undefined' && type != 'unknown') {
  189. return d[ns + ":" + name] || null;
  190. }
  191. return null;
  192. }
  193. if (name === "for") {
  194. name = "htmlFor";
  195. }
  196. return d[name] || null;
  197. } : function(name, ns) {
  198. var d = this.dom;
  199. if (ns) {
  200. return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
  201. }
  202. return d.getAttribute(name) || d[name] || null;
  203. },
  204. /**
  205. * @property {Boolean} autoBoxAdjust
  206. * True to automatically adjust width and height settings for box-model issues.
  207. */
  208. autoBoxAdjust: true,
  209. /**
  210. * Checks whether the element is currently visible using both visibility and display properties.
  211. * @param {Boolean} [deep] True to walk the dom and see if parent elements are hidden (defaults to false)
  212. * @return {Boolean} True if the element is currently visible, else false
  213. */
  214. isVisible : function(deep) {
  215. var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
  216. p = this.dom.parentNode;
  217. if (deep !== true || !vis) {
  218. return vis;
  219. }
  220. while (p && !(bodyRe.test(p.tagName))) {
  221. if (!Ext.fly(p, '_isVisible').isVisible()) {
  222. return false;
  223. }
  224. p = p.parentNode;
  225. }
  226. return true;
  227. },
  228. /**
  229. * Returns true if display is not "none"
  230. * @return {Boolean}
  231. */
  232. isDisplayed : function() {
  233. return !this.isStyle(DISPLAY, NONE);
  234. },
  235. /**
  236. * Convenience method for setVisibilityMode(Element.DISPLAY)
  237. * @param {String} [display] What to set display to when visible
  238. * @return {Ext.dom.Element} this
  239. */
  240. enableDisplayMode : function(display) {
  241. var me = this;
  242. me.setVisibilityMode(Element.DISPLAY);
  243. if (!Ext.isEmpty(display)) {
  244. (me.$cache || me.getCache()).data.originalDisplay = display;
  245. }
  246. return me;
  247. },
  248. /**
  249. * Puts a mask over this element to disable user interaction. Requires core.css.
  250. * This method can only be applied to elements which accept child nodes.
  251. * @param {String} [msg] A message to display in the mask
  252. * @param {String} [msgCls] A css class to apply to the msg element
  253. * @return {Ext.dom.Element} The mask element
  254. */
  255. mask : function(msg, msgCls /* private - passed by AbstractComponent.mask to avoid the need to interrogate the DOM to get the height*/, elHeight) {
  256. var me = this,
  257. dom = me.dom,
  258. setExpression = dom.style.setExpression,
  259. data = (me.$cache || me.getCache()).data,
  260. maskEl = data.maskEl,
  261. maskMsg = data.maskMsg;
  262. if (!(bodyRe.test(dom.tagName) && me.getStyle('position') == 'static')) {
  263. me.addCls(XMASKEDRELATIVE);
  264. }
  265. // We always needs to recreate the mask since the DOM element may have been re-created
  266. if (maskEl) {
  267. maskEl.remove();
  268. }
  269. if (maskMsg) {
  270. maskMsg.remove();
  271. }
  272. Ext.DomHelper.append(dom, [{
  273. cls : Ext.baseCSSPrefix + "mask"
  274. }, {
  275. cls : msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG,
  276. cn : {
  277. tag: 'div',
  278. html: msg || ''
  279. }
  280. }]);
  281. maskMsg = Ext.get(dom.lastChild);
  282. maskEl = Ext.get(maskMsg.dom.previousSibling);
  283. data.maskMsg = maskMsg;
  284. data.maskEl = maskEl;
  285. me.addCls(XMASKED);
  286. maskEl.setDisplayed(true);
  287. if (typeof msg == 'string') {
  288. maskMsg.setDisplayed(true);
  289. maskMsg.center(me);
  290. } else {
  291. maskMsg.setDisplayed(false);
  292. }
  293. // NOTE: CSS expressions are resource intensive and to be used only as a last resort
  294. // These expressions are removed as soon as they are no longer necessary - in the unmask method.
  295. // In normal use cases an element will be masked for a limited period of time.
  296. // Fix for https://sencha.jira.com/browse/EXTJSIV-19.
  297. // IE6 strict mode and IE6-9 quirks mode takes off left+right padding when calculating width!
  298. if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
  299. maskEl.dom.style.setExpression('width', 'this.parentNode.clientWidth + "px"');
  300. }
  301. // Some versions and modes of IE subtract top+bottom padding when calculating height.
  302. // Different versions from those which make the same error for width!
  303. if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
  304. maskEl.dom.style.setExpression('height', 'this.parentNode.' + (dom == DOC.body ? 'scrollHeight' : 'offsetHeight') + ' + "px"');
  305. }
  306. // ie will not expand full height automatically
  307. else if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
  308. maskEl.setSize(undefined, elHeight || me.getHeight());
  309. }
  310. return maskEl;
  311. },
  312. /**
  313. * Hides a previously applied mask.
  314. */
  315. unmask : function() {
  316. var me = this,
  317. data = (me.$cache || me.getCache()).data,
  318. maskEl = data.maskEl,
  319. maskMsg = data.maskMsg,
  320. style;
  321. if (maskEl) {
  322. style = maskEl.dom.style;
  323. // Remove resource-intensive CSS expressions as soon as they are not required.
  324. if (style.clearExpression) {
  325. style.clearExpression('width');
  326. style.clearExpression('height');
  327. }
  328. if (maskEl) {
  329. maskEl.remove();
  330. delete data.maskEl;
  331. }
  332. if (maskMsg) {
  333. maskMsg.remove();
  334. delete data.maskMsg;
  335. }
  336. me.removeCls([XMASKED, XMASKEDRELATIVE]);
  337. }
  338. },
  339. /**
  340. * Returns true if this element is masked. Also re-centers any displayed message within the mask.
  341. * @return {Boolean}
  342. */
  343. isMasked : function() {
  344. var me = this,
  345. data = (me.$cache || me.getCache()).data,
  346. maskEl = data.maskEl,
  347. maskMsg = data.maskMsg,
  348. hasMask = false;
  349. if (maskEl && maskEl.isVisible()) {
  350. if (maskMsg) {
  351. maskMsg.center(me);
  352. }
  353. hasMask = true;
  354. }
  355. return hasMask;
  356. },
  357. /**
  358. * Creates an iframe shim for this element to keep selects and other windowed objects from
  359. * showing through.
  360. * @return {Ext.dom.Element} The new shim element
  361. */
  362. createShim : function() {
  363. var el = DOC.createElement('iframe'),
  364. shim;
  365. el.frameBorder = '0';
  366. el.className = Ext.baseCSSPrefix + 'shim';
  367. el.src = Ext.SSL_SECURE_URL;
  368. shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
  369. shim.autoBoxAdjust = false;
  370. return shim;
  371. },
  372. /**
  373. * Convenience method for constructing a KeyMap
  374. * @param {String/Number/Number[]/Object} key Either a string with the keys to listen for, the numeric key code,
  375. * array of key codes or an object with the following options:
  376. * @param {Number/Array} key.key
  377. * @param {Boolean} key.shift
  378. * @param {Boolean} key.ctrl
  379. * @param {Boolean} key.alt
  380. * @param {Function} fn The function to call
  381. * @param {Object} [scope] The scope (`this` reference) in which the specified function is executed. Defaults to this Element.
  382. * @return {Ext.util.KeyMap} The KeyMap created
  383. */
  384. addKeyListener : function(key, fn, scope){
  385. var config;
  386. if(typeof key != 'object' || Ext.isArray(key)){
  387. config = {
  388. target: this,
  389. key: key,
  390. fn: fn,
  391. scope: scope
  392. };
  393. }else{
  394. config = {
  395. target: this,
  396. key : key.key,
  397. shift : key.shift,
  398. ctrl : key.ctrl,
  399. alt : key.alt,
  400. fn: fn,
  401. scope: scope
  402. };
  403. }
  404. return new Ext.util.KeyMap(config);
  405. },
  406. /**
  407. * Creates a KeyMap for this element
  408. * @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details
  409. * @return {Ext.util.KeyMap} The KeyMap created
  410. */
  411. addKeyMap : function(config) {
  412. return new Ext.util.KeyMap(Ext.apply({
  413. target: this
  414. }, config));
  415. },
  416. // Mouse events
  417. /**
  418. * @event click
  419. * Fires when a mouse click is detected within the element.
  420. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  421. * @param {HTMLElement} t The target of the event.
  422. */
  423. /**
  424. * @event contextmenu
  425. * Fires when a right click is detected within the element.
  426. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  427. * @param {HTMLElement} t The target of the event.
  428. */
  429. /**
  430. * @event dblclick
  431. * Fires when a mouse double click is detected within the element.
  432. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  433. * @param {HTMLElement} t The target of the event.
  434. */
  435. /**
  436. * @event mousedown
  437. * Fires when a mousedown is detected within the element.
  438. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  439. * @param {HTMLElement} t The target of the event.
  440. */
  441. /**
  442. * @event mouseup
  443. * Fires when a mouseup is detected within the element.
  444. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  445. * @param {HTMLElement} t The target of the event.
  446. */
  447. /**
  448. * @event mouseover
  449. * Fires when a mouseover is detected within the element.
  450. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  451. * @param {HTMLElement} t The target of the event.
  452. */
  453. /**
  454. * @event mousemove
  455. * Fires when a mousemove is detected with the element.
  456. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  457. * @param {HTMLElement} t The target of the event.
  458. */
  459. /**
  460. * @event mouseout
  461. * Fires when a mouseout is detected with the element.
  462. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  463. * @param {HTMLElement} t The target of the event.
  464. */
  465. /**
  466. * @event mouseenter
  467. * Fires when the mouse enters the element.
  468. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  469. * @param {HTMLElement} t The target of the event.
  470. */
  471. /**
  472. * @event mouseleave
  473. * Fires when the mouse leaves the element.
  474. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  475. * @param {HTMLElement} t The target of the event.
  476. */
  477. // Keyboard events
  478. /**
  479. * @event keypress
  480. * Fires when a keypress is detected within the element.
  481. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  482. * @param {HTMLElement} t The target of the event.
  483. */
  484. /**
  485. * @event keydown
  486. * Fires when a keydown is detected within the element.
  487. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  488. * @param {HTMLElement} t The target of the event.
  489. */
  490. /**
  491. * @event keyup
  492. * Fires when a keyup is detected within the element.
  493. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  494. * @param {HTMLElement} t The target of the event.
  495. */
  496. // HTML frame/object events
  497. /**
  498. * @event load
  499. * Fires when the user agent finishes loading all content within the element. Only supported by window, frames,
  500. * objects and images.
  501. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  502. * @param {HTMLElement} t The target of the event.
  503. */
  504. /**
  505. * @event unload
  506. * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target
  507. * element or any of its content has been removed.
  508. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  509. * @param {HTMLElement} t The target of the event.
  510. */
  511. /**
  512. * @event abort
  513. * Fires when an object/image is stopped from loading before completely loaded.
  514. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  515. * @param {HTMLElement} t The target of the event.
  516. */
  517. /**
  518. * @event error
  519. * Fires when an object/image/frame cannot be loaded properly.
  520. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  521. * @param {HTMLElement} t The target of the event.
  522. */
  523. /**
  524. * @event resize
  525. * Fires when a document view is resized.
  526. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  527. * @param {HTMLElement} t The target of the event.
  528. */
  529. /**
  530. * @event scroll
  531. * Fires when a document view is scrolled.
  532. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  533. * @param {HTMLElement} t The target of the event.
  534. */
  535. // Form events
  536. /**
  537. * @event select
  538. * Fires when a user selects some text in a text field, including input and textarea.
  539. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  540. * @param {HTMLElement} t The target of the event.
  541. */
  542. /**
  543. * @event change
  544. * Fires when a control loses the input focus and its value has been modified since gaining focus.
  545. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  546. * @param {HTMLElement} t The target of the event.
  547. */
  548. /**
  549. * @event submit
  550. * Fires when a form is submitted.
  551. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  552. * @param {HTMLElement} t The target of the event.
  553. */
  554. /**
  555. * @event reset
  556. * Fires when a form is reset.
  557. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  558. * @param {HTMLElement} t The target of the event.
  559. */
  560. /**
  561. * @event focus
  562. * Fires when an element receives focus either via the pointing device or by tab navigation.
  563. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  564. * @param {HTMLElement} t The target of the event.
  565. */
  566. /**
  567. * @event blur
  568. * Fires when an element loses focus either via the pointing device or by tabbing navigation.
  569. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  570. * @param {HTMLElement} t The target of the event.
  571. */
  572. // User Interface events
  573. /**
  574. * @event DOMFocusIn
  575. * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
  576. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  577. * @param {HTMLElement} t The target of the event.
  578. */
  579. /**
  580. * @event DOMFocusOut
  581. * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
  582. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  583. * @param {HTMLElement} t The target of the event.
  584. */
  585. /**
  586. * @event DOMActivate
  587. * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
  588. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  589. * @param {HTMLElement} t The target of the event.
  590. */
  591. // DOM Mutation events
  592. /**
  593. * @event DOMSubtreeModified
  594. * Where supported. Fires when the subtree is modified.
  595. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  596. * @param {HTMLElement} t The target of the event.
  597. */
  598. /**
  599. * @event DOMNodeInserted
  600. * Where supported. Fires when a node has been added as a child of another node.
  601. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  602. * @param {HTMLElement} t The target of the event.
  603. */
  604. /**
  605. * @event DOMNodeRemoved
  606. * Where supported. Fires when a descendant node of the element is removed.
  607. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  608. * @param {HTMLElement} t The target of the event.
  609. */
  610. /**
  611. * @event DOMNodeRemovedFromDocument
  612. * Where supported. Fires when a node is being removed from a document.
  613. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  614. * @param {HTMLElement} t The target of the event.
  615. */
  616. /**
  617. * @event DOMNodeInsertedIntoDocument
  618. * Where supported. Fires when a node is being inserted into a document.
  619. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  620. * @param {HTMLElement} t The target of the event.
  621. */
  622. /**
  623. * @event DOMAttrModified
  624. * Where supported. Fires when an attribute has been modified.
  625. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  626. * @param {HTMLElement} t The target of the event.
  627. */
  628. /**
  629. * @event DOMCharacterDataModified
  630. * Where supported. Fires when the character data has been modified.
  631. * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
  632. * @param {HTMLElement} t The target of the event.
  633. */
  634. /**
  635. * Appends an event handler to this element.
  636. *
  637. * @param {String} eventName The name of event to handle.
  638. *
  639. * @param {Function} fn The handler function the event invokes. This function is passed the following parameters:
  640. *
  641. * - **evt** : EventObject
  642. *
  643. * The {@link Ext.EventObject EventObject} describing the event.
  644. *
  645. * - **el** : HtmlElement
  646. *
  647. * The DOM element which was the target of the event. Note that this may be filtered by using the delegate option.
  648. *
  649. * - **o** : Object
  650. *
  651. * The options object from the call that setup the listener.
  652. *
  653. * @param {Object} scope (optional) The scope (**this** reference) in which the handler function is executed. **If
  654. * omitted, defaults to this Element.**
  655. *
  656. * @param {Object} options (optional) An object containing handler configuration properties. This may contain any of
  657. * the following properties:
  658. *
  659. * - **scope** Object :
  660. *
  661. * The scope (**this** reference) in which the handler function is executed. **If omitted, defaults to this
  662. * Element.**
  663. *
  664. * - **delegate** String:
  665. *
  666. * A simple selector to filter the target or look for a descendant of the target. See below for additional details.
  667. *
  668. * - **stopEvent** Boolean:
  669. *
  670. * True to stop the event. That is stop propagation, and prevent the default action.
  671. *
  672. * - **preventDefault** Boolean:
  673. *
  674. * True to prevent the default action
  675. *
  676. * - **stopPropagation** Boolean:
  677. *
  678. * True to prevent event propagation
  679. *
  680. * - **normalized** Boolean:
  681. *
  682. * False to pass a browser event to the handler function instead of an Ext.EventObject
  683. *
  684. * - **target** Ext.dom.Element:
  685. *
  686. * Only call the handler if the event was fired on the target Element, _not_ if the event was bubbled up from a
  687. * child node.
  688. *
  689. * - **delay** Number:
  690. *
  691. * The number of milliseconds to delay the invocation of the handler after the event fires.
  692. *
  693. * - **single** Boolean:
  694. *
  695. * True to add a handler to handle just the next firing of the event, and then remove itself.
  696. *
  697. * - **buffer** Number:
  698. *
  699. * Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed by the specified number of
  700. * milliseconds. If the event fires again within that time, the original handler is _not_ invoked, but the new
  701. * handler is scheduled in its place.
  702. *
  703. * **Combining Options**
  704. *
  705. * Using the options argument, it is possible to combine different types of listeners:
  706. *
  707. * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the options
  708. * object. The options object is available as the third parameter in the handler function.
  709. *
  710. * Code:
  711. *
  712. * el.on('click', this.onClick, this, {
  713. * single: true,
  714. * delay: 100,
  715. * stopEvent : true,
  716. * forumId: 4
  717. * });
  718. *
  719. * **Attaching multiple handlers in 1 call**
  720. *
  721. * The method also allows for a single argument to be passed which is a config object containing properties which
  722. * specify multiple handlers.
  723. *
  724. * Code:
  725. *
  726. * el.on({
  727. * 'click' : {
  728. * fn: this.onClick,
  729. * scope: this,
  730. * delay: 100
  731. * },
  732. * 'mouseover' : {
  733. * fn: this.onMouseOver,
  734. * scope: this
  735. * },
  736. * 'mouseout' : {
  737. * fn: this.onMouseOut,
  738. * scope: this
  739. * }
  740. * });
  741. *
  742. * Or a shorthand syntax:
  743. *
  744. * Code:
  745. *
  746. * el.on({
  747. * 'click' : this.onClick,
  748. * 'mouseover' : this.onMouseOver,
  749. * 'mouseout' : this.onMouseOut,
  750. * scope: this
  751. * });
  752. *
  753. * **delegate**
  754. *
  755. * This is a configuration option that you can pass along when registering a handler for an event to assist with
  756. * event delegation. Event delegation is a technique that is used to reduce memory consumption and prevent exposure
  757. * to memory-leaks. By registering an event for a container element as opposed to each element within a container.
  758. * By setting this configuration option to a simple selector, the target element will be filtered to look for a
  759. * descendant of the target. For example:
  760. *
  761. * // using this markup:
  762. * <div id='elId'>
  763. * <p id='p1'>paragraph one</p>
  764. * <p id='p2' class='clickable'>paragraph two</p>
  765. * <p id='p3'>paragraph three</p>
  766. * </div>
  767. *
  768. * // utilize event delegation to registering just one handler on the container element:
  769. * el = Ext.get('elId');
  770. * el.on(
  771. * 'click',
  772. * function(e,t) {
  773. * // handle click
  774. * console.info(t.id); // 'p2'
  775. * },
  776. * this,
  777. * {
  778. * // filter the target element to be a descendant with the class 'clickable'
  779. * delegate: '.clickable'
  780. * }
  781. * );
  782. *
  783. * @return {Ext.dom.Element} this
  784. */
  785. on: function(eventName, fn, scope, options) {
  786. Ext.EventManager.on(this, eventName, fn, scope || this, options);
  787. return this;
  788. },
  789. /**
  790. * Removes an event handler from this element.
  791. *
  792. * **Note**: if a *scope* was explicitly specified when {@link #on adding} the listener,
  793. * the same scope must be specified here.
  794. *
  795. * Example:
  796. *
  797. * el.un('click', this.handlerFn);
  798. * // or
  799. * el.removeListener('click', this.handlerFn);
  800. *
  801. * @param {String} eventName The name of the event from which to remove the handler.
  802. * @param {Function} fn The handler function to remove. **This must be a reference to the function passed into the
  803. * {@link #on} call.**
  804. * @param {Object} scope If a scope (**this** reference) was specified when the listener was added, then this must
  805. * refer to the same object.
  806. * @return {Ext.dom.Element} this
  807. */
  808. un: function(eventName, fn, scope) {
  809. Ext.EventManager.un(this, eventName, fn, scope || this);
  810. return this;
  811. },
  812. /**
  813. * Removes all previous added listeners from this element
  814. * @return {Ext.dom.Element} this
  815. */
  816. removeAllListeners: function() {
  817. Ext.EventManager.removeAll(this);
  818. return this;
  819. },
  820. /**
  821. * Recursively removes all previous added listeners from this element and its children
  822. * @return {Ext.dom.Element} this
  823. */
  824. purgeAllListeners: function() {
  825. Ext.EventManager.purgeElement(this);
  826. return this;
  827. }
  828. }, function() {
  829. var EC = Ext.cache,
  830. El = this,
  831. AbstractElement = Ext.dom.AbstractElement,
  832. focusRe = /a|button|embed|iframe|img|input|object|select|textarea/i,
  833. nonSpaceRe = /\S/,
  834. scriptTagRe = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
  835. replaceScriptTagRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
  836. srcRe = /\ssrc=([\'\"])(.*?)\1/i,
  837. typeRe = /\stype=([\'\"])(.*?)\1/i,
  838. useDocForId = !(Ext.isIE6 || Ext.isIE7 || Ext.isIE8);
  839. El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
  840. //</!if>
  841. // private
  842. // Garbage collection - uncache elements/purge listeners on orphaned elements
  843. // so we don't hold a reference and cause the browser to retain them
  844. function garbageCollect() {
  845. if (!Ext.enableGarbageCollector) {
  846. clearInterval(El.collectorThreadId);
  847. } else {
  848. var eid,
  849. el,
  850. d,
  851. o;
  852. for (eid in EC) {
  853. if (!EC.hasOwnProperty(eid)) {
  854. continue;
  855. }
  856. o = EC[eid];
  857. if (o.skipGarbageCollection) {
  858. continue;
  859. }
  860. el = o.el;
  861. if (!el) {
  862. continue;
  863. }
  864. d = el.dom;
  865. // -------------------------------------------------------
  866. // Determining what is garbage:
  867. // -------------------------------------------------------
  868. // !d
  869. // dom node is null, definitely garbage
  870. // -------------------------------------------------------
  871. // !d.parentNode
  872. // no parentNode == direct orphan, definitely garbage
  873. // -------------------------------------------------------
  874. // !d.offsetParent && !document.getElementById(eid)
  875. // display none elements have no offsetParent so we will
  876. // also try to look it up by it's id. However, check
  877. // offsetParent first so we don't do unneeded lookups.
  878. // This enables collection of elements that are not orphans
  879. // directly, but somewhere up the line they have an orphan
  880. // parent.
  881. // -------------------------------------------------------
  882. if (!d || !d.parentNode || (!d.offsetParent && !Ext.getElementById(eid))) {
  883. if (d && Ext.enableListenerCollection) {
  884. Ext.EventManager.removeAll(d);
  885. }
  886. delete EC[eid];
  887. }
  888. }
  889. // Cleanup IE Object leaks
  890. if (Ext.isIE) {
  891. var t = {};
  892. for (eid in EC) {
  893. if (!EC.hasOwnProperty(eid)) {
  894. continue;
  895. }
  896. t[eid] = EC[eid];
  897. }
  898. EC = Ext.cache = t;
  899. }
  900. }
  901. }
  902. El.collectorThreadId = setInterval(garbageCollect, 30000);
  903. //Stuff from Element-more.js
  904. El.addMethods({
  905. /**
  906. * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if
  907. * the mouse was not moved back into the Element within the delay. If the mouse *was* moved
  908. * back in, the function is not called.
  909. * @param {Number} delay The delay **in milliseconds** to wait for possible mouse re-entry before calling the handler function.
  910. * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time.
  911. * @param {Object} [scope] The scope (`this` reference) in which the handler function executes. Defaults to this Element.
  912. * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:
  913. *
  914. * // Hide the menu if the mouse moves out for 250ms or more
  915. * this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
  916. *
  917. * ...
  918. * // Remove mouseleave monitor on menu destroy
  919. * this.menuEl.un(this.mouseLeaveMonitor);
  920. *
  921. */
  922. monitorMouseLeave: function(delay, handler, scope) {
  923. var me = this,
  924. timer,
  925. listeners = {
  926. mouseleave: function(e) {
  927. timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
  928. },
  929. mouseenter: function() {
  930. clearTimeout(timer);
  931. },
  932. freezeEvent: true
  933. };
  934. me.on(listeners);
  935. return listeners;
  936. },
  937. /**
  938. * Stops the specified event(s) from bubbling and optionally prevents the default action
  939. * @param {String/String[]} eventName an event / array of events to stop from bubbling
  940. * @param {Boolean} [preventDefault] true to prevent the default action too
  941. * @return {Ext.dom.Element} this
  942. */
  943. swallowEvent : function(eventName, preventDefault) {
  944. var me = this;
  945. function fn(e) {
  946. e.stopPropagation();
  947. if (preventDefault) {
  948. e.preventDefault();
  949. }
  950. }
  951. if (Ext.isArray(eventName)) {
  952. var e,
  953. eLen = eventName.length;
  954. for (e = 0; e < eLen; e++) {
  955. me.on(eventName[e], fn);
  956. }
  957. return me;
  958. }
  959. me.on(eventName, fn);
  960. return me;
  961. },
  962. /**
  963. * Create an event handler on this element such that when the event fires and is handled by this element,
  964. * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
  965. * @param {String} eventName The type of event to relay
  966. * @param {Object} observable Any object that extends {@link Ext.util.Observable} that will provide the context
  967. * for firing the relayed event
  968. */
  969. relayEvent : function(eventName, observable) {
  970. this.on(eventName, function(e) {
  971. observable.fireEvent(eventName, e);
  972. });
  973. },
  974. /**
  975. * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
  976. * @param {Boolean} [forceReclean=false] By default the element keeps track if it has been cleaned already
  977. * so you can call this over and over. However, if you update the element and need to force a reclean, you
  978. * can pass true.
  979. */
  980. clean : function(forceReclean) {
  981. var me = this,
  982. dom = me.dom,
  983. data = (me.$cache || me.getCache()).data,
  984. n = dom.firstChild,
  985. ni = -1,
  986. nx;
  987. if (data.isCleaned && forceReclean !== true) {
  988. return me;
  989. }
  990. while (n) {
  991. nx = n.nextSibling;
  992. if (n.nodeType == 3) {
  993. // Remove empty/whitespace text nodes
  994. if (!(nonSpaceRe.test(n.nodeValue))) {
  995. dom.removeChild(n);
  996. // Combine adjacent text nodes
  997. } else if (nx && nx.nodeType == 3) {
  998. n.appendData(Ext.String.trim(nx.data));
  999. dom.removeChild(nx);
  1000. nx = n.nextSibling;
  1001. n.nodeIndex = ++ni;
  1002. }
  1003. } else {
  1004. // Recursively clean
  1005. Ext.fly(n).clean();
  1006. n.nodeIndex = ++ni;
  1007. }
  1008. n = nx;
  1009. }
  1010. data.isCleaned = true;
  1011. return me;
  1012. },
  1013. /**
  1014. * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#method-load} method. The method takes the same object
  1015. * parameter as {@link Ext.ElementLoader#method-load}
  1016. * @return {Ext.dom.Element} this
  1017. */
  1018. load : function(options) {
  1019. this.getLoader().load(options);
  1020. return this;
  1021. },
  1022. /**
  1023. * Gets this element's {@link Ext.ElementLoader ElementLoader}
  1024. * @return {Ext.ElementLoader} The loader
  1025. */
  1026. getLoader : function() {
  1027. var me = this,
  1028. data = (me.$cache || me.getCache()).data,
  1029. loader = data.loader;
  1030. if (!loader) {
  1031. data.loader = loader = new Ext.ElementLoader({
  1032. target: me
  1033. });
  1034. }
  1035. return loader;
  1036. },
  1037. /**
  1038. * Updates the innerHTML of this element, optionally searching for and processing scripts.
  1039. * @param {String} html The new HTML
  1040. * @param {Boolean} [loadScripts] True to look for and process scripts (defaults to false)
  1041. * @param {Function} [callback] For async script loading you can be notified when the update completes
  1042. * @return {Ext.dom.Element} this
  1043. */
  1044. update : function(html, loadScripts, callback) {
  1045. var me = this,
  1046. id,
  1047. dom,
  1048. interval;
  1049. if (!me.dom) {
  1050. return me;
  1051. }
  1052. html = html || '';
  1053. dom = me.dom;
  1054. if (loadScripts !== true) {
  1055. dom.innerHTML = html;
  1056. Ext.callback(callback, me);
  1057. return me;
  1058. }
  1059. id = Ext.id();
  1060. html += '<span id="' + id + '"></span>';
  1061. interval = setInterval(function() {
  1062. if (!(el = DOC.getElementById(id))) {
  1063. return false;
  1064. }
  1065. clearInterval(interval);
  1066. Ext.removeNode(el);
  1067. var hd = Ext.getHead().dom,
  1068. match,
  1069. attrs,
  1070. srcMatch,
  1071. typeMatch,
  1072. el,
  1073. s;
  1074. while ((match = scriptTagRe.exec(html))) {
  1075. attrs = match[1];
  1076. srcMatch = attrs ? attrs.match(srcRe) : false;
  1077. if (srcMatch && srcMatch[2]) {
  1078. s = DOC.createElement("script");
  1079. s.src = srcMatch[2];
  1080. typeMatch = attrs.match(typeRe);
  1081. if (typeMatch && typeMatch[2]) {
  1082. s.type = typeMatch[2];
  1083. }
  1084. hd.appendChild(s);
  1085. } else if (match[2] && match[2].length > 0) {
  1086. if (window.execScript) {
  1087. window.execScript(match[2]);
  1088. } else {
  1089. window.eval(match[2]);
  1090. }
  1091. }
  1092. }
  1093. Ext.callback(callback, me);
  1094. }, 20);
  1095. dom.innerHTML = html.replace(replaceScriptTagRe, '');
  1096. return me;
  1097. },
  1098. // inherit docs, overridden so we can add removeAnchor
  1099. removeAllListeners : function() {
  1100. this.removeAnchor();
  1101. Ext.EventManager.removeAll(this.dom);
  1102. return this;
  1103. },
  1104. /**
  1105. * Creates a proxy element of this element
  1106. * @param {String/Object} config The class name of the proxy element or a DomHelper config object
  1107. * @param {String/HTMLElement} [renderTo] The element or element id to render the proxy to. Defaults to: document.body.
  1108. * @param {Boolean} [matchBox=false] True to align and size the proxy to this element now.
  1109. * @return {Ext.dom.Element} The new proxy element
  1110. */
  1111. createProxy : function(config, renderTo, matchBox) {
  1112. config = (typeof config == 'object') ? config : {tag : "div", cls: config};
  1113. var me = this,
  1114. proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
  1115. Ext.DomHelper.insertBefore(me.dom, config, true);
  1116. proxy.setVisibilityMode(Element.DISPLAY);
  1117. proxy.hide();
  1118. if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
  1119. proxy.setBox(me.getBox());
  1120. }
  1121. return proxy;
  1122. },
  1123. /**
  1124. * Gets the parent node of the current element taking into account Ext.scopeResetCSS
  1125. * @protected
  1126. * @return {HTMLElement} The parent element
  1127. */
  1128. getScopeParent: function() {
  1129. var parent = this.dom.parentNode;
  1130. return Ext.scopeResetCSS ? parent.parentNode : parent;
  1131. },
  1132. /**
  1133. * Returns true if this element needs an explicit tabIndex to make it focusable. Input fields, text areas, buttons
  1134. * anchors elements **with an href** etc do not need a tabIndex, but structural elements do.
  1135. */
  1136. needsTabIndex: function() {
  1137. if (this.dom) {
  1138. if ((this.dom.nodeName === 'a') && (!this.dom.href)) {
  1139. return true;
  1140. }
  1141. return !focusRe.test(this.dom.nodeName);
  1142. }
  1143. },
  1144. /**
  1145. * Checks whether this element can be focused.
  1146. * @return {Boolean} True if the element is focusable
  1147. */
  1148. focusable: function () {
  1149. var dom = this.dom,
  1150. nodeName = dom.nodeName,
  1151. canFocus = false;
  1152. if (!dom.disabled) {
  1153. if (focusRe.test(nodeName)) {
  1154. if ((nodeName !== 'a') || dom.href) {
  1155. canFocus = true;
  1156. }
  1157. } else {
  1158. canFocus = !isNaN(dom.tabIndex);
  1159. }
  1160. }
  1161. return canFocus && this.isVisible(true);
  1162. }
  1163. });
  1164. if (Ext.isIE) {
  1165. El.prototype.getById = function (id, asDom) {
  1166. var dom = this.dom,
  1167. cached, el, ret;
  1168. if (dom) {
  1169. // for normal elements getElementById is the best solution, but if the el is
  1170. // not part of the document.body, we need to use all[]
  1171. el = (useDocForId && DOC.getElementById(id)) || dom.all[id];
  1172. if (el) {
  1173. if (asDom) {
  1174. ret = el;
  1175. } else {
  1176. // calling El.get here is a real hit (2x slower) because it has to
  1177. // redetermine that we are giving it a dom el.
  1178. cached = EC[id];
  1179. if (cached && cached.el) {
  1180. ret = cached.el;
  1181. ret.dom = el;
  1182. } else {
  1183. ret = new Element(el);
  1184. }
  1185. }
  1186. return ret;
  1187. }
  1188. }
  1189. return asDom ? Ext.getDom(id) : El.get(id);
  1190. };
  1191. }
  1192. El.createAlias({
  1193. /**
  1194. * @method
  1195. * @inheritdoc Ext.dom.Element#on
  1196. * Shorthand for {@link #on}.
  1197. */
  1198. addListener: 'on',
  1199. /**
  1200. * @method
  1201. * @inheritdoc Ext.dom.Element#un
  1202. * Shorthand for {@link #un}.
  1203. */
  1204. removeListener: 'un',
  1205. /**
  1206. * @method
  1207. * @inheritdoc Ext.dom.Element#removeAllListeners
  1208. * Alias for {@link #removeAllListeners}.
  1209. */
  1210. clearListeners: 'removeAllListeners'
  1211. });
  1212. El.Fly = AbstractElement.Fly = new Ext.Class({
  1213. extend: El,
  1214. constructor: function(dom) {
  1215. this.dom = dom;
  1216. },
  1217. attach: AbstractElement.Fly.prototype.attach
  1218. });
  1219. if (Ext.isIE) {
  1220. Ext.getElementById = function (id) {
  1221. var el = DOC.getElementById(id),
  1222. detachedBodyEl;
  1223. if (!el && (detachedBodyEl = AbstractElement.detachedBodyEl)) {
  1224. el = detachedBodyEl.dom.all[id];
  1225. }
  1226. return el;
  1227. };
  1228. } else if (!DOC.querySelector) {
  1229. Ext.getDetachedBody = Ext.getBody;
  1230. Ext.getElementById = function (id) {
  1231. return DOC.getElementById(id);
  1232. };
  1233. }
  1234. });
  1235. })();