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