PageRenderTime 63ms CodeModel.GetById 36ms app.highlight 18ms RepoModel.GetById 0ms app.codeStats 1ms

/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
  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})();