3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @class Ext.core.DomHelper
17 * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
18 * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
19 * from your DOM building code.</p>
21 * <p><b><u>DomHelper element specification object</u></b></p>
22 * <p>A specification object is used when creating elements. Attributes of this object
23 * are assumed to be element attributes, except for 4 special attributes:
24 * <div class="mdetail-params"><ul>
25 * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
26 * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
27 * same kind of element definition objects to be created and appended. These can be nested
28 * as deep as you want.</div></li>
29 * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
30 * This will end up being either the "class" attribute on a HTML fragment or className
31 * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
32 * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
34 * <p><b>NOTE:</b> For other arbitrary attributes, the value will currently <b>not</b> be automatically
35 * HTML-escaped prior to building the element's HTML string. This means that if your attribute value
36 * contains special characters that would not normally be allowed in a double-quoted attribute value,
37 * you <b>must</b> manually HTML-encode it beforehand (see {@link Ext.String#htmlEncode}) or risk
38 * malformed HTML being created. This behavior may change in a future release.</p>
40 * <p><b><u>Insertion methods</u></b></p>
41 * <p>Commonly used insertion methods:
42 * <div class="mdetail-params"><ul>
43 * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
44 * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
45 * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
46 * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
47 * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
48 * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
51 * <p><b><u>Example</u></b></p>
52 * <p>This is an example, where an unordered list with 3 children items is appended to an existing
53 * element with id <tt>'my-div'</tt>:<br>
55 var dh = Ext.core.DomHelper; // create shorthand alias
56 // specification object
61 // append children after creating
62 children: [ // may also specify 'cn' instead of 'children'
63 {tag: 'li', id: 'item0', html: 'List Item 0'},
64 {tag: 'li', id: 'item1', html: 'List Item 1'},
65 {tag: 'li', id: 'item2', html: 'List Item 2'}
69 'my-div', // the context element 'my-div' can either be the id or the actual node
70 spec // the specification object
73 * <p>Element creation specification parameters in this class may also be passed as an Array of
74 * specification objects. This can be used to insert multiple sibling nodes into an existing
75 * container very efficiently. For example, to add more list items to the example above:<pre><code>
77 {tag: 'li', id: 'item3', html: 'List Item 3'},
78 {tag: 'li', id: 'item4', html: 'List Item 4'}
82 * <p><b><u>Templating</u></b></p>
83 * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
84 * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
85 * insert new elements. Revisiting the example above, we could utilize templating this time:
88 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
90 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
92 for(var i = 0; i < 5, i++){
93 tpl.append(list, [i]); // use template to append to the actual node
96 * <p>An example using a template:<pre><code>
97 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
99 var tpl = new Ext.core.DomHelper.createTemplate(html);
100 tpl.append('blog-roll', ['link1', 'http://www.edspencer.net/', "Ed's Site"]);
101 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
104 * <p>The same example using named parameters:<pre><code>
105 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
107 var tpl = new Ext.core.DomHelper.createTemplate(html);
108 tpl.append('blog-roll', {
110 url: 'http://www.edspencer.net/',
111 text: "Ed's Site"
113 tpl.append('blog-roll', {
115 url: 'http://www.dustindiaz.com/',
116 text: "Dustin's Site"
120 * <p><b><u>Compiling Templates</u></b></p>
121 * <p>Templates are applied using regular expressions. The performance is great, but if
122 * you are adding a bunch of DOM elements using the same template, you can increase
123 * performance even further by {@link Ext.Template#compile "compiling"} the template.
124 * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
125 * broken up at the different variable points and a dynamic function is created and eval'ed.
126 * The generated function performs string concatenation of these parts and the passed
127 * variables instead of using regular expressions.
129 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
131 var tpl = new Ext.core.DomHelper.createTemplate(html);
134 //... use template like normal
137 * <p><b><u>Performance Boost</u></b></p>
138 * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
139 * of DOM can significantly boost performance.</p>
140 * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
141 * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
142 * results in the creation of a text node. Usage:</p>
144 Ext.core.DomHelper.useDom = true; // force it to use DOM; reduces performance
149 Ext.core.DomHelper = function(){
150 var tempTableEl = null,
151 emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
152 tableRe = /^table|tbody|tr|td$/i,
153 confRe = /tag|children|cn|html$/i,
154 tableElRe = /td|tr|tbody/i,
157 // kill repeat to save bytes
158 afterbegin = 'afterbegin',
159 afterend = 'afterend',
160 beforebegin = 'beforebegin',
161 beforeend = 'beforeend',
170 function doInsert(el, o, returnElement, pos, sibling, append){
174 newNode = createDom(o, null);
176 el.appendChild(newNode);
178 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
181 newNode = Ext.core.DomHelper.insertHtml(pos, el, Ext.core.DomHelper.createHtml(o));
183 return returnElement ? Ext.get(newNode, true) : newNode;
186 function createDom(o, parentNode){
194 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
195 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
196 for (var i = 0, l = o.length; i < l; i++) {
199 } else if (typeof o == 'string') { // Allow a string as a child spec.
200 el = doc.createTextNode(o);
202 el = doc.createElement( o.tag || 'div' );
203 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
205 if(!confRe.test(attr)){
211 el.setAttribute(attr, val);
218 Ext.core.DomHelper.applyStyles(el, o.style);
220 if ((cn = o.children || o.cn)) {
223 el.innerHTML = o.html;
227 parentNode.appendChild(el);
232 // build as innerHTML where available
233 function createHtml(o){
241 if(typeof o == "string"){
243 } else if (Ext.isArray(o)) {
244 for (i=0; i < o.length; i++) {
246 b += createHtml(o[i]);
250 b += '<' + (o.tag = o.tag || 'div');
253 if(!confRe.test(attr)){
254 if (typeof val == "object") {
255 b += ' ' + attr + '="';
257 b += key + ':' + val[key] + ';';
261 b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
265 // Now either just close the tag or try to add children and close the tag.
266 if (emptyTags.test(o.tag)) {
270 if ((cn = o.children || o.cn)) {
275 b += '</' + o.tag + '>';
281 function ieTable(depth, s, h, e){
282 tempTableEl.innerHTML = [s, h, e].join('');
289 // If the result is multiple siblings, then encapsulate them into one fragment.
292 var df = document.createDocumentFragment();
305 * Nasty code for IE's broken table implementation
307 function insertIntoTable(tag, where, el, html) {
311 tempTableEl = tempTableEl || document.createElement('div');
313 if(tag == 'td' && (where == afterbegin || where == beforeend) ||
314 !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
317 before = where == beforebegin ? el :
318 where == afterend ? el.nextSibling :
319 where == afterbegin ? el.firstChild : null;
321 if (where == beforebegin || where == afterend) {
325 if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
326 node = ieTable(4, trs, html, tre);
327 } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
328 (tag == 'tr' && (where == beforebegin || where == afterend))) {
329 node = ieTable(3, tbs, html, tbe);
331 node = ieTable(2, ts, html, te);
333 el.insertBefore(node, before);
339 * Fix for IE9 createContextualFragment missing method
341 function createContextualFragment(html){
342 var div = document.createElement("div"),
343 fragment = document.createDocumentFragment(),
347 div.innerHTML = html;
348 childNodes = div.childNodes;
349 length = childNodes.length;
351 for (; i < length; i++) {
352 fragment.appendChild(childNodes[i].cloneNode(true));
360 * Returns the markup for the passed Element(s) config.
361 * @param {Object} o The DOM object spec (and children)
364 markup : function(o){
365 return createHtml(o);
369 * Applies a style specification to an element.
370 * @param {String/HTMLElement} el The element to apply styles to
371 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
372 * a function which returns such a specification.
374 applyStyles : function(el, styles){
377 if (typeof styles == "function") {
378 styles = styles.call();
380 if (typeof styles == "string") {
381 styles = Ext.core.Element.parseStyles(styles);
383 if (typeof styles == "object") {
390 * Inserts an HTML fragment into the DOM.
391 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
392 * @param {HTMLElement/TextNode} el The context element
393 * @param {String} html The HTML fragment
394 * @return {HTMLElement} The new node
396 insertHtml : function(where, el, html){
405 where = where.toLowerCase();
406 // add these here because they are used in both branches of the condition.
407 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
408 hash[afterend] = ['AfterEnd', 'nextSibling'];
410 // if IE and context element is an HTMLElement
411 if (el.insertAdjacentHTML) {
412 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
416 // add these two to the hash.
417 hash[afterbegin] = ['AfterBegin', 'firstChild'];
418 hash[beforeend] = ['BeforeEnd', 'lastChild'];
419 if ((hashVal = hash[where])) {
420 el.insertAdjacentHTML(hashVal[0], html);
421 return el[hashVal[1]];
423 // if (not IE and context element is an HTMLElement) or TextNode
425 // we cannot insert anything inside a textnode so...
426 if (Ext.isTextNode(el)) {
427 where = where === 'afterbegin' ? 'beforebegin' : where;
428 where = where === 'beforeend' ? 'afterend' : where;
430 range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
431 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
435 frag = range.createContextualFragment(html);
437 frag = createContextualFragment(html);
439 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
440 return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
442 rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
445 range[setStart](el[rangeEl]);
446 frag = range.createContextualFragment(html);
448 frag = createContextualFragment(html);
451 if(where == afterbegin){
452 el.insertBefore(frag, el.firstChild);
454 el.appendChild(frag);
464 sourceClass: 'Ext.core.DomHelper',
465 sourceMethod: 'insertHtml',
468 msg: 'Illegal insertion point reached: "' + where + '"'
474 * Creates new DOM element(s) and inserts them before el.
475 * @param {Mixed} el The context element
476 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
477 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
478 * @return {HTMLElement/Ext.core.Element} The new node
480 insertBefore : function(el, o, returnElement){
481 return doInsert(el, o, returnElement, beforebegin);
485 * Creates new DOM element(s) and inserts them after el.
486 * @param {Mixed} el The context element
487 * @param {Object} o The DOM object spec (and children)
488 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
489 * @return {HTMLElement/Ext.core.Element} The new node
491 insertAfter : function(el, o, returnElement){
492 return doInsert(el, o, returnElement, afterend, 'nextSibling');
496 * Creates new DOM element(s) and inserts them as the first child of el.
497 * @param {Mixed} el The context element
498 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
499 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
500 * @return {HTMLElement/Ext.core.Element} The new node
502 insertFirst : function(el, o, returnElement){
503 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
507 * Creates new DOM element(s) and appends them to el.
508 * @param {Mixed} el The context element
509 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
510 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
511 * @return {HTMLElement/Ext.core.Element} The new node
513 append : function(el, o, returnElement){
514 return doInsert(el, o, returnElement, beforeend, '', true);
518 * Creates new DOM element(s) and overwrites the contents of el with them.
519 * @param {Mixed} el The context element
520 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
521 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
522 * @return {HTMLElement/Ext.core.Element} The new node
524 overwrite : function(el, o, returnElement){
526 el.innerHTML = createHtml(o);
527 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
530 createHtml : createHtml,
533 * Creates new DOM element(s) without inserting them to the document.
534 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
535 * @return {HTMLElement} The new uninserted node
538 createDom: createDom,
540 /** True to force the use of DOM instead of html fragments @type Boolean */
544 * Creates a new Ext.Template from the DOM object spec.
545 * @param {Object} o The DOM object spec (and children)
546 * @return {Ext.Template} The new template
548 createTemplate : function(o){
549 var html = Ext.core.DomHelper.createHtml(o);
550 return Ext.create('Ext.Template', html);
557 * This is code is also distributed under MIT license for use
558 * with jQuery and prototype JavaScript libraries.
561 * @class Ext.DomQuery
562 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
564 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#selectors">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
567 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
569 <h4>Element Selectors:</h4>
571 <li> <b>*</b> any element</li>
572 <li> <b>E</b> an element with the tag E</li>
573 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
574 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
575 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
576 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
578 <h4>Attribute Selectors:</h4>
579 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
581 <li> <b>E[foo]</b> has an attribute "foo"</li>
582 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
583 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
584 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
585 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
586 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
587 <li> <b>E[foo!=bar]</b> attribute "foo" does not equal "bar"</li>
589 <h4>Pseudo Classes:</h4>
591 <li> <b>E:first-child</b> E is the first child of its parent</li>
592 <li> <b>E:last-child</b> E is the last child of its parent</li>
593 <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
594 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
595 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
596 <li> <b>E:only-child</b> E is the only child of its parent</li>
597 <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
598 <li> <b>E:first</b> the first E in the resultset</li>
599 <li> <b>E:last</b> the last E in the resultset</li>
600 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
601 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
602 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
603 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
604 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
605 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
606 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
607 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
608 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
609 <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
611 <h4>CSS Value Selectors:</h4>
613 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
614 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
615 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
616 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
617 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
618 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
624 Ext.core.DomQuery = Ext.DomQuery = function(){
629 trimRe = /^\s+|\s+$/g,
630 tplRe = /\{(\d+)\}/g,
631 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
632 tagTokenRe = /^(#)?([\w-\*]+)/,
633 nthRe = /(\d*)n\+?(\d*)/,
635 // This is for IE MSXML which does not support expandos.
636 // IE runs the same speed using setAttribute, however FF slows way down
637 // and Safari completely fails so they need to continue to use expandos.
638 isIE = window.ActiveXObject ? true : false,
641 // this eval is stop the compressor from
642 // renaming the variable to something shorter
643 eval("var batch = 30803;");
645 // Retrieve the child node from a particular
646 // parent at the specified index.
647 function child(parent, index){
649 n = parent.firstChild;
661 // retrieve the next element node
663 while((n = n.nextSibling) && n.nodeType != 1);
667 // retrieve the previous element node
669 while((n = n.previousSibling) && n.nodeType != 1);
673 // Mark each child node with a nodeIndex skipping and
674 // removing empty text nodes.
675 function children(parent){
676 var n = parent.firstChild,
680 nextNode = n.nextSibling;
681 // clean worthless empty nodes.
682 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
683 parent.removeChild(n);
685 // add an expando nodeIndex
686 n.nodeIndex = ++nodeIndex;
694 // nodeSet - array of nodes
696 function byClassName(nodeSet, cls){
700 var result = [], ri = -1;
701 for(var i = 0, ci; ci = nodeSet[i]; i++){
702 if((' '+ci.className+' ').indexOf(cls) != -1){
709 function attrValue(n, attr){
710 // if its an array, use the first node.
711 if(!n.tagName && typeof n.length != "undefined"){
721 if(attr == "class" || attr == "className"){
724 return n.getAttribute(attr) || n[attr];
730 // mode - false, /, >, +, ~
731 // tagName - defaults to "*"
732 function getNodes(ns, mode, tagName){
733 var result = [], ri = -1, cs;
737 tagName = tagName || "*";
739 if(typeof ns.getElementsByTagName != "undefined"){
743 // no mode specified, grab all elements by tagName
746 for(var i = 0, ni; ni = ns[i]; i++){
747 cs = ni.getElementsByTagName(tagName);
748 for(var j = 0, ci; ci = cs[j]; j++){
752 // Direct Child mode (/ or >)
753 // E > F or E/F all direct children elements of E that have the tag
754 } else if(mode == "/" || mode == ">"){
755 var utag = tagName.toUpperCase();
756 for(var i = 0, ni, cn; ni = ns[i]; i++){
758 for(var j = 0, cj; cj = cn[j]; j++){
759 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
764 // Immediately Preceding mode (+)
765 // E + F all elements with the tag F that are immediately preceded by an element with the tag E
766 }else if(mode == "+"){
767 var utag = tagName.toUpperCase();
768 for(var i = 0, n; n = ns[i]; i++){
769 while((n = n.nextSibling) && n.nodeType != 1);
770 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
775 // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
776 }else if(mode == "~"){
777 var utag = tagName.toUpperCase();
778 for(var i = 0, n; n = ns[i]; i++){
779 while((n = n.nextSibling)){
780 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
789 function concat(a, b){
793 for(var i = 0, l = b.length; i < l; i++){
799 function byTag(cs, tagName){
800 if(cs.tagName || cs == document){
806 var result = [], ri = -1;
807 tagName = tagName.toLowerCase();
808 for(var i = 0, ci; ci = cs[i]; i++){
809 if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
816 function byId(cs, id){
817 if(cs.tagName || cs == document){
823 var result = [], ri = -1;
824 for(var i = 0, ci; ci = cs[i]; i++){
825 if(ci && ci.id == id){
833 // operators are =, !=, ^=, $=, *=, %=, |= and ~=
835 function byAttribute(cs, attr, value, op, custom){
838 useGetStyle = custom == "{",
839 fn = Ext.DomQuery.operators[op],
844 for(var i = 0, ci; ci = cs[i]; i++){
845 // skip non-element nodes.
846 if(ci.nodeType != 1){
849 // only need to do this for the first node
851 xml = Ext.DomQuery.isXml(ci);
855 // we only need to change the property names if we're dealing with html nodes, not XML
858 a = Ext.DomQuery.getStyle(ci, attr);
859 } else if (attr == "class" || attr == "className"){
861 } else if (attr == "for"){
863 } else if (attr == "href"){
864 // getAttribute href bug
865 // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
866 a = ci.getAttribute("href", 2);
868 a = ci.getAttribute(attr);
871 a = ci.getAttribute(attr);
873 if((fn && fn(a, value)) || (!fn && a)){
880 function byPseudo(cs, name, value){
881 return Ext.DomQuery.pseudos[name](cs, value);
884 function nodupIEXml(cs){
887 cs[0].setAttribute("_nodup", d);
889 for(var i = 1, len = cs.length; i < len; i++){
891 if(!c.getAttribute("_nodup") != d){
892 c.setAttribute("_nodup", d);
896 for(var i = 0, len = cs.length; i < len; i++){
897 cs[i].removeAttribute("_nodup");
906 var len = cs.length, c, i, r = cs, cj, ri = -1;
907 if(!len || typeof cs.nodeType != "undefined" || len == 1){
910 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
911 return nodupIEXml(cs);
915 for(i = 1; c = cs[i]; i++){
920 for(var j = 0; j < i; j++){
923 for(j = i+1; cj = cs[j]; j++){
935 function quickDiffIEXml(c1, c2){
938 for(var i = 0, len = c1.length; i < len; i++){
939 c1[i].setAttribute("_qdiff", d);
941 for(var i = 0, len = c2.length; i < len; i++){
942 if(c2[i].getAttribute("_qdiff") != d){
946 for(var i = 0, len = c1.length; i < len; i++){
947 c1[i].removeAttribute("_qdiff");
952 function quickDiff(c1, c2){
953 var len1 = c1.length,
959 if(isIE && typeof c1[0].selectSingleNode != "undefined"){
960 return quickDiffIEXml(c1, c2);
962 for(var i = 0; i < len1; i++){
965 for(var i = 0, len = c2.length; i < len; i++){
966 if(c2[i]._qdiff != d){
973 function quickId(ns, mode, root, id){
975 var d = root.ownerDocument || root;
976 return d.getElementById(id);
978 ns = getNodes(ns, mode, "*");
983 getStyle : function(el, name){
984 return Ext.fly(el).getStyle(name);
987 * Compiles a selector/xpath query into a reusable function. The returned function
988 * takes one parameter "root" (optional), which is the context node from where the query should start.
989 * @param {String} selector The selector/xpath query
990 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
993 compile : function(path, type){
994 type = type || "select";
997 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
1000 matchers = Ext.DomQuery.matchers,
1001 matchersLn = matchers.length,
1003 // accept leading mode switch
1004 lmode = path.match(modeRe);
1006 if(lmode && lmode[1]){
1007 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
1008 path = path.replace(lmode[1], "");
1011 // strip leading slashes
1012 while(path.substr(0, 1)=="/"){
1013 path = path.substr(1);
1016 while(path && lastPath != path){
1018 var tokenMatch = path.match(tagTokenRe);
1019 if(type == "select"){
1022 if(tokenMatch[1] == "#"){
1023 fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
1025 fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1027 path = path.replace(tokenMatch[0], "");
1028 }else if(path.substr(0, 1) != '@'){
1029 fn[fn.length] = 'n = getNodes(n, mode, "*");';
1034 if(tokenMatch[1] == "#"){
1035 fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1037 fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1039 path = path.replace(tokenMatch[0], "");
1042 while(!(modeMatch = path.match(modeRe))){
1043 var matched = false;
1044 for(var j = 0; j < matchersLn; j++){
1045 var t = matchers[j];
1046 var m = path.match(t.re);
1048 fn[fn.length] = t.select.replace(tplRe, function(x, i){
1051 path = path.replace(m[0], "");
1056 // prevent infinite loop on bad selector
1060 sourceClass: 'Ext.DomQuery',
1061 sourceMethod: 'compile',
1062 msg: 'Error parsing selector. Parsing failed at "' + path + '"'
1068 fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1069 path = path.replace(modeMatch[1], "");
1073 fn[fn.length] = "return nodup(n);\n}";
1075 // eval fn and return it
1081 * Selects an array of DOM nodes using JavaScript-only implementation.
1083 * Use {@link #select} to take advantage of browsers built-in support for CSS selectors.
1085 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1086 * @param {Node/String} root (optional) The start of the query (defaults to document).
1087 * @return {Array} An Array of DOM elements which match the selector. If there are
1088 * no matches, and empty Array is returned.
1090 jsSelect: function(path, root, type){
1091 // set root to doc if not specified.
1092 root = root || document;
1094 if(typeof root == "string"){
1095 root = document.getElementById(root);
1097 var paths = path.split(","),
1100 // loop over each selector
1101 for(var i = 0, len = paths.length; i < len; i++){
1102 var subPath = paths[i].replace(trimRe, "");
1103 // compile and place in cache
1104 if(!cache[subPath]){
1105 cache[subPath] = Ext.DomQuery.compile(subPath);
1106 if(!cache[subPath]){
1109 sourceClass: 'Ext.DomQuery',
1110 sourceMethod: 'jsSelect',
1111 msg: subPath + ' is not a valid selector'
1116 var result = cache[subPath](root);
1117 if(result && result != document){
1118 results = results.concat(result);
1122 // if there were multiple selectors, make sure dups
1124 if(paths.length > 1){
1125 return nodup(results);
1130 isXml: function(el) {
1131 var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1132 return docEl ? docEl.nodeName !== "HTML" : false;
1136 * Selects an array of DOM nodes by CSS/XPath selector.
1138 * Uses [document.querySelectorAll][0] if browser supports that, otherwise falls back to
1139 * {@link #jsSelect} to do the work.
1141 * Aliased as {@link Ext#query}.
1143 * [0]: https://developer.mozilla.org/en/DOM/document.querySelectorAll
1145 * @param {String} path The selector/xpath query
1146 * @param {Node} root (optional) The start of the query (defaults to document).
1147 * @return {Array} An array of DOM elements (not a NodeList as returned by `querySelectorAll`).
1148 * Empty array when no matches.
1151 select : document.querySelectorAll ? function(path, root, type) {
1152 root = root || document;
1153 if (!Ext.DomQuery.isXml(root)) {
1155 var cs = root.querySelectorAll(path);
1156 return Ext.Array.toArray(cs);
1160 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1161 } : function(path, root, type) {
1162 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1166 * Selects a single element.
1167 * @param {String} selector The selector/xpath query
1168 * @param {Node} root (optional) The start of the query (defaults to document).
1169 * @return {Element} The DOM element which matched the selector.
1171 selectNode : function(path, root){
1172 return Ext.DomQuery.select(path, root)[0];
1176 * Selects the value of a node, optionally replacing null with the defaultValue.
1177 * @param {String} selector The selector/xpath query
1178 * @param {Node} root (optional) The start of the query (defaults to document).
1179 * @param {String} defaultValue
1182 selectValue : function(path, root, defaultValue){
1183 path = path.replace(trimRe, "");
1184 if(!valueCache[path]){
1185 valueCache[path] = Ext.DomQuery.compile(path, "select");
1187 var n = valueCache[path](root), v;
1188 n = n[0] ? n[0] : n;
1190 // overcome a limitation of maximum textnode size
1191 // Rumored to potentially crash IE6 but has not been confirmed.
1192 // http://reference.sitepoint.com/javascript/Node/normalize
1193 // https://developer.mozilla.org/En/DOM/Node.normalize
1194 if (typeof n.normalize == 'function') n.normalize();
1196 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1197 return ((v === null||v === undefined||v==='') ? defaultValue : v);
1201 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1202 * @param {String} selector The selector/xpath query
1203 * @param {Node} root (optional) The start of the query (defaults to document).
1204 * @param {Number} defaultValue
1207 selectNumber : function(path, root, defaultValue){
1208 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1209 return parseFloat(v);
1213 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1214 * @param {String/HTMLElement/Array} el An element id, element or array of elements
1215 * @param {String} selector The simple selector to test
1218 is : function(el, ss){
1219 if(typeof el == "string"){
1220 el = document.getElementById(el);
1222 var isArray = Ext.isArray(el),
1223 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1224 return isArray ? (result.length == el.length) : (result.length > 0);
1228 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1229 * @param {Array} el An array of elements to filter
1230 * @param {String} selector The simple selector to test
1231 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1232 * the selector instead of the ones that match
1233 * @return {Array} An Array of DOM elements which match the selector. If there are
1234 * no matches, and empty Array is returned.
1236 filter : function(els, ss, nonMatches){
1237 ss = ss.replace(trimRe, "");
1238 if(!simpleCache[ss]){
1239 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1241 var result = simpleCache[ss](els);
1242 return nonMatches ? quickDiff(result, els) : result;
1246 * Collection of matching regular expressions and code snippets.
1247 * Each capture group within () will be replace the {} in the select
1248 * statement as specified by their index.
1252 select: 'n = byClassName(n, " {1} ");'
1254 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1255 select: 'n = byPseudo(n, "{1}", "{2}");'
1257 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1258 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1261 select: 'n = byId(n, "{1}");'
1264 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1269 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1270 * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, > <.
1273 "=" : function(a, v){
1276 "!=" : function(a, v){
1279 "^=" : function(a, v){
1280 return a && a.substr(0, v.length) == v;
1282 "$=" : function(a, v){
1283 return a && a.substr(a.length-v.length) == v;
1285 "*=" : function(a, v){
1286 return a && a.indexOf(v) !== -1;
1288 "%=" : function(a, v){
1289 return (a % v) == 0;
1291 "|=" : function(a, v){
1292 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1294 "~=" : function(a, v){
1295 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1300 Object hash of "pseudo class" filter functions which are used when filtering selections.
1301 Each function is passed two parameters:
1304 An Array of DOM elements to filter.
1307 The argument (if any) supplied in the selector.
1309 A filter function returns an Array of DOM elements which conform to the pseudo class.
1310 In addition to the provided pseudo classes listed above such as `first-child` and `nth-child`,
1311 developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.
1313 For example, to filter `a` elements to only return links to __external__ resources:
1315 Ext.DomQuery.pseudos.external = function(c, v){
1316 var r = [], ri = -1;
1317 for(var i = 0, ci; ci = c[i]; i++){
1318 // Include in result set only if it's a link to an external resource
1319 if(ci.hostname != location.hostname){
1326 Then external links could be gathered with the following statement:
1328 var externalLinks = Ext.select("a:external");
1333 "first-child" : function(c){
1334 var r = [], ri = -1, n;
1335 for(var i = 0, ci; ci = n = c[i]; i++){
1336 while((n = n.previousSibling) && n.nodeType != 1);
1344 "last-child" : function(c){
1345 var r = [], ri = -1, n;
1346 for(var i = 0, ci; ci = n = c[i]; i++){
1347 while((n = n.nextSibling) && n.nodeType != 1);
1355 "nth-child" : function(c, a) {
1356 var r = [], ri = -1,
1357 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1358 f = (m[1] || 1) - 0, l = m[2] - 0;
1359 for(var i = 0, n; n = c[i]; i++){
1360 var pn = n.parentNode;
1361 if (batch != pn._batch) {
1363 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1364 if(cn.nodeType == 1){
1371 if (l == 0 || n.nodeIndex == l){
1374 } else if ((n.nodeIndex + l) % f == 0){
1382 "only-child" : function(c){
1383 var r = [], ri = -1;;
1384 for(var i = 0, ci; ci = c[i]; i++){
1385 if(!prev(ci) && !next(ci)){
1392 "empty" : function(c){
1393 var r = [], ri = -1;
1394 for(var i = 0, ci; ci = c[i]; i++){
1395 var cns = ci.childNodes, j = 0, cn, empty = true;
1398 if(cn.nodeType == 1 || cn.nodeType == 3){
1410 "contains" : function(c, v){
1411 var r = [], ri = -1;
1412 for(var i = 0, ci; ci = c[i]; i++){
1413 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
1420 "nodeValue" : function(c, v){
1421 var r = [], ri = -1;
1422 for(var i = 0, ci; ci = c[i]; i++){
1423 if(ci.firstChild && ci.firstChild.nodeValue == v){
1430 "checked" : function(c){
1431 var r = [], ri = -1;
1432 for(var i = 0, ci; ci = c[i]; i++){
1433 if(ci.checked == true){
1440 "not" : function(c, ss){
1441 return Ext.DomQuery.filter(c, ss, true);
1444 "any" : function(c, selectors){
1445 var ss = selectors.split('|'),
1447 for(var i = 0, ci; ci = c[i]; i++){
1448 for(var j = 0; s = ss[j]; j++){
1449 if(Ext.DomQuery.is(ci, s)){
1458 "odd" : function(c){
1459 return this["nth-child"](c, "odd");
1462 "even" : function(c){
1463 return this["nth-child"](c, "even");
1466 "nth" : function(c, a){
1467 return c[a-1] || [];
1470 "first" : function(c){
1474 "last" : function(c){
1475 return c[c.length-1] || [];
1478 "has" : function(c, ss){
1479 var s = Ext.DomQuery.select,
1481 for(var i = 0, ci; ci = c[i]; i++){
1482 if(s(ss, ci).length > 0){
1489 "next" : function(c, ss){
1490 var is = Ext.DomQuery.is,
1492 for(var i = 0, ci; ci = c[i]; i++){
1501 "prev" : function(c, ss){
1502 var is = Ext.DomQuery.is,
1504 for(var i = 0, ci; ci = c[i]; i++){
1517 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
1518 * @param {String} path The selector/xpath query
1519 * @param {Node} root (optional) The start of the query (defaults to document).
1524 Ext.query = Ext.DomQuery.select;
1527 * @class Ext.core.Element
1528 * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
1529 * <p>All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all DOM elements.</p>
1530 * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
1531 * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
1532 * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
1536 var el = Ext.get("my-div");
1538 // by DOM element reference
1539 var el = Ext.get(myDivElement);
1541 * <b>Animations</b><br />
1542 * <p>When an element is manipulated, by default there is no animation.</p>
1544 var el = Ext.get("my-div");
1549 * <p>Many of the functions for manipulating an element have an optional "animate" parameter. This
1550 * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
1552 // default animation
1553 el.setWidth(100, true);
1556 * <p>To configure the effects, an object literal with animation options to use as the Element animation
1557 * configuration object can also be specified. Note that the supported Element animation configuration
1558 * options are a subset of the {@link Ext.fx.Anim} animation options specific to Fx effects. The supported
1559 * Element animation configuration options are:</p>
1561 Option Default Description
1562 --------- -------- ---------------------------------------------
1563 {@link Ext.fx.Anim#duration duration} .35 The duration of the animation in seconds
1564 {@link Ext.fx.Anim#easing easing} easeOut The easing method
1565 {@link Ext.fx.Anim#callback callback} none A function to execute when the anim completes
1566 {@link Ext.fx.Anim#scope scope} this The scope (this) of the callback function
1570 // Element animation options object
1572 {@link Ext.fx.Anim#duration duration}: 1,
1573 {@link Ext.fx.Anim#easing easing}: 'elasticIn',
1574 {@link Ext.fx.Anim#callback callback}: this.foo,
1575 {@link Ext.fx.Anim#scope scope}: this
1577 // animation with some options set
1578 el.setWidth(100, opt);
1580 * <p>The Element animation object being used for the animation will be set on the options
1581 * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
1583 // using the "anim" property to get the Anim object
1584 if(opt.anim.isAnimated()){
1588 * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
1589 * <p><b> Composite (Collections of) Elements</b></p>
1590 * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
1591 * @constructor Create a new Element directly.
1592 * @param {String/HTMLElement} element
1593 * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
1599 Ext.Element = Ext.core.Element = function(element, forceNew) {
1600 var dom = typeof element == "string" ? DOC.getElementById(element) : element,
1609 if (!forceNew && id && EC[id]) {
1610 // element object already exists
1621 * The DOM element ID
1624 this.id = id || Ext.id(dom);
1627 var DH = Ext.core.DomHelper,
1628 El = Ext.core.Element;
1633 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
1634 * @param {Object} o The object with the attributes
1635 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
1636 * @return {Ext.core.Element} this
1638 set: function(o, useSet) {
1642 useSet = (useSet !== false) && !!el.setAttribute;
1645 if (o.hasOwnProperty(attr)) {
1647 if (attr == 'style') {
1648 DH.applyStyles(el, val);
1649 } else if (attr == 'cls') {
1651 } else if (useSet) {
1652 el.setAttribute(attr, val);
1664 * Fires when a mouse click is detected within the element.
1665 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1666 * @param {HtmlElement} t The target of the event.
1667 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1670 * @event contextmenu
1671 * Fires when a right click is detected within the element.
1672 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1673 * @param {HtmlElement} t The target of the event.
1674 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1678 * Fires when a mouse double click is detected within the element.
1679 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1680 * @param {HtmlElement} t The target of the event.
1681 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1685 * Fires when a mousedown is detected within the element.
1686 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1687 * @param {HtmlElement} t The target of the event.
1688 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1692 * Fires when a mouseup is detected within the element.
1693 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1694 * @param {HtmlElement} t The target of the event.
1695 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1699 * Fires when a mouseover is detected within the element.
1700 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1701 * @param {HtmlElement} t The target of the event.
1702 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1706 * Fires when a mousemove is detected with the element.
1707 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1708 * @param {HtmlElement} t The target of the event.
1709 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1713 * Fires when a mouseout is detected with the element.
1714 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1715 * @param {HtmlElement} t The target of the event.
1716 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1720 * Fires when the mouse enters the element.
1721 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1722 * @param {HtmlElement} t The target of the event.
1723 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1727 * Fires when the mouse leaves the element.
1728 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1729 * @param {HtmlElement} t The target of the event.
1730 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1736 * Fires when a keypress is detected within the element.
1737 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1738 * @param {HtmlElement} t The target of the event.
1739 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1743 * Fires when a keydown is detected within the element.
1744 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1745 * @param {HtmlElement} t The target of the event.
1746 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1750 * Fires when a keyup is detected within the element.
1751 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1752 * @param {HtmlElement} t The target of the event.
1753 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1757 // HTML frame/object events
1760 * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
1761 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1762 * @param {HtmlElement} t The target of the event.
1763 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1767 * Fires when the user agent removes all content from a window or frame. For elements, it fires when the target element or any of its content has been removed.
1768 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1769 * @param {HtmlElement} t The target of the event.
1770 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1774 * Fires when an object/image is stopped from loading before completely loaded.
1775 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1776 * @param {HtmlElement} t The target of the event.
1777 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1781 * Fires when an object/image/frame cannot be loaded properly.
1782 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1783 * @param {HtmlElement} t The target of the event.
1784 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1788 * Fires when a document view is resized.
1789 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1790 * @param {HtmlElement} t The target of the event.
1791 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1795 * Fires when a document view is scrolled.
1796 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1797 * @param {HtmlElement} t The target of the event.
1798 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1804 * Fires when a user selects some text in a text field, including input and textarea.
1805 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1806 * @param {HtmlElement} t The target of the event.
1807 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1811 * Fires when a control loses the input focus and its value has been modified since gaining focus.
1812 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1813 * @param {HtmlElement} t The target of the event.
1814 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1818 * Fires when a form is submitted.
1819 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1820 * @param {HtmlElement} t The target of the event.
1821 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1825 * Fires when a form is reset.
1826 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1827 * @param {HtmlElement} t The target of the event.
1828 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1832 * Fires when an element receives focus either via the pointing device or by tab navigation.
1833 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1834 * @param {HtmlElement} t The target of the event.
1835 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1839 * Fires when an element loses focus either via the pointing device or by tabbing navigation.
1840 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1841 * @param {HtmlElement} t The target of the event.
1842 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1845 // User Interface events
1848 * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
1849 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1850 * @param {HtmlElement} t The target of the event.
1851 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1854 * @event DOMFocusOut
1855 * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
1856 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1857 * @param {HtmlElement} t The target of the event.
1858 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1861 * @event DOMActivate
1862 * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
1863 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1864 * @param {HtmlElement} t The target of the event.
1865 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1868 // DOM Mutation events
1870 * @event DOMSubtreeModified
1871 * Where supported. Fires when the subtree is modified.
1872 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1873 * @param {HtmlElement} t The target of the event.
1874 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1877 * @event DOMNodeInserted
1878 * Where supported. Fires when a node has been added as a child of another node.
1879 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1880 * @param {HtmlElement} t The target of the event.
1881 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1884 * @event DOMNodeRemoved
1885 * Where supported. Fires when a descendant node of the element is removed.
1886 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1887 * @param {HtmlElement} t The target of the event.
1888 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1891 * @event DOMNodeRemovedFromDocument
1892 * Where supported. Fires when a node is being removed from a document.
1893 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1894 * @param {HtmlElement} t The target of the event.
1895 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1898 * @event DOMNodeInsertedIntoDocument
1899 * Where supported. Fires when a node is being inserted into a document.
1900 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1901 * @param {HtmlElement} t The target of the event.
1902 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1905 * @event DOMAttrModified
1906 * Where supported. Fires when an attribute has been modified.
1907 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1908 * @param {HtmlElement} t The target of the event.
1909 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1912 * @event DOMCharacterDataModified
1913 * Where supported. Fires when the character data has been modified.
1914 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1915 * @param {HtmlElement} t The target of the event.
1916 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1920 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
1926 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
1927 * @param {String} selector The simple selector to test
1928 * @return {Boolean} True if this element matches the selector, else false
1930 is: function(simpleSelector) {
1931 return Ext.DomQuery.is(this.dom, simpleSelector);
1935 * Tries to focus the element. Any exceptions are caught and ignored.
1936 * @param {Number} defer (optional) Milliseconds to defer the focus
1937 * @return {Ext.core.Element} this
1939 focus: function(defer,
1943 dom = dom || me.dom;
1945 if (Number(defer)) {
1946 Ext.defer(me.focus, defer, null, [null, dom]);
1955 * Tries to blur the element. Any exceptions are caught and ignored.
1956 * @return {Ext.core.Element} this
1966 * Returns the value of the "value" attribute
1967 * @param {Boolean} asNumber true to parse the value as a number
1968 * @return {String/Number}
1970 getValue: function(asNumber) {
1971 var val = this.dom.value;
1972 return asNumber ? parseInt(val, 10) : val;
1976 * Appends an event handler to this element. The shorthand version {@link #on} is equivalent.
1977 * @param {String} eventName The name of event to handle.
1978 * @param {Function} fn The handler function the event invokes. This function is passed
1979 * the following parameters:<ul>
1980 * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
1981 * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
1982 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
1983 * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
1985 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1986 * <b>If omitted, defaults to this Element.</b>.
1987 * @param {Object} options (optional) An object containing handler configuration properties.
1988 * This may contain any of the following properties:<ul>
1989 * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1990 * <b>If omitted, defaults to this Element.</b></div></li>
1991 * <li><b>delegate</b> String: <div class="sub-desc">A simple selector to filter the target or look for a descendant of the target. See below for additional details.</div></li>
1992 * <li><b>stopEvent</b> Boolean: <div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
1993 * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
1994 * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
1995 * <li><b>normalized</b> Boolean: <div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
1996 * <li><b>target</b> Ext.core.Element: <div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
1997 * <li><b>delay</b> Number: <div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
1998 * <li><b>single</b> Boolean: <div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
1999 * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2000 * by the specified number of milliseconds. If the event fires again within that time, the original
2001 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2004 * <b>Combining Options</b><br>
2005 * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
2006 * addListener. The two are equivalent. Using the options argument, it is possible to combine different
2007 * types of listeners:<br>
2009 * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
2010 * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
2012 el.on('click', this.onClick, this, {
2017 });</code></pre></p>
2019 * <b>Attaching multiple handlers in 1 call</b><br>
2020 * The method also allows for a single argument to be passed which is a config object containing properties
2021 * which specify multiple handlers.</p>
2031 fn: this.onMouseOver,
2035 fn: this.onMouseOut,
2040 * Or a shorthand syntax:<br>
2041 * Code:<pre><code></p>
2043 'click' : this.onClick,
2044 'mouseover' : this.onMouseOver,
2045 'mouseout' : this.onMouseOut,
2049 * <p><b>delegate</b></p>
2050 * <p>This is a configuration option that you can pass along when registering a handler for
2051 * an event to assist with event delegation. Event delegation is a technique that is used to
2052 * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
2053 * for a container element as opposed to each element within a container. By setting this
2054 * configuration option to a simple selector, the target element will be filtered to look for
2055 * a descendant of the target.
2056 * For example:<pre><code>
2057 // using this markup:
2059 <p id='p1'>paragraph one</p>
2060 <p id='p2' class='clickable'>paragraph two</p>
2061 <p id='p3'>paragraph three</p>
2063 // utilize event delegation to registering just one handler on the container element:
2064 el = Ext.get('elId');
2069 console.info(t.id); // 'p2'
2073 // filter the target element to be a descendant with the class 'clickable'
2074 delegate: '.clickable'
2078 * @return {Ext.core.Element} this
2080 addListener: function(eventName, fn, scope, options) {
2081 Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
2086 * Removes an event handler from this element. The shorthand version {@link #un} is equivalent.
2087 * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
2088 * listener, the same scope must be specified here.
2091 el.removeListener('click', this.handlerFn);
2093 el.un('click', this.handlerFn);
2095 * @param {String} eventName The name of the event from which to remove the handler.
2096 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2097 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2098 * then this must refer to the same object.
2099 * @return {Ext.core.Element} this
2101 removeListener: function(eventName, fn, scope) {
2102 Ext.EventManager.un(this.dom, eventName, fn, scope || this);
2107 * Removes all previous added listeners from this element
2108 * @return {Ext.core.Element} this
2110 removeAllListeners: function() {
2111 Ext.EventManager.removeAll(this.dom);
2116 * Recursively removes all previous added listeners from this element and its children
2117 * @return {Ext.core.Element} this
2119 purgeAllListeners: function() {
2120 Ext.EventManager.purgeElement(this);
2125 * @private Test if size has a unit, otherwise appends the passed unit string, or the default for this Element.
2126 * @param size {Mixed} The size to set
2127 * @param units {String} The units to append to a numeric size value
2129 addUnits: function(size, units) {
2131 // Most common case first: Size is set to a number
2132 if (Ext.isNumber(size)) {
2133 return size + (units || this.defaultUnit || 'px');
2136 // Size set to a value which means "auto"
2137 if (size === "" || size == "auto" || size === undefined || size === null) {
2141 // Otherwise, warn if it's not a valid CSS measurement
2142 if (!unitPattern.test(size)) {
2144 if (Ext.isDefined(Ext.global.console)) {
2145 Ext.global.console.warn("Warning, size detected as NaN on Element.addUnits.");
2154 * Tests various css rules/browsers to determine if this element uses a border box
2157 isBorderBox: function() {
2158 return Ext.isBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
2162 * <p>Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}</p>
2164 remove: function() {
2170 Ext.removeNode(dom);
2175 * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
2176 * @param {Function} overFn The function to call when the mouse enters the Element.
2177 * @param {Function} outFn The function to call when the mouse leaves the Element.
2178 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
2179 * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
2180 * @return {Ext.core.Element} this
2182 hover: function(overFn, outFn, scope, options) {
2184 me.on('mouseenter', overFn, scope || me.dom, options);
2185 me.on('mouseleave', outFn, scope || me.dom, options);
2190 * Returns true if this element is an ancestor of the passed element
2191 * @param {HTMLElement/String} el The element to check
2192 * @return {Boolean} True if this element is an ancestor of el, else false
2194 contains: function(el) {
2195 return ! el ? false: Ext.core.Element.isAncestor(this.dom, el.dom ? el.dom: el);
2199 * Returns the value of a namespaced attribute from the element's underlying DOM node.
2200 * @param {String} namespace The namespace in which to look for the attribute
2201 * @param {String} name The attribute name
2202 * @return {String} The attribute value
2205 getAttributeNS: function(ns, name) {
2206 return this.getAttribute(name, ns);
2210 * Returns the value of an attribute from the element's underlying DOM node.
2211 * @param {String} name The attribute name
2212 * @param {String} namespace (optional) The namespace in which to look for the attribute
2213 * @return {String} The attribute value
2216 getAttribute: (Ext.isIE && !(Ext.isIE9 && document.documentMode === 9)) ?
2217 function(name, ns) {
2221 type = typeof d[ns + ":" + name];
2222 if (type != 'undefined' && type != 'unknown') {
2223 return d[ns + ":" + name] || null;
2227 if (name === "for") {
2230 return d[name] || null;
2231 }: function(name, ns) {
2234 return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
2236 return d.getAttribute(name) || d[name] || null;
2240 * Update the innerHTML of this element
2241 * @param {String} html The new HTML
2242 * @return {Ext.core.Element} this
2244 update: function(html) {
2246 this.dom.innerHTML = html;
2252 var ep = El.prototype;
2254 El.addMethods = function(o) {
2259 * Appends an event handler (shorthand for {@link #addListener}).
2260 * @param {String} eventName The name of event to handle.
2261 * @param {Function} fn The handler function the event invokes.
2262 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
2263 * @param {Object} options (optional) An object containing standard {@link #addListener} options
2264 * @member Ext.core.Element
2267 ep.on = ep.addListener;
2270 * Removes an event handler from this element (see {@link #removeListener} for additional notes).
2271 * @param {String} eventName The name of the event from which to remove the handler.
2272 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2273 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2274 * then this must refer to the same object.
2275 * @return {Ext.core.Element} this
2276 * @member Ext.core.Element
2279 ep.un = ep.removeListener;
2282 * Removes all previous added listeners from this element
2283 * @return {Ext.core.Element} this
2284 * @member Ext.core.Element
2285 * @method clearListeners
2287 ep.clearListeners = ep.removeAllListeners;
2290 * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}.
2291 * Alias to {@link #remove}.
2292 * @member Ext.core.Element
2295 ep.destroy = ep.remove;
2298 * true to automatically adjust width and height settings for box-model issues (default to true)
2300 ep.autoBoxAdjust = true;
2303 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
2307 * Retrieves Ext.core.Element objects.
2308 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
2309 * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
2310 * its ID, use {@link Ext.ComponentManager#get}.</p>
2311 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
2312 * object was recreated with the same id via AJAX or DOM.</p>
2313 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
2314 * @return {Element} The Element object (or null if no matching element was found)
2316 * @member Ext.core.Element
2319 El.get = function(el) {
2326 if (typeof el == "string") {
2328 if (! (elm = DOC.getElementById(el))) {
2331 if (EC[el] && EC[el].el) {
2335 ex = El.addToCache(new El(elm));
2338 } else if (el.tagName) {
2340 if (! (id = el.id)) {
2343 if (EC[id] && EC[id].el) {
2347 ex = El.addToCache(new El(el));
2350 } else if (el instanceof El) {
2352 // refresh dom element in case no longer valid,
2353 // catch case where it hasn't been appended
2354 // If an el instance is passed, don't pass to getElementById without some kind of id
2355 if (Ext.isIE && (el.id == undefined || el.id == '')) {
2358 el.dom = DOC.getElementById(el.id) || el.dom;
2362 } else if (el.isComposite) {
2364 } else if (Ext.isArray(el)) {
2365 return El.select(el);
2366 } else if (el == DOC) {
2367 // create a bogus element object representing the document object
2369 var f = function() {};
2370 f.prototype = El.prototype;
2379 El.addToCache = function(el, id) {
2391 // private method for getting and setting element data
2392 El.data = function(el, key, value) {
2397 var c = EC[el.id].data;
2398 if (arguments.length == 2) {
2401 return (c[key] = value);
2406 // Garbage collection - uncache elements/purge listeners on orphaned elements
2407 // so we don't hold a reference and cause the browser to retain them
2408 function garbageCollect() {
2409 if (!Ext.enableGarbageCollector) {
2410 clearInterval(El.collectorThreadId);
2418 if (!EC.hasOwnProperty(eid)) {
2422 if (o.skipGarbageCollection) {
2427 // -------------------------------------------------------
2428 // Determining what is garbage:
2429 // -------------------------------------------------------
2431 // dom node is null, definitely garbage
2432 // -------------------------------------------------------
2434 // no parentNode == direct orphan, definitely garbage
2435 // -------------------------------------------------------
2436 // !d.offsetParent && !document.getElementById(eid)
2437 // display none elements have no offsetParent so we will
2438 // also try to look it up by it's id. However, check
2439 // offsetParent first so we don't do unneeded lookups.
2440 // This enables collection of elements that are not orphans
2441 // directly, but somewhere up the line they have an orphan
2443 // -------------------------------------------------------
2444 if (!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))) {
2445 if (d && Ext.enableListenerCollection) {
2446 Ext.EventManager.removeAll(d);
2451 // Cleanup IE Object leaks
2455 if (!EC.hasOwnProperty(eid)) {
2464 El.collectorThreadId = setInterval(garbageCollect, 30000);
2466 var flyFn = function() {};
2467 flyFn.prototype = El.prototype;
2470 El.Flyweight = function(dom) {
2474 El.Flyweight.prototype = new flyFn();
2475 El.Flyweight.prototype.isFlyweight = true;
2476 El._flyweights = {};
2479 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
2480 * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
2481 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
2482 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
2483 * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
2484 * @param {String/HTMLElement} el The dom node or id
2485 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
2486 * (e.g. internally Ext uses "_global")
2487 * @return {Element} The shared Element object (or null if no matching element was found)
2488 * @member Ext.core.Element
2491 El.fly = function(el, named) {
2493 named = named || '_global';
2494 el = Ext.getDom(el);
2496 (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
2497 ret = El._flyweights[named];
2503 * Retrieves Ext.core.Element objects.
2504 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
2505 * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
2506 * its ID, use {@link Ext.ComponentManager#get}.</p>
2507 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
2508 * object was recreated with the same id via AJAX or DOM.</p>
2509 * Shorthand of {@link Ext.core.Element#get}
2510 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
2511 * @return {Element} The Element object (or null if no matching element was found)
2518 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
2519 * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
2520 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
2521 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
2522 * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
2523 * @param {String/HTMLElement} el The dom node or id
2524 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
2525 * (e.g. internally Ext uses "_global")
2526 * @return {Element} The shared Element object (or null if no matching element was found)
2532 // speedy lookup for elements never to box adjust
2533 var noBoxAdjust = Ext.isStrict ? {
2540 if (Ext.isIE || Ext.isGecko) {
2541 noBoxAdjust['button'] = 1;
2546 * @class Ext.core.Element
2548 Ext.core.Element.addMethods({
2550 * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
2551 * @param {String} selector The simple selector to test
2552 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
2553 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
2554 * @return {HTMLElement} The matching DOM node (or null if no match was found)
2556 findParent : function(simpleSelector, maxDepth, returnEl) {
2562 maxDepth = maxDepth || 50;
2563 if (isNaN(maxDepth)) {
2564 stopEl = Ext.getDom(maxDepth);
2565 maxDepth = Number.MAX_VALUE;
2567 while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
2568 if (Ext.DomQuery.is(p, simpleSelector)) {
2569 return returnEl ? Ext.get(p) : p;
2578 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
2579 * @param {String} selector The simple selector to test
2580 * @param {Number/Mixed} maxDepth (optional) The max depth to
2581 search as a number or element (defaults to 10 || document.body)
2582 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
2583 * @return {HTMLElement} The matching DOM node (or null if no match was found)
2585 findParentNode : function(simpleSelector, maxDepth, returnEl) {
2586 var p = Ext.fly(this.dom.parentNode, '_internal');
2587 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
2591 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
2592 * This is a shortcut for findParentNode() that always returns an Ext.core.Element.
2593 * @param {String} selector The simple selector to test
2594 * @param {Number/Mixed} maxDepth (optional) The max depth to
2595 search as a number or element (defaults to 10 || document.body)
2596 * @return {Ext.core.Element} The matching DOM node (or null if no match was found)
2598 up : function(simpleSelector, maxDepth) {
2599 return this.findParentNode(simpleSelector, maxDepth, true);
2603 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
2604 * @param {String} selector The CSS selector
2605 * @return {CompositeElement/CompositeElement} The composite element
2607 select : function(selector) {
2608 return Ext.core.Element.select(selector, false, this.dom);
2612 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
2613 * @param {String} selector The CSS selector
2614 * @return {Array} An array of the matched nodes
2616 query : function(selector) {
2617 return Ext.DomQuery.select(selector, this.dom);
2621 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
2622 * @param {String} selector The CSS selector
2623 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
2624 * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
2626 down : function(selector, returnDom) {
2627 var n = Ext.DomQuery.selectNode(selector, this.dom);
2628 return returnDom ? n : Ext.get(n);
2632 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
2633 * @param {String} selector The CSS selector
2634 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
2635 * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
2637 child : function(selector, returnDom) {
2641 id = Ext.get(me).id;
2643 id = id.replace(/[\.:]/g, "\\$0");
2644 node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
2645 return returnDom ? node : Ext.get(node);
2649 * Gets the parent node for this element, optionally chaining up trying to match a selector
2650 * @param {String} selector (optional) Find a parent node that matches the passed simple selector
2651 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2652 * @return {Ext.core.Element/HTMLElement} The parent node or null
2654 parent : function(selector, returnDom) {
2655 return this.matchNode('parentNode', 'parentNode', selector, returnDom);
2659 * Gets the next sibling, skipping text nodes
2660 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
2661 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2662 * @return {Ext.core.Element/HTMLElement} The next sibling or null
2664 next : function(selector, returnDom) {
2665 return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
2669 * Gets the previous sibling, skipping text nodes
2670 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
2671 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2672 * @return {Ext.core.Element/HTMLElement} The previous sibling or null
2674 prev : function(selector, returnDom) {
2675 return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
2680 * Gets the first child, skipping text nodes
2681 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
2682 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2683 * @return {Ext.core.Element/HTMLElement} The first child or null
2685 first : function(selector, returnDom) {
2686 return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
2690 * Gets the last child, skipping text nodes
2691 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
2692 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2693 * @return {Ext.core.Element/HTMLElement} The last child or null
2695 last : function(selector, returnDom) {
2696 return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
2699 matchNode : function(dir, start, selector, returnDom) {
2704 var n = this.dom[start];
2706 if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
2707 return !returnDom ? Ext.get(n) : n;
2716 * @class Ext.core.Element
2718 Ext.core.Element.addMethods({
2720 * Appends the passed element(s) to this element
2721 * @param {String/HTMLElement/Array/Element/CompositeElement} el
2722 * @return {Ext.core.Element} this
2724 appendChild : function(el) {
2725 return Ext.get(el).appendTo(this);
2729 * Appends this element to the passed element
2730 * @param {Mixed} el The new parent element
2731 * @return {Ext.core.Element} this
2733 appendTo : function(el) {
2734 Ext.getDom(el).appendChild(this.dom);
2739 * Inserts this element before the passed element in the DOM
2740 * @param {Mixed} el The element before which this element will be inserted
2741 * @return {Ext.core.Element} this
2743 insertBefore : function(el) {
2744 el = Ext.getDom(el);
2745 el.parentNode.insertBefore(this.dom, el);
2750 * Inserts this element after the passed element in the DOM
2751 * @param {Mixed} el The element to insert after
2752 * @return {Ext.core.Element} this
2754 insertAfter : function(el) {
2755 el = Ext.getDom(el);
2756 el.parentNode.insertBefore(this.dom, el.nextSibling);
2761 * Inserts (or creates) an element (or DomHelper config) as the first child of this element
2762 * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
2763 * @return {Ext.core.Element} The new child
2765 insertFirst : function(el, returnDom) {
2767 if (el.nodeType || el.dom || typeof el == 'string') { // element
2768 el = Ext.getDom(el);
2769 this.dom.insertBefore(el, this.dom.firstChild);
2770 return !returnDom ? Ext.get(el) : el;
2773 return this.createChild(el, this.dom.firstChild, returnDom);
2778 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
2779 * @param {Mixed/Object/Array} el The id, element to insert or a DomHelper config to create and insert *or* an array of any of those.
2780 * @param {String} where (optional) 'before' or 'after' defaults to before
2781 * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.core.Element
2782 * @return {Ext.core.Element} The inserted Element. If an array is passed, the last inserted element is returned.
2784 insertSibling: function(el, where, returnDom){
2786 isAfter = (where || 'before').toLowerCase() == 'after',
2789 if(Ext.isArray(el)){
2791 Ext.each(el, function(e) {
2792 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
2802 if(el.nodeType || el.dom){
2803 rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
2808 if (isAfter && !me.dom.nextSibling) {
2809 rt = Ext.core.DomHelper.append(me.dom.parentNode, el, !returnDom);
2811 rt = Ext.core.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
2818 * Replaces the passed element with this element
2819 * @param {Mixed} el The element to replace
2820 * @return {Ext.core.Element} this
2822 replace : function(el) {
2824 this.insertBefore(el);
2830 * Replaces this element with the passed element
2831 * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
2832 * @return {Ext.core.Element} this
2834 replaceWith: function(el){
2837 if(el.nodeType || el.dom || typeof el == 'string'){
2839 me.dom.parentNode.insertBefore(el, me.dom);
2841 el = Ext.core.DomHelper.insertBefore(me.dom, el);
2844 delete Ext.cache[me.id];
2845 Ext.removeNode(me.dom);
2846 me.id = Ext.id(me.dom = el);
2847 Ext.core.Element.addToCache(me.isFlyweight ? new Ext.core.Element(me.dom) : me);
2852 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
2853 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
2854 * automatically generated with the specified attributes.
2855 * @param {HTMLElement} insertBefore (optional) a child element of this element
2856 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
2857 * @return {Ext.core.Element} The new child element
2859 createChild : function(config, insertBefore, returnDom) {
2860 config = config || {tag:'div'};
2862 return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
2865 return Ext.core.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config, returnDom !== true);
2870 * Creates and wraps this element with another element
2871 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
2872 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.core.Element
2873 * @return {HTMLElement/Element} The newly created wrapper element
2875 wrap : function(config, returnDom) {
2876 var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom),
2877 d = newEl.dom || newEl;
2879 d.appendChild(this.dom);
2884 * Inserts an html fragment into this element
2885 * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
2886 * @param {String} html The HTML fragment
2887 * @param {Boolean} returnEl (optional) True to return an Ext.core.Element (defaults to false)
2888 * @return {HTMLElement/Ext.core.Element} The inserted node (or nearest related if more than 1 inserted)
2890 insertHtml : function(where, html, returnEl) {
2891 var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
2892 return returnEl ? Ext.get(el) : el;
2897 * @class Ext.core.Element
2900 Ext.core.Element.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>';
2901 // local style camelizing for speed
2902 var supports = Ext.supports,
2903 view = document.defaultView,
2904 opacityRe = /alpha\(opacity=(.*)\)/i,
2905 trimRe = /^\s+|\s+$/g,
2908 adjustDirect2DTableRe = /table-row|table-.*-group/,
2909 INTERNAL = '_internal',
2910 PADDING = 'padding',
2920 ISCLIPPED = 'isClipped',
2921 OVERFLOW = 'overflow',
2922 OVERFLOWX = 'overflow-x',
2923 OVERFLOWY = 'overflow-y',
2924 ORIGINALCLIP = 'originalClip',
2925 // special markup used throughout Ext when box wrapping elements
2926 borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
2927 paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
2928 margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
2929 data = Ext.core.Element.data;
2931 Ext.override(Ext.core.Element, {
2934 * TODO: Look at this
2936 // private ==> used by Fx
2937 adjustWidth : function(width) {
2939 isNum = (typeof width == 'number');
2941 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
2942 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
2944 return (isNum && width < 0) ? 0 : width;
2947 // private ==> used by Fx
2948 adjustHeight : function(height) {
2950 isNum = (typeof height == "number");
2952 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
2953 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
2955 return (isNum && height < 0) ? 0 : height;
2960 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
2961 * @param {String/Array} className The CSS classes to add separated by space, or an array of classes
2962 * @return {Ext.core.Element} this
2964 addCls : function(className){
2967 space = ((me.dom.className.replace(trimRe, '') == '') ? "" : " "),
2969 if (className === undefined) {
2972 // Separate case is for speed
2973 if (Object.prototype.toString.call(className) !== '[object Array]') {
2974 if (typeof className === 'string') {
2975 className = className.replace(trimRe, '').split(spacesRe);
2976 if (className.length === 1) {
2977 className = className[0];
2978 if (!me.hasCls(className)) {
2979 me.dom.className += space + className;
2982 this.addCls(className);
2986 for (i = 0, len = className.length; i < len; i++) {
2988 if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
2993 me.dom.className += space + cls.join(" ");
3000 * Removes one or more CSS classes from the element.
3001 * @param {String/Array} className The CSS classes to remove separated by space, or an array of classes
3002 * @return {Ext.core.Element} this
3004 removeCls : function(className){
3006 i, idx, len, cls, elClasses;
3007 if (className === undefined) {
3010 if (Object.prototype.toString.call(className) !== '[object Array]') {
3011 className = className.replace(trimRe, '').split(spacesRe);
3013 if (me.dom && me.dom.className) {
3014 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
3015 for (i = 0, len = className.length; i < len; i++) {
3017 if (typeof cls == 'string') {
3018 cls = cls.replace(trimRe, '');
3019 idx = Ext.Array.indexOf(elClasses, cls);
3021 Ext.Array.erase(elClasses, idx, 1);
3025 me.dom.className = elClasses.join(" ");
3031 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
3032 * @param {String/Array} className The CSS class to add, or an array of classes
3033 * @return {Ext.core.Element} this
3035 radioCls : function(className){
3036 var cn = this.dom.parentNode.childNodes,
3038 className = Ext.isArray(className) ? className : [className];
3039 for (i = 0, len = cn.length; i < len; i++) {
3041 if (v && v.nodeType == 1) {
3042 Ext.fly(v, '_internal').removeCls(className);
3045 return this.addCls(className);
3049 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
3050 * @param {String} className The CSS class to toggle
3051 * @return {Ext.core.Element} this
3054 toggleCls : Ext.supports.ClassList ?
3055 function(className) {
3056 this.dom.classList.toggle(Ext.String.trim(className));
3059 function(className) {
3060 return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
3064 * Checks if the specified CSS class exists on this element's DOM node.
3065 * @param {String} className The CSS class to check for
3066 * @return {Boolean} True if the class exists, else false
3069 hasCls : Ext.supports.ClassList ?
3070 function(className) {
3074 className = className.split(spacesRe);
3075 var ln = className.length,
3077 for (; i < ln; i++) {
3078 if (className[i] && this.dom.classList.contains(className[i])) {
3084 function(className){
3085 return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
3089 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
3090 * @param {String} oldClassName The CSS class to replace
3091 * @param {String} newClassName The replacement CSS class
3092 * @return {Ext.core.Element} this
3094 replaceCls : function(oldClassName, newClassName){
3095 return this.removeCls(oldClassName).addCls(newClassName);
3098 isStyle : function(style, val) {
3099 return this.getStyle(style) == val;
3103 * Normalizes currentStyle and computedStyle.
3104 * @param {String} property The style property whose value is returned.
3105 * @return {String} The current value of the style property for this element.
3108 getStyle : function(){
3109 return view && view.getComputedStyle ?
3112 v, cs, out, display, cleaner;
3117 prop = Ext.core.Element.normalize(prop);
3118 out = (v = el.style[prop]) ? v :
3119 (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
3121 // Ignore cases when the margin is correctly reported as 0, the bug only shows
3123 if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){
3124 cleaner = Ext.core.Element.getRightMarginFixCleaner(el);
3125 display = this.getStyle('display');
3126 el.style.display = 'inline-block';
3127 out = view.getComputedStyle(el, '').marginRight;
3128 el.style.display = display;
3132 if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){
3133 out = 'transparent';
3141 if (el == document) {
3145 if (prop == 'opacity') {
3146 if (el.style.filter.match) {
3147 m = el.style.filter.match(opacityRe);
3149 var fv = parseFloat(m[1]);
3151 return fv ? fv / 100 : 0;
3157 prop = Ext.core.Element.normalize(prop);
3158 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
3163 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
3164 * are convert to standard 6 digit hex color.
3165 * @param {String} attr The css attribute
3166 * @param {String} defaultValue The default value to use when a valid color isn't found
3167 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
3170 getColor : function(attr, defaultValue, prefix){
3171 var v = this.getStyle(attr),
3172 color = prefix || prefix === '' ? prefix : '#',
3175 if(!v || (/transparent|inherit/.test(v))) {
3176 return defaultValue;
3179 Ext.each(v.slice(4, v.length -1).split(','), function(s){
3180 h = parseInt(s, 10);
3181 color += (h < 16 ? '0' : '') + h.toString(16);
3184 v = v.replace('#', '');
3185 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
3187 return(color.length > 5 ? color.toLowerCase() : defaultValue);
3191 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
3192 * @param {String/Object} property The style property to be set, or an object of multiple styles.
3193 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
3194 * @return {Ext.core.Element} this
3196 setStyle : function(prop, value){
3203 if (typeof prop === 'string') {
3208 for (style in prop) {
3209 if (prop.hasOwnProperty(style)) {
3210 value = Ext.value(prop[style], '');
3211 if (style == 'opacity') {
3212 me.setOpacity(value);
3215 me.dom.style[Ext.core.Element.normalize(style)] = value;
3223 * Set the opacity of the element
3224 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
3225 * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
3226 * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
3227 * @return {Ext.core.Element} this
3229 setOpacity: function(opacity, animate) {
3239 style = me.dom.style;
3241 if (!animate || !me.anim) {
3242 if (!Ext.supports.Opacity) {
3243 opacity = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')': '';
3244 val = style.filter.replace(opacityRe, '').replace(trimRe, '');
3247 style.filter = val + (val.length > 0 ? ' ': '') + opacity;
3250 style.opacity = opacity;
3254 if (!Ext.isObject(animate)) {
3260 me.animate(Ext.applyIf({
3272 * Clears any opacity settings from this element. Required in some cases for IE.
3273 * @return {Ext.core.Element} this
3275 clearOpacity : function(){
3276 var style = this.dom.style;
3277 if(!Ext.supports.Opacity){
3278 if(!Ext.isEmpty(style.filter)){
3279 style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
3282 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
3289 * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel.
3290 * @return {Number} 0 or 1
3292 adjustDirect2DDimension: function(dimension) {
3295 display = me.getStyle('display'),
3296 inlineDisplay = dom.style['display'],
3297 inlinePosition = dom.style['position'],
3298 originIndex = dimension === 'width' ? 0 : 1,
3301 if (display === 'inline') {
3302 dom.style['display'] = 'inline-block';
3305 dom.style['position'] = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';
3307 // floating will contain digits that appears after the decimal point
3308 // if height or width are set to auto we fallback to msTransformOrigin calculation
3309 floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
3311 dom.style['position'] = inlinePosition;
3313 if (display === 'inline') {
3314 dom.style['display'] = inlineDisplay;
3321 * Returns the offset height of the element
3322 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
3323 * @return {Number} The element's height
3325 getHeight: function(contentHeight, preciseHeight) {
3328 hidden = Ext.isIE && me.isStyle('display', 'none'),
3329 height, overflow, style, floating;
3331 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3332 // We will put the overflow back to it's original value when we are done measuring.
3333 if (Ext.isIEQuirks) {
3335 overflow = style.overflow;
3336 me.setStyle({ overflow: 'hidden'});
3339 height = dom.offsetHeight;
3341 height = MATH.max(height, hidden ? 0 : dom.clientHeight) || 0;
3343 // IE9 Direct2D dimension rounding bug
3344 if (!hidden && Ext.supports.Direct2DBug) {
3345 floating = me.adjustDirect2DDimension('height');
3346 if (preciseHeight) {
3349 else if (floating > 0 && floating < 0.5) {
3354 if (contentHeight) {
3355 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
3358 if (Ext.isIEQuirks) {
3359 me.setStyle({ overflow: overflow});
3369 * Returns the offset width of the element
3370 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
3371 * @return {Number} The element's width
3373 getWidth: function(contentWidth, preciseWidth) {
3376 hidden = Ext.isIE && me.isStyle('display', 'none'),
3377 rect, width, overflow, style, floating, parentPosition;
3379 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3380 // We will put the overflow back to it's original value when we are done measuring.
3381 if (Ext.isIEQuirks) {
3383 overflow = style.overflow;
3384 me.setStyle({overflow: 'hidden'});
3387 // Fix Opera 10.5x width calculation issues
3388 if (Ext.isOpera10_5) {
3389 if (dom.parentNode.currentStyle.position === 'relative') {
3390 parentPosition = dom.parentNode.style.position;
3391 dom.parentNode.style.position = 'static';
3392 width = dom.offsetWidth;
3393 dom.parentNode.style.position = parentPosition;
3395 width = Math.max(width || 0, dom.offsetWidth);
3397 // Gecko will in some cases report an offsetWidth that is actually less than the width of the
3398 // text contents, because it measures fonts with sub-pixel precision but rounds the calculated
3399 // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise
3400 // subpixel measurements so we can force them to always be rounded up. See
3401 // https://bugzilla.mozilla.org/show_bug.cgi?id=458617
3402 } else if (Ext.supports.BoundingClientRect) {
3403 rect = dom.getBoundingClientRect();
3404 width = rect.right - rect.left;
3405 width = preciseWidth ? width : Math.ceil(width);
3407 width = dom.offsetWidth;
3410 width = MATH.max(width, hidden ? 0 : dom.clientWidth) || 0;
3412 // IE9 Direct2D dimension rounding bug
3413 if (!hidden && Ext.supports.Direct2DBug) {
3414 floating = me.adjustDirect2DDimension('width');
3418 else if (floating > 0 && floating < 0.5) {
3424 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
3427 if (Ext.isIEQuirks) {
3428 me.setStyle({ overflow: overflow});
3438 * Set the width of this Element.
3439 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
3440 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3441 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
3443 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3444 * @return {Ext.core.Element} this
3446 setWidth : function(width, animate){
3448 width = me.adjustWidth(width);
3449 if (!animate || !me.anim) {
3450 me.dom.style.width = me.addUnits(width);
3453 if (!Ext.isObject(animate)) {
3456 me.animate(Ext.applyIf({
3466 * Set the height of this Element.
3468 // change the height to 200px and animate with default configuration
3469 Ext.fly('elementId').setHeight(200, true);
3471 // change the height to 150px and animate with a custom configuration
3472 Ext.fly('elId').setHeight(150, {
3473 duration : .5, // animation will have a duration of .5 seconds
3474 // will change the content to "finished"
3475 callback: function(){ this.{@link #update}("finished"); }
3478 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
3479 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
3480 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
3482 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3483 * @return {Ext.core.Element} this
3485 setHeight : function(height, animate){
3487 height = me.adjustHeight(height);
3488 if (!animate || !me.anim) {
3489 me.dom.style.height = me.addUnits(height);
3492 if (!Ext.isObject(animate)) {
3495 me.animate(Ext.applyIf({
3505 * Gets the width of the border(s) for the specified side(s)
3506 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
3507 * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
3508 * @return {Number} The width of the sides passed added together
3510 getBorderWidth : function(side){
3511 return this.addStyles(side, borders);
3515 * Gets the width of the padding(s) for the specified side(s)
3516 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
3517 * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
3518 * @return {Number} The padding of the sides passed added together
3520 getPadding : function(side){
3521 return this.addStyles(side, paddings);
3525 * Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
3526 * @return {Ext.core.Element} this
3532 if(!data(dom, ISCLIPPED)){
3533 data(dom, ISCLIPPED, true);
3534 data(dom, ORIGINALCLIP, {
3535 o: me.getStyle(OVERFLOW),
3536 x: me.getStyle(OVERFLOWX),
3537 y: me.getStyle(OVERFLOWY)
3539 me.setStyle(OVERFLOW, HIDDEN);
3540 me.setStyle(OVERFLOWX, HIDDEN);
3541 me.setStyle(OVERFLOWY, HIDDEN);
3547 * Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
3548 * @return {Ext.core.Element} this
3550 unclip : function(){
3555 if(data(dom, ISCLIPPED)){
3556 data(dom, ISCLIPPED, false);
3557 clip = data(dom, ORIGINALCLIP);
3559 me.setStyle(OVERFLOW, o.o);
3562 me.setStyle(OVERFLOWX, o.x);
3565 me.setStyle(OVERFLOWY, o.y);
3572 addStyles : function(sides, styles){
3574 sidesArr = sides.match(wordsRe),
3576 len = sidesArr.length,
3578 for (; i < len; i++) {
3580 size = side && parseInt(this.getStyle(styles[side]), 10);
3582 totalSize += MATH.abs(size);
3591 * More flexible version of {@link #setStyle} for setting style properties.
3592 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
3593 * a function which returns such a specification.
3594 * @return {Ext.core.Element} this
3596 applyStyles : function(style){
3597 Ext.core.DomHelper.applyStyles(this.dom, style);
3602 * Returns an object with properties matching the styles requested.
3603 * For example, el.getStyles('color', 'font-size', 'width') might return
3604 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
3605 * @param {String} style1 A style name
3606 * @param {String} style2 A style name
3607 * @param {String} etc.
3608 * @return {Object} The style object
3610 getStyles : function(){
3612 len = arguments.length,
3615 for(; i < len; ++i) {
3616 style = arguments[i];
3617 styles[style] = this.getStyle(style);
3623 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
3624 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
3625 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
3626 * {@link Ext.panel.Panel} when <tt>{@link Ext.panel.Panel#frame frame=true}</tt>, {@link Ext.window.Window}). The markup
3627 * is of this form:</p>
3629 Ext.core.Element.boxMarkup =
3630 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
3631 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
3632 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
3634 * <p>Example usage:</p>
3637 Ext.get("foo").boxWrap();
3639 // You can also add a custom class and use CSS inheritance rules to customize the box look.
3640 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
3641 // for how to create a custom box wrap style.
3642 Ext.get("foo").boxWrap().addCls("x-box-blue");
3644 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
3645 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
3646 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
3647 * also supply all of the necessary rules.
3648 * @return {Ext.core.Element} The outermost wrapping element of the created box structure.
3650 boxWrap : function(cls){
3651 cls = cls || Ext.baseCSSPrefix + 'box';
3652 var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(Ext.core.Element.boxMarkup, cls) + "</div>"));
3653 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
3658 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
3659 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
3660 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3661 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
3662 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
3664 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
3665 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3666 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
3668 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3669 * @return {Ext.core.Element} this
3671 setSize : function(width, height, animate){
3673 if (Ext.isObject(width)) { // in case of object from getSize()
3675 height = width.height;
3676 width = width.width;
3678 width = me.adjustWidth(width);
3679 height = me.adjustHeight(height);
3680 if(!animate || !me.anim){
3681 me.dom.style.width = me.addUnits(width);
3682 me.dom.style.height = me.addUnits(height);
3685 if (animate === true) {
3688 me.animate(Ext.applyIf({
3699 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
3700 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
3701 * if a height has not been set using CSS.
3704 getComputedHeight : function(){
3706 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
3708 h = parseFloat(me.getStyle('height')) || 0;
3709 if(!me.isBorderBox()){
3710 h += me.getFrameWidth('tb');
3717 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
3718 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
3719 * if a width has not been set using CSS.
3722 getComputedWidth : function(){
3724 w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);
3727 w = parseFloat(me.getStyle('width')) || 0;
3728 if(!me.isBorderBox()){
3729 w += me.getFrameWidth('lr');
3736 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
3737 for more information about the sides.
3738 * @param {String} sides
3741 getFrameWidth : function(sides, onlyContentBox){
3742 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
3746 * Sets up event handlers to add and remove a css class when the mouse is over this element
3747 * @param {String} className
3748 * @return {Ext.core.Element} this
3750 addClsOnOver : function(className){
3754 Ext.fly(dom, INTERNAL).addCls(className);
3757 Ext.fly(dom, INTERNAL).removeCls(className);
3764 * Sets up event handlers to add and remove a css class when this element has the focus
3765 * @param {String} className
3766 * @return {Ext.core.Element} this
3768 addClsOnFocus : function(className){
3771 me.on("focus", function(){
3772 Ext.fly(dom, INTERNAL).addCls(className);
3774 me.on("blur", function(){
3775 Ext.fly(dom, INTERNAL).removeCls(className);
3781 * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
3782 * @param {String} className
3783 * @return {Ext.core.Element} this
3785 addClsOnClick : function(className){
3787 this.on("mousedown", function(){
3788 Ext.fly(dom, INTERNAL).addCls(className);
3789 var d = Ext.getDoc(),
3791 Ext.fly(dom, INTERNAL).removeCls(className);
3792 d.removeListener("mouseup", fn);
3794 d.on("mouseup", fn);
3800 * <p>Returns the dimensions of the element available to lay content out in.<p>
3801 * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
3802 * example:<pre><code>
3803 var vpSize = Ext.getBody().getViewSize();
3805 // all Windows created afterwards will have a default value of 90% height and 95% width
3806 Ext.Window.override({
3807 width: vpSize.width * 0.9,
3808 height: vpSize.height * 0.95
3810 // To handle window resizing you would have to hook onto onWindowResize.
3813 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
3814 * To obtain the size including scrollbars, use getStyleSize
3816 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
3819 getViewSize : function(){
3822 isDoc = (dom == Ext.getDoc().dom || dom == Ext.getBody().dom),
3823 style, overflow, ret;
3825 // If the body, use static methods
3828 width : Ext.core.Element.getViewWidth(),
3829 height : Ext.core.Element.getViewHeight()
3832 // Else use clientHeight/clientWidth
3835 // IE 6 & IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3836 // We will put the overflow back to it's original value when we are done measuring.
3837 if (Ext.isIE6 || Ext.isIEQuirks) {
3839 overflow = style.overflow;
3840 me.setStyle({ overflow: 'hidden'});
3843 width : dom.clientWidth,
3844 height : dom.clientHeight
3846 if (Ext.isIE6 || Ext.isIEQuirks) {
3847 me.setStyle({ overflow: overflow });
3854 * <p>Returns the dimensions of the element available to lay content out in.<p>
3856 * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
3857 * To obtain the size excluding scrollbars, use getViewSize
3859 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
3862 getStyleSize : function(){
3866 isDoc = (d == doc || d == doc.body),
3870 // If the body, use static methods
3873 width : Ext.core.Element.getViewWidth(),
3874 height : Ext.core.Element.getViewHeight()
3877 // Use Styles if they are set
3878 if(s.width && s.width != 'auto'){
3879 w = parseFloat(s.width);
3880 if(me.isBorderBox()){
3881 w -= me.getFrameWidth('lr');
3884 // Use Styles if they are set
3885 if(s.height && s.height != 'auto'){
3886 h = parseFloat(s.height);
3887 if(me.isBorderBox()){
3888 h -= me.getFrameWidth('tb');
3891 // Use getWidth/getHeight if style not set.
3892 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
3896 * Returns the size of the element.
3897 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
3898 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
3900 getSize : function(contentSize){
3901 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
3905 * Forces the browser to repaint this element
3906 * @return {Ext.core.Element} this
3908 repaint : function(){
3910 this.addCls(Ext.baseCSSPrefix + 'repaint');
3911 setTimeout(function(){
3912 Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
3918 * Disables text selection for this element (normalized across browsers)
3919 * @return {Ext.core.Element} this
3921 unselectable : function(){
3923 me.dom.unselectable = "on";
3925 me.swallowEvent("selectstart", true);
3926 me.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
3927 me.addCls(Ext.baseCSSPrefix + 'unselectable');
3933 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
3934 * then it returns the calculated width of the sides (see getPadding)
3935 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
3936 * @return {Object/Number}
3938 getMargin : function(side){
3940 hash = {t:"top", l:"left", r:"right", b: "bottom"},
3945 for (key in me.margins){
3946 o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
3950 return me.addStyles.call(me, side, me.margins);
3956 * @class Ext.core.Element
3959 * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
3963 Ext.core.Element.VISIBILITY = 1;
3965 * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
3969 Ext.core.Element.DISPLAY = 2;
3972 * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen)
3977 Ext.core.Element.OFFSETS = 3;
3980 Ext.core.Element.ASCLASS = 4;
3983 * Defaults to 'x-hide-nosize'
3987 Ext.core.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize';
3989 Ext.core.Element.addMethods(function(){
3990 var El = Ext.core.Element,
3991 OPACITY = "opacity",
3992 VISIBILITY = "visibility",
3993 DISPLAY = "display",
3995 OFFSETS = "offsets",
3996 ASCLASS = "asclass",
3999 ORIGINALDISPLAY = 'originalDisplay',
4000 VISMODE = 'visibilityMode',
4001 ISVISIBLE = 'isVisible',
4003 getDisplay = function(dom){
4004 var d = data(dom, ORIGINALDISPLAY);
4005 if(d === undefined){
4006 data(dom, ORIGINALDISPLAY, d = '');
4010 getVisMode = function(dom){
4011 var m = data(dom, VISMODE);
4012 if(m === undefined){
4013 data(dom, VISMODE, m = 1);
4020 * The element's default display mode (defaults to "")
4023 originalDisplay : "",
4027 * Sets the element's visibility mode. When setVisible() is called it
4028 * will use this to determine whether to set the visibility or the display property.
4029 * @param {Number} visMode Ext.core.Element.VISIBILITY or Ext.core.Element.DISPLAY
4030 * @return {Ext.core.Element} this
4032 setVisibilityMode : function(visMode){
4033 data(this.dom, VISMODE, visMode);
4038 * Checks whether the element is currently visible using both visibility and display properties.
4039 * @return {Boolean} True if the element is currently visible, else false
4041 isVisible : function() {
4044 visible = data(dom, ISVISIBLE);
4046 if(typeof visible == 'boolean'){ //return the cached value if registered
4049 //Determine the current state based on display states
4050 visible = !me.isStyle(VISIBILITY, HIDDEN) &&
4051 !me.isStyle(DISPLAY, NONE) &&
4052 !((getVisMode(dom) == El.ASCLASS) && me.hasCls(me.visibilityCls || El.visibilityCls));
4054 data(dom, ISVISIBLE, visible);
4059 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
4060 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
4061 * @param {Boolean} visible Whether the element is visible
4062 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4063 * @return {Ext.core.Element} this
4065 setVisible : function(visible, animate){
4066 var me = this, isDisplay, isVisibility, isOffsets, isNosize,
4068 visMode = getVisMode(dom);
4071 // hideMode string override
4072 if (typeof animate == 'string'){
4075 visMode = El.DISPLAY;
4078 visMode = El.VISIBILITY;
4081 visMode = El.OFFSETS;
4085 visMode = El.ASCLASS;
4088 me.setVisibilityMode(visMode);
4092 if (!animate || !me.anim) {
4093 if(visMode == El.ASCLASS ){
4095 me[visible?'removeCls':'addCls'](me.visibilityCls || El.visibilityCls);
4097 } else if (visMode == El.DISPLAY){
4099 return me.setDisplayed(visible);
4101 } else if (visMode == El.OFFSETS){
4104 // Remember position for restoring, if we are not already hidden by offsets.
4105 if (!me.hideModeStyles) {
4106 me.hideModeStyles = {
4107 position: me.getStyle('position'),
4108 top: me.getStyle('top'),
4109 left: me.getStyle('left')
4112 me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
4115 // Only "restore" as position if we have actually been hidden using offsets.
4116 // Calling setVisible(true) on a positioned element should not reposition it.
4117 else if (me.hideModeStyles) {
4118 me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
4119 delete me.hideModeStyles;
4124 // Show by clearing visibility style. Explicitly setting to "visible" overrides parent visibility setting.
4125 dom.style.visibility = visible ? '' : HIDDEN;
4128 // closure for composites
4130 me.setOpacity(0.01);
4131 me.setVisible(true);
4133 if (!Ext.isObject(animate)) {
4139 me.animate(Ext.applyIf({
4140 callback: function() {
4141 visible || me.setVisible(false).setOpacity(1);
4144 opacity: (visible) ? 1 : 0
4148 data(dom, ISVISIBLE, visible); //set logical visibility state
4155 * Determine if the Element has a relevant height and width available based
4156 * upon current logical visibility state
4158 hasMetrics : function(){
4160 return this.isVisible() || (getVisMode(dom) == El.OFFSETS) || (getVisMode(dom) == El.VISIBILITY);
4164 * Toggles the element's visibility or display, depending on visibility mode.
4165 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4166 * @return {Ext.core.Element} this
4168 toggle : function(animate){
4170 me.setVisible(!me.isVisible(), me.anim(animate));
4175 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
4176 * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
4177 * @return {Ext.core.Element} this
4179 setDisplayed : function(value) {
4180 if(typeof value == "boolean"){
4181 value = value ? getDisplay(this.dom) : NONE;
4183 this.setStyle(DISPLAY, value);
4188 fixDisplay : function(){
4190 if (me.isStyle(DISPLAY, NONE)) {
4191 me.setStyle(VISIBILITY, HIDDEN);
4192 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
4193 if (me.isStyle(DISPLAY, NONE)) { // if that fails, default to block
4194 me.setStyle(DISPLAY, "block");
4200 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
4201 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
4202 * @return {Ext.core.Element} this
4204 hide : function(animate){
4205 // hideMode override
4206 if (typeof animate == 'string'){
4207 this.setVisible(false, animate);
4210 this.setVisible(false, this.anim(animate));
4215 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
4216 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
4217 * @return {Ext.core.Element} this
4219 show : function(animate){
4220 // hideMode override
4221 if (typeof animate == 'string'){
4222 this.setVisible(true, animate);
4225 this.setVisible(true, this.anim(animate));
4231 * @class Ext.core.Element
4233 Ext.applyIf(Ext.core.Element.prototype, {
4234 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
4235 animate: function(config) {
4238 me = Ext.get(me.dom);
4240 if (Ext.fx.Manager.hasFxBlock(me.id)) {
4243 Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(config)));
4247 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
4248 anim: function(config) {
4249 if (!Ext.isObject(config)) {
4250 return (config) ? {} : false;
4254 duration = config.duration || Ext.fx.Anim.prototype.duration,
4255 easing = config.easing || 'ease',
4258 if (config.stopAnimation) {
4262 Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
4264 // Clear any 'paused' defaults.
4265 Ext.fx.Manager.setFxDefaults(me.id, {
4271 remove: config.remove,
4272 alternate: config.alternate || false,
4275 callback: config.callback,
4276 listeners: config.listeners,
4277 iterations: config.iterations || 1,
4278 scope: config.scope,
4279 block: config.block,
4280 concurrent: config.concurrent,
4281 delay: config.delay || 0,
4283 keyframes: config.keyframes,
4284 from: config.from || {},
4285 to: Ext.apply({}, config)
4287 Ext.apply(animConfig.to, config.to);
4289 // Anim API properties - backward compat
4290 delete animConfig.to.to;
4291 delete animConfig.to.from;
4292 delete animConfig.to.remove;
4293 delete animConfig.to.alternate;
4294 delete animConfig.to.keyframes;
4295 delete animConfig.to.iterations;
4296 delete animConfig.to.listeners;
4297 delete animConfig.to.target;
4298 delete animConfig.to.paused;
4299 delete animConfig.to.callback;
4300 delete animConfig.to.scope;
4301 delete animConfig.to.duration;
4302 delete animConfig.to.easing;
4303 delete animConfig.to.concurrent;
4304 delete animConfig.to.block;
4305 delete animConfig.to.stopAnimation;
4306 delete animConfig.to.delay;
4311 * Slides the element into view. An anchor point can be optionally passed to set the point of
4312 * origin for the slide effect. This function automatically handles wrapping the element with
4313 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
4316 // default: slide the element in from the top
4319 // custom: slide the element in from the right with a 2-second duration
4320 el.slideIn('r', { duration: 2 });
4322 // common config options shown with default values
4328 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
4329 * @param {Object} options (optional) Object literal with any of the Fx config options
4330 * @return {Ext.core.Element} The Element
4332 slideIn: function(anchor, obj, slideOut) {
4334 elStyle = me.dom.style,
4335 beforeAnim, wrapAnim;
4337 anchor = anchor || "t";
4340 beforeAnim = function() {
4341 var animScope = this,
4342 listeners = obj.listeners,
4343 box, position, restoreSize, wrap, anim;
4350 if ((anchor == 't' || anchor == 'b') && box.height == 0) {
4351 box.height = me.dom.scrollHeight;
4353 else if ((anchor == 'l' || anchor == 'r') && box.width == 0) {
4354 box.width = me.dom.scrollWidth;
4357 position = me.getPositioning();
4358 me.setSize(box.width, box.height);
4362 visibility: slideOut ? 'visible' : 'hidden'
4365 wrap.setPositioning(position);
4366 if (wrap.isStyle('position', 'static')) {
4367 wrap.position('relative');
4369 me.clearPositioning('auto');
4372 // This element is temporarily positioned absolute within its wrapper.
4373 // Restore to its default, CSS-inherited visibility setting.
4374 // We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap.
4377 position: 'absolute'
4380 wrap.setSize(box.width, box.height);
4387 width: box.width + 'px',
4391 width: box.width + 'px',
4392 height: box.height + 'px'
4395 elStyle.bottom = '0px';
4401 height: box.height + 'px'
4404 width: box.width + 'px',
4405 height: box.height + 'px'
4408 elStyle.right = '0px';
4413 x: box.x + box.width,
4415 height: box.height + 'px'
4419 width: box.width + 'px',
4420 height: box.height + 'px'
4427 y: box.y + box.height,
4428 width: box.width + 'px',
4433 width: box.width + 'px',
4434 height: box.height + 'px'
4447 width: box.width + 'px',
4448 height: box.height + 'px'
4451 elStyle.bottom = '0px';
4452 elStyle.right = '0px';
4457 x: box.x + box.width,
4463 width: box.width + 'px',
4464 height: box.height + 'px'
4467 elStyle.right = '0px';
4472 x: box.x + box.width,
4473 y: box.y + box.height,
4480 width: box.width + 'px',
4481 height: box.height + 'px'
4488 y: box.y + box.height,
4494 width: box.width + 'px',
4495 height: box.height + 'px'
4498 elStyle.bottom = '0px';
4503 wrapAnim = Ext.apply({}, obj);
4504 delete wrapAnim.listeners;
4505 wrapAnim = Ext.create('Ext.fx.Anim', Ext.applyIf(wrapAnim, {
4509 from: slideOut ? anim.to : anim.from,
4510 to: slideOut ? anim.from : anim.to
4513 // In the absence of a callback, this listener MUST be added first
4514 wrapAnim.on('afteranimate', function() {
4516 me.setPositioning(position);
4517 if (obj.useDisplay) {
4518 me.setDisplayed(false);
4524 me.clearPositioning();
4525 me.setPositioning(position);
4528 wrap.dom.parentNode.insertBefore(me.dom, wrap.dom);
4531 me.setSize(box.width, box.height);
4534 // Add configured listeners after
4536 wrapAnim.on(listeners);
4541 duration: obj.duration ? obj.duration * 2 : 1000,
4548 if (wrapAnim && wrapAnim.running) {
4560 * Slides the element out of view. An anchor point can be optionally passed to set the end point
4561 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
4562 * 'hidden') but block elements will still take up space in the document. The element must be removed
4563 * from the DOM using the 'remove' config option if desired. This function automatically handles
4564 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
4567 // default: slide the element out to the top
4570 // custom: slide the element out to the right with a 2-second duration
4571 el.slideOut('r', { duration: 2 });
4573 // common config options shown with default values
4581 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
4582 * @param {Object} options (optional) Object literal with any of the Fx config options
4583 * @return {Ext.core.Element} The Element
4585 slideOut: function(anchor, o) {
4586 return this.slideIn(anchor, o, true);
4590 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
4591 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
4597 // common config options shown with default values
4604 * @param {Object} options (optional) Object literal with any of the Fx config options
4605 * @return {Ext.core.Element} The Element
4608 puff: function(obj) {
4611 obj = Ext.applyIf(obj || {}, {
4617 beforeAnim = function() {
4621 var box = me.getBox(),
4622 fontSize = me.getStyle('fontSize'),
4623 position = me.getPositioning();
4625 width: box.width * 2,
4626 height: box.height * 2,
4627 x: box.x - (box.width / 2),
4628 y: box.y - (box.height /2),
4632 this.on('afteranimate',function() {
4634 if (obj.useDisplay) {
4635 me.setDisplayed(false);
4640 me.setPositioning(position);
4641 me.setStyle({fontSize: fontSize});
4647 duration: obj.duration,
4659 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
4660 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
4661 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
4667 // all config options shown with default values
4675 * @param {Object} options (optional) Object literal with any of the Fx config options
4676 * @return {Ext.core.Element} The Element
4678 switchOff: function(obj) {
4682 obj = Ext.applyIf(obj || {}, {
4689 beforeAnim = function() {
4690 var animScope = this,
4691 size = me.getSize(),
4696 position = me.getPositioning();
4698 keyframe = Ext.create('Ext.fx.Animator', {
4700 duration: obj.duration,
4708 y: xy[1] + size.height / 2
4712 x: xy[0] + size.width / 2
4716 keyframe.on('afteranimate', function() {
4717 if (obj.useDisplay) {
4718 me.setDisplayed(false);
4723 me.setPositioning(position);
4729 duration: (obj.duration * 2),
4740 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
4743 // default: a single light blue ripple
4746 // custom: 3 red ripples lasting 3 seconds total
4747 el.frame("#ff0000", 3, { duration: 3 });
4749 // common config options shown with default values
4750 el.frame("#C3DAF9", 1, {
4751 duration: 1 //duration of each individual ripple.
4752 // Note: Easing is not configurable and will be ignored if included
4755 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
4756 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
4757 * @param {Object} options (optional) Object literal with any of the Fx config options
4758 * @return {Ext.core.Element} The Element
4760 frame : function(color, count, obj){
4764 color = color || '#C3DAF9';
4768 beforeAnim = function() {
4770 var animScope = this,
4772 proxy = Ext.getBody().createChild({
4774 position : 'absolute',
4775 'pointer-events': 'none',
4777 border : '0px solid ' + color
4781 proxyAnim = Ext.create('Ext.fx.Anim', {
4783 duration: obj.duration || 1000,
4798 height: box.height + 40,
4799 width: box.width + 40
4802 proxyAnim.on('afteranimate', function() {
4809 duration: (obj.duration * 2) || 2000,
4820 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
4821 * ending point of the effect.
4824 // default: slide the element downward while fading out
4827 // custom: slide the element out to the right with a 2-second duration
4828 el.ghost('r', { duration: 2 });
4830 // common config options shown with default values
4836 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
4837 * @param {Object} options (optional) Object literal with any of the Fx config options
4838 * @return {Ext.core.Element} The Element
4840 ghost: function(anchor, obj) {
4844 anchor = anchor || "b";
4845 beforeAnim = function() {
4846 var width = me.getWidth(),
4847 height = me.getHeight(),
4849 position = me.getPositioning(),
4855 to.y = xy[1] - height;
4858 to.x = xy[0] - width;
4861 to.x = xy[0] + width;
4864 to.y = xy[1] + height;
4867 to.x = xy[0] - width;
4868 to.y = xy[1] - height;
4871 to.x = xy[0] - width;
4872 to.y = xy[1] + height;
4875 to.x = xy[0] + width;
4876 to.y = xy[1] + height;
4879 to.x = xy[0] + width;
4880 to.y = xy[1] - height;
4884 this.on('afteranimate', function () {
4888 me.setPositioning(position);
4893 me.animate(Ext.applyIf(obj || {}, {
4906 * Highlights the Element by setting a color (applies to the background-color by default, but can be
4907 * changed using the "attr" config option) and then fading back to the original color. If no original
4908 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
4911 // default: highlight background to yellow
4914 // custom: highlight foreground text to blue for 2 seconds
4915 el.highlight("0000ff", { attr: 'color', duration: 2 });
4917 // common config options shown with default values
4918 el.highlight("ffff9c", {
4919 attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value
4920 endColor: (current color) or "ffffff",
4925 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
4926 * @param {Object} options (optional) Object literal with any of the Fx config options
4927 * @return {Ext.core.Element} The Element
4929 highlight: function(color, o) {
4933 restore, to, attr, lns, event, fn;
4936 lns = o.listeners || {};
4937 attr = o.attr || 'backgroundColor';
4938 from[attr] = color || 'ffff9c';
4942 to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
4948 // Don't apply directly on lns, since we reference it in our own callbacks below
4949 o.listeners = Ext.apply(Ext.apply({}, lns), {
4950 beforeanimate: function() {
4951 restore = dom.style[attr];
4955 event = lns.beforeanimate;
4957 fn = event.fn || event;
4958 return fn.apply(event.scope || lns.scope || window, arguments);
4961 afteranimate: function() {
4963 dom.style[attr] = restore;
4966 event = lns.afteranimate;
4968 fn = event.fn || event;
4969 fn.apply(event.scope || lns.scope || window, arguments);
4974 me.animate(Ext.apply({}, o, {
4985 * Creates a pause before any subsequent queued effects begin. If there are
4986 * no effects queued after the pause it will have no effect.
4991 * @param {Number} seconds The length of time to pause (in seconds)
4992 * @return {Ext.Element} The Element
4994 pause: function(ms) {
4996 Ext.fx.Manager.setFxDefaults(me.id, {
5003 * Fade an element in (from transparent to opaque). The ending opacity can be specified
5004 * using the <tt>{@link #endOpacity}</tt> config option.
5007 // default: fade in from opacity 0 to 100%
5010 // custom: fade in from opacity 0 to 75% over 2 seconds
5011 el.fadeIn({ endOpacity: .75, duration: 2});
5013 // common config options shown with default values
5015 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
5020 * @param {Object} options (optional) Object literal with any of the Fx config options
5021 * @return {Ext.Element} The Element
5023 fadeIn: function(o) {
5024 this.animate(Ext.apply({}, o, {
5031 * Fade an element out (from opaque to transparent). The ending opacity can be specified
5032 * using the <tt>{@link #endOpacity}</tt> config option. Note that IE may require
5033 * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
5036 // default: fade out from the element's current opacity to 0
5039 // custom: fade out from the element's current opacity to 25% over 2 seconds
5040 el.fadeOut({ endOpacity: .25, duration: 2});
5042 // common config options shown with default values
5044 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
5051 * @param {Object} options (optional) Object literal with any of the Fx config options
5052 * @return {Ext.Element} The Element
5054 fadeOut: function(o) {
5055 this.animate(Ext.apply({}, o, {
5063 * Animates the transition of an element's dimensions from a starting height/width
5064 * to an ending height/width. This method is a convenience implementation of {@link #shift}.
5067 // change height and width to 100x100 pixels
5070 // common config options shown with default values. The height and width will default to
5071 // the element's existing values if passed as null.
5073 [element's width],
5074 [element's height], {
5080 * @param {Number} width The new width (pass undefined to keep the original width)
5081 * @param {Number} height The new height (pass undefined to keep the original height)
5082 * @param {Object} options (optional) Object literal with any of the Fx config options
5083 * @return {Ext.Element} The Element
5085 scale: function(w, h, o) {
5086 this.animate(Ext.apply({}, o, {
5095 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
5096 * Any of these properties not specified in the config object will not be changed. This effect
5097 * requires that at least one new dimension, position or opacity setting must be passed in on
5098 * the config object in order for the function to have any effect.
5101 // slide the element horizontally to x position 200 while changing the height and opacity
5102 el.shift({ x: 200, height: 50, opacity: .8 });
5104 // common config options shown with default values.
5106 width: [element's width],
5107 height: [element's height],
5108 x: [element's x position],
5109 y: [element's y position],
5110 opacity: [element's opacity],
5115 * @param {Object} options Object literal with any of the Fx config options
5116 * @return {Ext.Element} The Element
5118 shift: function(config) {
5119 this.animate(config);
5125 * @class Ext.core.Element
5127 Ext.applyIf(Ext.core.Element, {
5128 unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
5129 camelRe: /(-[a-z])/gi,
5130 opacityRe: /alpha\(opacity=(.*)\)/i,
5131 cssRe: /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
5134 borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
5135 paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
5136 margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
5138 // Reference the prototype's version of the method. Signatures are identical.
5139 addUnits : Ext.core.Element.prototype.addUnits,
5142 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
5143 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
5145 * @param {Number|String} box The encoded margins
5146 * @return {Object} An object with margin sizes for top, right, bottom and left
5148 parseBox : function(box) {
5149 if (Ext.isObject(box)) {
5152 right: box.right || 0,
5153 bottom: box.bottom || 0,
5157 if (typeof box != 'string') {
5158 box = box.toString();
5160 var parts = box.split(' '),
5164 parts[1] = parts[2] = parts[3] = parts[0];
5167 parts[2] = parts[0];
5168 parts[3] = parts[1];
5171 parts[3] = parts[1];
5175 top :parseFloat(parts[0]) || 0,
5176 right :parseFloat(parts[1]) || 0,
5177 bottom:parseFloat(parts[2]) || 0,
5178 left :parseFloat(parts[3]) || 0
5185 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
5186 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
5188 * @param {Number|String} box The encoded margins
5189 * @param {String} units The type of units to add
5190 * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
5192 unitizeBox : function(box, units) {
5193 var A = this.addUnits,
5194 B = this.parseBox(box);
5196 return A(B.top, units) + ' ' +
5197 A(B.right, units) + ' ' +
5198 A(B.bottom, units) + ' ' +
5204 camelReplaceFn : function(m, a) {
5205 return a.charAt(1).toUpperCase();
5209 * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
5212 * <li>border-width -> borderWidth</li>
5213 * <li>padding-top -> paddingTop</li>
5216 * @param {String} prop The property to normalize
5217 * @return {String} The normalized string
5219 normalize : function(prop) {
5220 if (prop == 'float') {
5221 prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
5223 return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
5227 * Retrieves the document height
5229 * @return {Number} documentHeight
5231 getDocumentHeight: function() {
5232 return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
5236 * Retrieves the document width
5238 * @return {Number} documentWidth
5240 getDocumentWidth: function() {
5241 return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
5245 * Retrieves the viewport height of the window.
5247 * @return {Number} viewportHeight
5249 getViewportHeight: function(){
5250 return window.innerHeight;
5254 * Retrieves the viewport width of the window.
5256 * @return {Number} viewportWidth
5258 getViewportWidth : function() {
5259 return window.innerWidth;
5263 * Retrieves the viewport size of the window.
5265 * @return {Object} object containing width and height properties
5267 getViewSize : function() {
5269 width: window.innerWidth,
5270 height: window.innerHeight
5275 * Retrieves the current orientation of the window. This is calculated by
5276 * determing if the height is greater than the width.
5278 * @return {String} Orientation of window: 'portrait' or 'landscape'
5280 getOrientation : function() {
5281 if (Ext.supports.OrientationChange) {
5282 return (window.orientation == 0) ? 'portrait' : 'landscape';
5285 return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
5289 * Returns the top Element that is located at the passed coordinates
5291 * @param {Number} x The x coordinate
5292 * @param {Number} x The y coordinate
5293 * @return {String} The found Element
5295 fromPoint: function(x, y) {
5296 return Ext.get(document.elementFromPoint(x, y));
5300 * Converts a CSS string into an object with a property for each style.
5302 * The sample code below would return an object with 2 properties, one
5303 * for background-color and one for color.</p>
5305 var css = 'background-color: red;color: blue; ';
5306 console.log(Ext.core.Element.parseStyles(css));
5309 * @param {String} styles A CSS string
5310 * @return {Object} styles
5312 parseStyles: function(styles){
5318 // Since we're using the g flag on the regex, we need to set the lastIndex.
5319 // This automatically happens on some implementations, but not others, see:
5320 // http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
5321 // http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
5322 cssRe.lastIndex = 0;
5323 while ((matches = cssRe.exec(styles))) {
5324 out[matches[1]] = matches[2];
5332 * @class Ext.CompositeElementLite
5333 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
5334 * members, or to perform collective actions upon the whole set.</p>
5335 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
5336 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
5337 * Example:<pre><code>
5338 var els = Ext.select("#some-el div.some-class");
5339 // or select directly from an existing element
5340 var el = Ext.get('some-el');
5341 el.select('div.some-class');
5343 els.setWidth(100); // all elements become 100 width
5344 els.hide(true); // all elements fade out and hide
5346 els.setWidth(100).hide(true);
5349 Ext.CompositeElementLite = function(els, root){
5351 * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
5352 * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
5353 * to augment the capabilities of the CompositeElementLite class may use it when adding
5354 * methods to the class.</p>
5355 * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
5356 * following siblings of selected elements, the code would be</p><code><pre>
5357 Ext.override(Ext.CompositeElementLite, {
5358 nextAll: function() {
5359 var els = this.elements, i, l = els.length, n, r = [], ri = -1;
5361 // Loop through all elements in this Composite, accumulating
5362 // an Array of all siblings.
5363 for (i = 0; i < l; i++) {
5364 for (n = els[i].nextSibling; n; n = n.nextSibling) {
5369 // Add all found siblings to this Composite
5374 * @property elements
5377 this.add(els, root);
5378 this.el = new Ext.core.Element.Flyweight();
5381 Ext.CompositeElementLite.prototype = {
5385 getElement : function(el){
5386 // Set the shared flyweight dom property to the current element
5394 transformElement : function(el){
5395 return Ext.getDom(el);
5399 * Returns the number of elements in this Composite.
5402 getCount : function(){
5403 return this.elements.length;
5406 * Adds elements to this Composite object.
5407 * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
5408 * @return {CompositeElement} This Composite object.
5410 add : function(els, root){
5412 elements = me.elements;
5416 if(typeof els == "string"){
5417 els = Ext.core.Element.selectorFunction(els, root);
5418 }else if(els.isComposite){
5420 }else if(!Ext.isIterable(els)){
5424 for(var i = 0, len = els.length; i < len; ++i){
5425 elements.push(me.transformElement(els[i]));
5430 invoke : function(fn, args){
5437 for(i = 0; i < len; i++) {
5440 Ext.core.Element.prototype[fn].apply(me.getElement(e), args);
5446 * Returns a flyweight Element of the dom element object at the specified index
5447 * @param {Number} index
5448 * @return {Ext.core.Element}
5450 item : function(index){
5452 el = me.elements[index],
5456 out = me.getElement(el);
5461 // fixes scope with flyweight
5462 addListener : function(eventName, handler, scope, opt){
5463 var els = this.elements,
5467 for(i = 0; i<len; i++) {
5470 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
5476 * <p>Calls the passed function for each element in this composite.</p>
5477 * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
5478 * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
5479 * <b>This is the flyweight (shared) Ext.core.Element instance, so if you require a
5480 * a reference to the dom node, use el.dom.</b></div></li>
5481 * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
5482 * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
5484 * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
5485 * @return {CompositeElement} this
5487 each : function(fn, scope){
5493 for(i = 0; i<len; i++) {
5496 e = this.getElement(e);
5497 if(fn.call(scope || e, e, me, i) === false){
5506 * Clears this Composite and adds the elements passed.
5507 * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
5508 * @return {CompositeElement} this
5510 fill : function(els){
5518 * Filters this composite to only elements that match the passed selector.
5519 * @param {String/Function} selector A string CSS selector or a comparison function.
5520 * The comparison function will be called with the following arguments:<ul>
5521 * <li><code>el</code> : Ext.core.Element<div class="sub-desc">The current DOM element.</div></li>
5522 * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
5524 * @return {CompositeElement} this
5526 filter : function(selector){
5529 fn = Ext.isFunction(selector) ? selector
5531 return el.is(selector);
5534 me.each(function(el, self, i) {
5535 if (fn(el, i) !== false) {
5536 els[els.length] = me.transformElement(el);
5545 * Find the index of the passed element within the composite collection.
5546 * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
5547 * @return Number The index of the passed Ext.core.Element in the composite collection, or -1 if not found.
5549 indexOf : function(el){
5550 return Ext.Array.indexOf(this.elements, this.transformElement(el));
5554 * Replaces the specified element with the passed element.
5555 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
5557 * @param {Mixed} replacement The id of an element or the Element itself.
5558 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
5559 * @return {CompositeElement} this
5561 replaceElement : function(el, replacement, domReplace){
5562 var index = !isNaN(el) ? el : this.indexOf(el),
5565 replacement = Ext.getDom(replacement);
5567 d = this.elements[index];
5568 d.parentNode.insertBefore(replacement, d);
5571 Ext.Array.splice(this.elements, index, 1, replacement);
5577 * Removes all elements.
5584 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
5588 * Copies all of the functions from Ext.core.Element's prototype onto CompositeElementLite's prototype.
5589 * This is called twice - once immediately below, and once again after additional Ext.core.Element
5590 * are added in Ext JS
5592 Ext.CompositeElementLite.importElementMethods = function() {
5594 ElProto = Ext.core.Element.prototype,
5595 CelProto = Ext.CompositeElementLite.prototype;
5597 for (fnName in ElProto) {
5598 if (typeof ElProto[fnName] == 'function'){
5600 CelProto[fnName] = CelProto[fnName] || function() {
5601 return this.invoke(fnName, arguments);
5603 }).call(CelProto, fnName);
5609 Ext.CompositeElementLite.importElementMethods();
5612 Ext.core.Element.selectorFunction = Ext.DomQuery.select;
5616 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
5617 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
5618 * {@link Ext.CompositeElementLite CompositeElementLite} object.
5619 * @param {String/Array} selector The CSS selector or an array of elements
5620 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
5621 * @return {CompositeElementLite/CompositeElement}
5622 * @member Ext.core.Element
5625 Ext.core.Element.select = function(selector, root){
5627 if(typeof selector == "string"){
5628 els = Ext.core.Element.selectorFunction(selector, root);
5629 }else if(selector.length !== undefined){
5634 sourceClass: "Ext.core.Element",
5635 sourceMethod: "select",
5638 msg: "Invalid selector specified: " + selector
5642 return new Ext.CompositeElementLite(els);
5645 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
5646 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
5647 * {@link Ext.CompositeElementLite CompositeElementLite} object.
5648 * @param {String/Array} selector The CSS selector or an array of elements
5649 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
5650 * @return {CompositeElementLite/CompositeElement}
5654 Ext.select = Ext.core.Element.select;
5657 * @class Ext.util.DelayedTask
5659 * The DelayedTask class provides a convenient way to "buffer" the execution of a method,
5660 * performing setTimeout where a new timeout cancels the old timeout. When called, the
5661 * task will wait the specified time period before executing. If durng that time period,
5662 * the task is called again, the original call will be cancelled. This continues so that
5663 * the function is only called a single time for each iteration.
5665 * This method is especially useful for things like detecting whether a user has finished
5666 * typing in a text field. An example would be performing validation on a keypress. You can
5667 * use this class to buffer the keypress events for a certain number of milliseconds, and
5668 * perform only if they stop for that amount of time.
5672 * var task = new Ext.util.DelayedTask(function(){
5673 * alert(Ext.getDom('myInputField').value.length);
5676 * // Wait 500ms before calling our function. If the user presses another key
5677 * // during that 500ms, it will be cancelled and we'll wait another 500ms.
5678 * Ext.get('myInputField').on('keypress', function(){
5679 * task.{@link #delay}(500);
5682 * Note that we are using a DelayedTask here to illustrate a point. The configuration
5683 * option `buffer` for {@link Ext.util.Observable#addListener addListener/on} will
5684 * also setup a delayed task for you to buffer events.
5686 * @constructor The parameters to this constructor serve as defaults and are not required.
5687 * @param {Function} fn (optional) The default function to call.
5688 * @param {Object} scope (optional) The default scope (The <code><b>this</b></code> reference) in which the
5689 * function is called. If not specified, <code>this</code> will refer to the browser window.
5690 * @param {Array} args (optional) The default Array of arguments.
5692 Ext.util.DelayedTask = function(fn, scope, args) {
5698 fn.apply(scope, args || []);
5702 * Cancels any pending timeout and queues a new one
5703 * @param {Number} delay The milliseconds to delay
5704 * @param {Function} newFn (optional) Overrides function passed to constructor
5705 * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
5706 * is specified, <code>this</code> will refer to the browser window.
5707 * @param {Array} newArgs (optional) Overrides args passed to constructor
5709 this.delay = function(delay, newFn, newScope, newArgs) {
5712 scope = newScope || scope;
5713 args = newArgs || args;
5714 id = setInterval(call, delay);
5718 * Cancel the last queued timeout
5720 this.cancel = function(){
5727 Ext.require('Ext.util.DelayedTask', function() {
5729 Ext.util.Event = Ext.extend(Object, (function() {
5730 function createBuffered(handler, listener, o, scope) {
5731 listener.task = new Ext.util.DelayedTask();
5733 listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
5737 function createDelayed(handler, listener, o, scope) {
5739 var task = new Ext.util.DelayedTask();
5740 if (!listener.tasks) {
5741 listener.tasks = [];
5743 listener.tasks.push(task);
5744 task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
5748 function createSingle(handler, listener, o, scope) {
5750 listener.ev.removeListener(listener.fn, scope);
5751 return handler.apply(scope, arguments);
5758 constructor: function(observable, name) {
5760 this.observable = observable;
5761 this.listeners = [];
5764 addListener: function(fn, scope, options) {
5767 scope = scope || me.observable;
5772 sourceClass: Ext.getClassName(this.observable),
5773 sourceMethod: "addListener",
5774 msg: "The specified callback function is undefined"
5779 if (!me.isListening(fn, scope)) {
5780 listener = me.createListener(fn, scope, options);
5782 // if we are currently firing this event, don't disturb the listener loop
5783 me.listeners = me.listeners.slice(0);
5785 me.listeners.push(listener);
5789 createListener: function(fn, scope, o) {
5791 scope = scope || this.observable;
5801 // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
5802 // because the event removal that the single listener does destroys the listener's DelayedTask(s)
5804 handler = createSingle(handler, listener, o, scope);
5807 handler = createDelayed(handler, listener, o, scope);
5810 handler = createBuffered(handler, listener, o, scope);
5813 listener.fireFn = handler;
5817 findListener: function(fn, scope) {
5818 var listeners = this.listeners,
5819 i = listeners.length,
5824 listener = listeners[i];
5827 if (listener.fn == fn && (s == scope || s == this.observable)) {
5836 isListening: function(fn, scope) {
5837 return this.findListener(fn, scope) !== -1;
5840 removeListener: function(fn, scope) {
5845 index = me.findListener(fn, scope);
5847 listener = me.listeners[index];
5850 me.listeners = me.listeners.slice(0);
5853 // cancel and remove a buffered handler that hasn't fired yet
5854 if (listener.task) {
5855 listener.task.cancel();
5856 delete listener.task;
5859 // cancel and remove all delayed handlers that haven't fired yet
5860 k = listener.tasks && listener.tasks.length;
5863 listener.tasks[k].cancel();
5865 delete listener.tasks;
5868 // remove this listener from the listeners array
5869 Ext.Array.erase(me.listeners, index, 1);
5876 // Iterate to stop any buffered/delayed events
5877 clearListeners: function() {
5878 var listeners = this.listeners,
5879 i = listeners.length;
5882 this.removeListener(listeners[i].fn, listeners[i].scope);
5888 listeners = me.listeners,
5889 count = listeners.length,
5896 for (i = 0; i < count; i++) {
5897 listener = listeners[i];
5898 args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
5900 args.push(listener.o);
5902 if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
5903 return (me.firing = false);
5915 * @class Ext.EventManager
5916 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
5917 * several useful events directly.
5918 * See {@link Ext.EventObject} for more details on normalized event objects.
5921 Ext.EventManager = {
5923 // --------------------- onReady ---------------------
5926 * Check if we have bound our global onReady listener
5929 hasBoundOnReady: false,
5932 * Check if fireDocReady has been called
5935 hasFiredReady: false,
5938 * Timer for the document ready event in old IE versions
5944 * Checks if we have bound an onreadystatechange event
5947 hasOnReadyStateChange: false,
5950 * Holds references to any onReady functions
5953 readyEvent: new Ext.util.Event(),
5956 * Check the ready state for old IE versions
5958 * @return {Boolean} True if the document is ready
5960 checkReadyState: function(){
5961 var me = Ext.EventManager;
5963 if(window.attachEvent){
5964 // See here for reference: http://javascript.nwbox.com/IEContentLoaded/
5965 if (window != top) {
5969 document.documentElement.doScroll('left');
5976 if (document.readyState == 'complete') {
5980 me.readyTimeout = setTimeout(arguments.callee, 2);
5985 * Binds the appropriate browser event for checking if the DOM has loaded.
5988 bindReadyEvent: function(){
5989 var me = Ext.EventManager;
5990 if (me.hasBoundOnReady) {
5994 if (document.addEventListener) {
5995 document.addEventListener('DOMContentLoaded', me.fireDocReady, false);
5996 // fallback, load will ~always~ fire
5997 window.addEventListener('load', me.fireDocReady, false);
5999 // check if the document is ready, this will also kick off the scroll checking timer
6000 if (!me.checkReadyState()) {
6001 document.attachEvent('onreadystatechange', me.checkReadyState);
6002 me.hasOnReadyStateChange = true;
6004 // fallback, onload will ~always~ fire
6005 window.attachEvent('onload', me.fireDocReady, false);
6007 me.hasBoundOnReady = true;
6011 * We know the document is loaded, so trigger any onReady events.
6014 fireDocReady: function(){
6015 var me = Ext.EventManager;
6017 // only unbind these events once
6018 if (!me.hasFiredReady) {
6019 me.hasFiredReady = true;
6021 if (document.addEventListener) {
6022 document.removeEventListener('DOMContentLoaded', me.fireDocReady, false);
6023 window.removeEventListener('load', me.fireDocReady, false);
6025 if (me.readyTimeout !== null) {
6026 clearTimeout(me.readyTimeout);
6028 if (me.hasOnReadyStateChange) {
6029 document.detachEvent('onreadystatechange', me.checkReadyState);
6031 window.detachEvent('onload', me.fireDocReady);
6033 Ext.supports.init();
6037 me.onWindowUnload();
6038 me.readyEvent.fire();
6043 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
6044 * accessed shorthanded as Ext.onReady().
6045 * @param {Function} fn The method the event invokes.
6046 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
6047 * @param {boolean} options (optional) Options object as passed to {@link Ext.core.Element#addListener}.
6049 onDocumentReady: function(fn, scope, options){
6050 options = options || {};
6051 var me = Ext.EventManager,
6052 readyEvent = me.readyEvent;
6054 // force single to be true so our event is only ever fired once.
6055 options.single = true;
6057 // Document already loaded, let's just fire it
6059 readyEvent.addListener(fn, scope, options);
6062 options.delay = options.delay || 1;
6063 readyEvent.addListener(fn, scope, options);
6064 me.bindReadyEvent();
6069 // --------------------- event binding ---------------------
6072 * Contains a list of all document mouse downs, so we can ensure they fire even when stopEvent is called.
6075 stoppedMouseDownEvent: new Ext.util.Event(),
6078 * Options to parse for the 4th argument to addListener.
6081 propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,
6084 * Get the id of the element. If one has not been assigned, automatically assign it.
6085 * @param {Mixed} element The element to get the id for.
6086 * @return {String} id
6088 getId : function(element) {
6089 var skipGarbageCollection = false,
6092 element = Ext.getDom(element);
6094 if (element === document || element === window) {
6095 id = element === document ? Ext.documentId : Ext.windowId;
6098 id = Ext.id(element);
6100 // skip garbage collection for special elements (window, document, iframes)
6101 if (element && (element.getElementById || element.navigator)) {
6102 skipGarbageCollection = true;
6105 if (!Ext.cache[id]){
6106 Ext.core.Element.addToCache(new Ext.core.Element(element), id);
6107 if (skipGarbageCollection) {
6108 Ext.cache[id].skipGarbageCollection = true;
6115 * Convert a "config style" listener into a set of flat arguments so they can be passed to addListener
6117 * @param {Object} element The element the event is for
6118 * @param {Object} event The event configuration
6119 * @param {Object} isRemove True if a removal should be performed, otherwise an add will be done.
6121 prepareListenerConfig: function(element, config, isRemove){
6126 // loop over all the keys in the object
6127 for (key in config) {
6128 if (config.hasOwnProperty(key)) {
6129 // if the key is something else then an event option
6130 if (!propRe.test(key)) {
6131 value = config[key];
6132 // if the value is a function it must be something like click: function(){}, scope: this
6133 // which means that there might be multiple event listeners with shared options
6134 if (Ext.isFunction(value)) {
6136 args = [element, key, value, config.scope, config];
6138 // if its not a function, it must be an object like click: {fn: function(){}, scope: this}
6139 args = [element, key, value.fn, value.scope, value];
6142 if (isRemove === true) {
6143 me.removeListener.apply(this, args);
6145 me.addListener.apply(me, args);
6153 * Normalize cross browser event differences
6155 * @param {Object} eventName The event name
6156 * @param {Object} fn The function to execute
6157 * @return {Object} The new event name/function
6159 normalizeEvent: function(eventName, fn){
6160 if (/mouseenter|mouseleave/.test(eventName) && !Ext.supports.MouseEnterLeave) {
6162 fn = Ext.Function.createInterceptor(fn, this.contains, this);
6164 eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
6165 } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera){
6166 eventName = 'DOMMouseScroll';
6169 eventName: eventName,
6175 * Checks whether the event's relatedTarget is contained inside (or <b>is</b>) the element.
6177 * @param {Object} event
6179 contains: function(event){
6180 var parent = event.browserEvent.currentTarget,
6181 child = this.getRelatedTarget(event);
6183 if (parent && parent.firstChild) {
6185 if (child === parent) {
6188 child = child.parentNode;
6189 if (child && (child.nodeType != 1)) {
6198 * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
6199 * use {@link Ext.core.Element#addListener} directly on an Element in favor of calling this version.
6200 * @param {String/HTMLElement} el The html element or id to assign the event handler to.
6201 * @param {String} eventName The name of the event to listen for.
6202 * @param {Function} handler The handler function the event invokes. This function is passed
6203 * the following parameters:<ul>
6204 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
6205 * <li>t : Element<div class="sub-desc">The {@link Ext.core.Element Element} which was the target of the event.
6206 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
6207 * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
6209 * @param {Object} scope (optional) The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.
6210 * @param {Object} options (optional) An object containing handler configuration properties.
6211 * This may contain any of the following properties:<ul>
6212 * <li>scope : Object<div class="sub-desc">The scope (<b><code>this</code></b> reference) in which the handler function is executed. <b>Defaults to the Element</b>.</div></li>
6213 * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
6214 * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
6215 * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
6216 * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
6217 * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
6218 * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
6219 * <li>single : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
6220 * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
6221 * by the specified number of milliseconds. If the event fires again within that time, the original
6222 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
6223 * <li>target : Element<div class="sub-desc">Only call the handler if the event was fired on the target Element, <i>not</i> if the event was bubbled up from a child node.</div></li>
6225 * <p>See {@link Ext.core.Element#addListener} for examples of how to use these options.</p>
6227 addListener: function(element, eventName, fn, scope, options){
6228 // Check if we've been passed a "config style" event.
6229 if (typeof eventName !== 'string') {
6230 this.prepareListenerConfig(element, eventName);
6234 var dom = Ext.getDom(element),
6241 sourceClass: 'Ext.EventManager',
6242 sourceMethod: 'addListener',
6243 targetElement: element,
6244 eventName: eventName,
6245 msg: 'Error adding "' + eventName + '\" listener for nonexistent element "' + element + '"'
6250 sourceClass: 'Ext.EventManager',
6251 sourceMethod: 'addListener',
6252 targetElement: element,
6253 eventName: eventName,
6254 msg: 'Error adding "' + eventName + '\" listener. The handler function is undefined.'
6259 // create the wrapper function
6260 options = options || {};
6262 bind = this.normalizeEvent(eventName, fn);
6263 wrap = this.createListenerWrap(dom, eventName, bind.fn, scope, options);
6266 if (dom.attachEvent) {
6267 dom.attachEvent('on' + bind.eventName, wrap);
6269 dom.addEventListener(bind.eventName, wrap, options.capture || false);
6272 if (dom == document && eventName == 'mousedown') {
6273 this.stoppedMouseDownEvent.addListener(wrap);
6276 // add all required data into the event cache
6277 this.getEventListenerCache(dom, eventName).push({
6285 * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
6286 * you will use {@link Ext.core.Element#removeListener} directly on an Element in favor of calling this version.
6287 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
6288 * @param {String} eventName The name of the event.
6289 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
6290 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
6291 * then this must refer to the same object.
6293 removeListener : function(element, eventName, fn, scope) {
6294 // handle our listener config object syntax
6295 if (typeof eventName !== 'string') {
6296 this.prepareListenerConfig(element, eventName, true);
6300 var dom = Ext.getDom(element),
6301 cache = this.getEventListenerCache(dom, eventName),
6302 bindName = this.normalizeEvent(eventName).eventName,
6303 i = cache.length, j,
6304 listener, wrap, tasks;
6308 listener = cache[i];
6310 if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
6311 wrap = listener.wrap;
6313 // clear buffered calls
6315 clearTimeout(wrap.task);
6319 // clear delayed calls
6320 j = wrap.tasks && wrap.tasks.length;
6323 clearTimeout(wrap.tasks[j]);
6328 if (dom.detachEvent) {
6329 dom.detachEvent('on' + bindName, wrap);
6331 dom.removeEventListener(bindName, wrap, false);
6334 if (wrap && dom == document && eventName == 'mousedown') {
6335 this.stoppedMouseDownEvent.removeListener(wrap);
6338 // remove listener from cache
6339 Ext.Array.erase(cache, i, 1);
6345 * Removes all event handers from an element. Typically you will use {@link Ext.core.Element#removeAllListeners}
6346 * directly on an Element in favor of calling this version.
6347 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
6349 removeAll : function(element){
6350 var dom = Ext.getDom(element),
6355 cache = this.getElementEventCache(dom);
6358 if (cache.hasOwnProperty(ev)) {
6359 this.removeListener(dom, ev);
6362 Ext.cache[dom.id].events = {};
6366 * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.core.Element#purgeAllListeners}
6367 * directly on an Element in favor of calling this version.
6368 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
6369 * @param {String} eventName (optional) The name of the event.
6371 purgeElement : function(element, eventName) {
6372 var dom = Ext.getDom(element),
6376 this.removeListener(dom, eventName);
6379 this.removeAll(dom);
6382 if(dom && dom.childNodes) {
6383 for(len = element.childNodes.length; i < len; i++) {
6384 this.purgeElement(element.childNodes[i], eventName);
6390 * Create the wrapper function for the event
6392 * @param {HTMLElement} dom The dom element
6393 * @param {String} ename The event name
6394 * @param {Function} fn The function to execute
6395 * @param {Object} scope The scope to execute callback in
6396 * @param {Object} options The options
6397 * @return {Function} the wrapper function
6399 createListenerWrap : function(dom, ename, fn, scope, options) {
6400 options = options || {};
6404 return function wrap(e, args) {
6405 // Compile the implementation upon first firing
6407 f = ['if(!Ext) {return;}'];
6409 if(options.buffer || options.delay || options.freezeEvent) {
6410 f.push('e = new Ext.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
6412 f.push('e = Ext.EventObject.setEvent(e);');
6415 if (options.delegate) {
6416 f.push('var t = e.getTarget("' + options.delegate + '", this);');
6417 f.push('if(!t) {return;}');
6419 f.push('var t = e.target;');
6422 if (options.target) {
6423 f.push('if(e.target !== options.target) {return;}');
6426 if(options.stopEvent) {
6427 f.push('e.stopEvent();');
6429 if(options.preventDefault) {
6430 f.push('e.preventDefault();');
6432 if(options.stopPropagation) {
6433 f.push('e.stopPropagation();');
6437 if(options.normalized === false) {
6438 f.push('e = e.browserEvent;');
6441 if(options.buffer) {
6442 f.push('(wrap.task && clearTimeout(wrap.task));');
6443 f.push('wrap.task = setTimeout(function(){');
6447 f.push('wrap.tasks = wrap.tasks || [];');
6448 f.push('wrap.tasks.push(setTimeout(function(){');
6451 // finally call the actual handler fn
6452 f.push('fn.call(scope || dom, e, t, options);');
6454 if(options.single) {
6455 f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
6459 f.push('}, ' + options.delay + '));');
6462 if(options.buffer) {
6463 f.push('}, ' + options.buffer + ');');
6466 gen = Ext.functionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join('\n'));
6469 gen.call(dom, e, options, fn, scope, ename, dom, wrap, args);
6474 * Get the event cache for a particular element for a particular event
6476 * @param {HTMLElement} element The element
6477 * @param {Object} eventName The event name
6478 * @return {Array} The events for the element
6480 getEventListenerCache : function(element, eventName) {
6485 var eventCache = this.getElementEventCache(element);
6486 return eventCache[eventName] || (eventCache[eventName] = []);
6490 * Gets the event cache for the object
6492 * @param {HTMLElement} element The element
6493 * @return {Object} The event cache for the object
6495 getElementEventCache : function(element) {
6499 var elementCache = Ext.cache[this.getId(element)];
6500 return elementCache.events || (elementCache.events = {});
6503 // --------------------- utility methods ---------------------
6504 mouseLeaveRe: /(mouseout|mouseleave)/,
6505 mouseEnterRe: /(mouseover|mouseenter)/,
6508 * Stop the event (preventDefault and stopPropagation)
6509 * @param {Event} The event to stop
6511 stopEvent: function(event) {
6512 this.stopPropagation(event);
6513 this.preventDefault(event);
6517 * Cancels bubbling of the event.
6518 * @param {Event} The event to stop bubbling.
6520 stopPropagation: function(event) {
6521 event = event.browserEvent || event;
6522 if (event.stopPropagation) {
6523 event.stopPropagation();
6525 event.cancelBubble = true;
6530 * Prevents the browsers default handling of the event.
6531 * @param {Event} The event to prevent the default
6533 preventDefault: function(event) {
6534 event = event.browserEvent || event;
6535 if (event.preventDefault) {
6536 event.preventDefault();
6538 event.returnValue = false;
6539 // Some keys events require setting the keyCode to -1 to be prevented
6541 // all ctrl + X and F1 -> F12
6542 if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
6546 // see this outdated document http://support.microsoft.com/kb/934364/en-us for more info
6552 * Gets the related target from the event.
6553 * @param {Object} event The event
6554 * @return {HTMLElement} The related target.
6556 getRelatedTarget: function(event) {
6557 event = event.browserEvent || event;
6558 var target = event.relatedTarget;
6560 if (this.mouseLeaveRe.test(event.type)) {
6561 target = event.toElement;
6562 } else if (this.mouseEnterRe.test(event.type)) {
6563 target = event.fromElement;
6566 return this.resolveTextNode(target);
6570 * Gets the x coordinate from the event
6571 * @param {Object} event The event
6572 * @return {Number} The x coordinate
6574 getPageX: function(event) {
6575 return this.getXY(event)[0];
6579 * Gets the y coordinate from the event
6580 * @param {Object} event The event
6581 * @return {Number} The y coordinate
6583 getPageY: function(event) {
6584 return this.getXY(event)[1];
6588 * Gets the x & ycoordinate from the event
6589 * @param {Object} event The event
6590 * @return {Array} The x/y coordinate
6592 getPageXY: function(event) {
6593 event = event.browserEvent || event;
6594 var x = event.pageX,
6596 doc = document.documentElement,
6597 body = document.body;
6599 // pageX/pageY not available (undefined, not null), use clientX/clientY instead
6600 if (!x && x !== 0) {
6601 x = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
6602 y = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
6608 * Gets the target of the event.
6609 * @param {Object} event The event
6610 * @return {HTMLElement} target
6612 getTarget: function(event) {
6613 event = event.browserEvent || event;
6614 return this.resolveTextNode(event.target || event.srcElement);
6618 * Resolve any text nodes accounting for browser differences.
6620 * @param {HTMLElement} node The node
6621 * @return {HTMLElement} The resolved node
6623 // technically no need to browser sniff this, however it makes no sense to check this every time, for every event, whether the string is equal.
6624 resolveTextNode: Ext.isGecko ?
6629 // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
6630 var s = HTMLElement.prototype.toString.call(node);
6631 if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]') {
6634 return node.nodeType == 3 ? node.parentNode: node;
6636 return node && node.nodeType == 3 ? node.parentNode: node;
6639 // --------------------- custom event binding ---------------------
6641 // Keep track of the current width/height
6646 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
6647 * passes new viewport width and height to handlers.
6648 * @param {Function} fn The handler function the window resize event invokes.
6649 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
6650 * @param {boolean} options Options object as passed to {@link Ext.core.Element#addListener}
6652 onWindowResize: function(fn, scope, options){
6653 var resize = this.resizeEvent;
6655 this.resizeEvent = resize = new Ext.util.Event();
6656 this.on(window, 'resize', this.fireResize, this, {buffer: 100});
6658 resize.addListener(fn, scope, options);
6662 * Fire the resize event.
6665 fireResize: function(){
6667 w = Ext.core.Element.getViewWidth(),
6668 h = Ext.core.Element.getViewHeight();
6670 //whacky problem in IE where the resize event will sometimes fire even though the w/h are the same.
6671 if(me.curHeight != h || me.curWidth != w){
6674 me.resizeEvent.fire(w, h);
6679 * Removes the passed window resize listener.
6680 * @param {Function} fn The method the event invokes
6681 * @param {Object} scope The scope of handler
6683 removeResizeListener: function(fn, scope){
6684 if (this.resizeEvent) {
6685 this.resizeEvent.removeListener(fn, scope);
6689 onWindowUnload: function() {
6690 var unload = this.unloadEvent;
6692 this.unloadEvent = unload = new Ext.util.Event();
6693 this.addListener(window, 'unload', this.fireUnload, this);
6698 * Fires the unload event for items bound with onWindowUnload
6701 fireUnload: function() {
6702 // wrap in a try catch, could have some problems during unload
6704 this.removeUnloadListener();
6705 // Work around FF3 remembering the last scroll position when refreshing the grid and then losing grid view
6707 var gridviews = Ext.ComponentQuery.query('gridview'),
6709 ln = gridviews.length;
6710 for (; i < ln; i++) {
6711 gridviews[i].scrollToTop();
6714 // Purge all elements in the cache
6718 if (cache.hasOwnProperty(el)) {
6719 Ext.EventManager.removeAll(el);
6727 * Removes the passed window unload listener.
6728 * @param {Function} fn The method the event invokes
6729 * @param {Object} scope The scope of handler
6731 removeUnloadListener: function(){
6732 if (this.unloadEvent) {
6733 this.removeListener(window, 'unload', this.fireUnload);
6738 * note 1: IE fires ONLY the keydown event on specialkey autorepeat
6739 * note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
6740 * (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
6743 useKeyDown: Ext.isWebKit ?
6744 parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
6745 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera),
6748 * Indicates which event to use for getting key presses.
6749 * @return {String} The appropriate event name.
6751 getKeyEvent: function(){
6752 return this.useKeyDown ? 'keydown' : 'keypress';
6757 * Alias for {@link Ext.Loader#onReady Ext.Loader.onReady} with withDomReady set to true
6761 Ext.onReady = function(fn, scope, options) {
6762 Ext.Loader.onReady(fn, scope, true, options);
6766 * Alias for {@link Ext.EventManager#onDocumentReady Ext.EventManager.onDocumentReady}
6768 * @method onDocumentReady
6770 Ext.onDocumentReady = Ext.EventManager.onDocumentReady;
6773 * Alias for {@link Ext.EventManager#addListener Ext.EventManager.addListener}
6774 * @member Ext.EventManager
6777 Ext.EventManager.on = Ext.EventManager.addListener;
6780 * Alias for {@link Ext.EventManager#removeListener Ext.EventManager.removeListener}
6781 * @member Ext.EventManager
6784 Ext.EventManager.un = Ext.EventManager.removeListener;
6787 var initExtCss = function() {
6788 // find the body element
6789 var bd = document.body || document.getElementsByTagName('body')[0],
6790 baseCSSPrefix = Ext.baseCSSPrefix,
6791 cls = [baseCSSPrefix + 'body'],
6799 html = bd.parentNode;
6801 //Let's keep this human readable!
6803 cls.push(baseCSSPrefix + 'ie');
6806 cls.push(baseCSSPrefix + 'ie6');
6809 cls.push(baseCSSPrefix + 'ie7');
6812 cls.push(baseCSSPrefix + 'ie8');
6815 cls.push(baseCSSPrefix + 'ie9');
6818 cls.push(baseCSSPrefix + 'gecko');
6821 cls.push(baseCSSPrefix + 'gecko3');
6824 cls.push(baseCSSPrefix + 'gecko4');
6827 cls.push(baseCSSPrefix + 'opera');
6830 cls.push(baseCSSPrefix + 'webkit');
6833 cls.push(baseCSSPrefix + 'safari');
6835 if (Ext.isSafari2) {
6836 cls.push(baseCSSPrefix + 'safari2');
6838 if (Ext.isSafari3) {
6839 cls.push(baseCSSPrefix + 'safari3');
6841 if (Ext.isSafari4) {
6842 cls.push(baseCSSPrefix + 'safari4');
6845 cls.push(baseCSSPrefix + 'chrome');
6848 cls.push(baseCSSPrefix + 'mac');
6851 cls.push(baseCSSPrefix + 'linux');
6853 if (!Ext.supports.CSS3BorderRadius) {
6854 cls.push(baseCSSPrefix + 'nbr');
6856 if (!Ext.supports.CSS3LinearGradient) {
6857 cls.push(baseCSSPrefix + 'nlg');
6859 if (!Ext.scopeResetCSS) {
6860 cls.push(baseCSSPrefix + 'reset');
6863 // add to the parent to allow for selectors x-strict x-border-box, also set the isBorderBox property correctly
6865 if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
6866 Ext.isBorderBox = false;
6869 Ext.isBorderBox = true;
6872 htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict'));
6873 if (!Ext.isStrict) {
6874 htmlCls.push(baseCSSPrefix + 'quirks');
6875 if (Ext.isIE && !Ext.isStrict) {
6876 Ext.isIEQuirks = true;
6879 Ext.fly(html, '_internal').addCls(htmlCls);
6882 Ext.fly(bd, '_internal').addCls(cls);
6886 Ext.onReady(initExtCss);
6890 * @class Ext.EventObject
6892 Just as {@link Ext.core.Element} wraps around a native DOM node, Ext.EventObject
6893 wraps the browser's native event-object normalizing cross-browser differences,
6894 such as which mouse button is clicked, keys pressed, mechanisms to stop
6895 event-propagation along with a method to prevent default actions from taking place.
6899 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
6901 var target = e.getTarget(); // same as t (the target HTMLElement)
6905 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.core.Element}
6906 myDiv.on( // 'on' is shorthand for addListener
6907 "click", // perform an action on click of myDiv
6908 handleClick // reference to the action handler
6911 // other methods to do the same:
6912 Ext.EventManager.on("myDiv", 'click', handleClick);
6913 Ext.EventManager.addListener("myDiv", 'click', handleClick);
6918 Ext.define('Ext.EventObjectImpl', {
6919 uses: ['Ext.util.Point'],
6921 /** Key constant @type Number */
6923 /** Key constant @type Number */
6925 /** Key constant @type Number */
6927 /** Key constant @type Number */
6929 /** Key constant @type Number */
6931 /** Key constant @type Number */
6933 /** Key constant @type Number */
6935 /** Key constant @type Number */
6937 /** Key constant @type Number */
6939 /** Key constant @type Number */
6941 /** Key constant @type Number */
6943 /** Key constant @type Number */
6945 /** Key constant @type Number */
6947 /** Key constant @type Number */
6949 /** Key constant @type Number */
6951 /** Key constant @type Number */
6953 /** Key constant @type Number */
6955 /** Key constant @type Number */
6957 /** Key constant @type Number */
6959 /** Key constant @type Number */
6961 /** Key constant @type Number */
6963 /** Key constant @type Number */
6965 /** Key constant @type Number */
6967 /** Key constant @type Number */
6969 /** Key constant @type Number */
6971 /** Key constant @type Number */
6973 /** Key constant @type Number */
6975 /** Key constant @type Number */
6977 /** Key constant @type Number */
6979 /** Key constant @type Number */
6981 /** Key constant @type Number */
6983 /** Key constant @type Number */
6985 /** Key constant @type Number */
6987 /** Key constant @type Number */
6989 /** Key constant @type Number */
6991 /** Key constant @type Number */
6993 /** Key constant @type Number */
6995 /** Key constant @type Number */
6997 /** Key constant @type Number */
6999 /** Key constant @type Number */
7001 /** Key constant @type Number */
7003 /** Key constant @type Number */
7005 /** Key constant @type Number */
7007 /** Key constant @type Number */
7009 /** Key constant @type Number */
7011 /** Key constant @type Number */
7013 /** Key constant @type Number */
7015 /** Key constant @type Number */
7017 /** Key constant @type Number */
7019 /** Key constant @type Number */
7021 /** Key constant @type Number */
7023 /** Key constant @type Number */
7025 /** Key constant @type Number */
7027 /** Key constant @type Number */
7029 /** Key constant @type Number */
7031 /** Key constant @type Number */
7033 /** Key constant @type Number */
7035 /** Key constant @type Number */
7037 /** Key constant @type Number */
7039 /** Key constant @type Number */
7041 /** Key constant @type Number */
7043 /** Key constant @type Number */
7045 /** Key constant @type Number */
7047 /** Key constant @type Number */
7049 /** Key constant @type Number */
7051 /** Key constant @type Number */
7053 /** Key constant @type Number */
7055 /** Key constant @type Number */
7057 /** Key constant @type Number */
7059 /** Key constant @type Number */
7061 /** Key constant @type Number */
7063 /** Key constant @type Number */
7065 /** Key constant @type Number */
7067 /** Key constant @type Number */
7069 /** Key constant @type Number */
7071 /** Key constant @type Number */
7073 /** Key constant @type Number */
7075 /** Key constant @type Number */
7077 /** Key constant @type Number */
7079 /** Key constant @type Number */
7081 /** Key constant @type Number */
7083 /** Key constant @type Number */
7085 /** Key constant @type Number */
7087 /** Key constant @type Number */
7089 /** Key constant @type Number */
7091 /** Key constant @type Number */
7093 /** Key constant @type Number */
7096 * The mouse wheel delta scaling factor. This value depends on browser version and OS and
7097 * attempts to produce a similar scrolling experience across all platforms and browsers.
7099 * To change this value:
7101 * Ext.EventObjectImpl.prototype.WHEEL_SCALE = 72;
7106 WHEEL_SCALE: (function () {
7110 // Firefox uses 3 on all platforms
7112 } else if (Ext.isMac) {
7113 // Continuous scrolling devices have momentum and produce much more scroll than
7114 // discrete devices on the same OS and browser. To make things exciting, Safari
7115 // (and not Chrome) changed from small values to 120 (like IE).
7117 if (Ext.isSafari && Ext.webKitVersion >= 532.0) {
7118 // Safari changed the scrolling factor to match IE (for details see
7119 // https://bugs.webkit.org/show_bug.cgi?id=24368). The WebKit version where this
7120 // change was introduced was 532.0
7121 // Detailed discussion:
7122 // https://bugs.webkit.org/show_bug.cgi?id=29601
7123 // http://trac.webkit.org/browser/trunk/WebKit/chromium/src/mac/WebInputEventFactory.mm#L1063
7126 // MS optical wheel mouse produces multiples of 12 which is close enough
7127 // to help tame the speed of the continuous mice...
7131 // Momentum scrolling produces very fast scrolling, so increase the scale factor
7132 // to help produce similar results cross platform. This could be even larger and
7133 // it would help those mice, but other mice would become almost unusable as a
7134 // result (since we cannot tell which device type is in use).
7137 // IE, Opera and other Windows browsers use 120.
7145 * Simple click regex
7148 clickRe: /(dbl)?click/,
7149 // safari keypress events for special keys return bad keycodes
7156 63276: 33, // page up
7157 63277: 34, // page down
7158 63272: 46, // delete
7162 // normalize button clicks, don't see any way to feature detect this.
7163 btnMap: Ext.isIE ? {
7173 constructor: function(event, freezeEvent){
7175 this.setEvent(event.browserEvent || event, freezeEvent);
7179 setEvent: function(event, freezeEvent){
7180 var me = this, button, options;
7182 if (event == me || (event && event.browserEvent)) { // already wrapped
7185 me.browserEvent = event;
7187 // normalize buttons
7188 button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
7189 if (me.clickRe.test(event.type) && button == -1) {
7195 shiftKey: event.shiftKey,
7196 // mac metaKey behaves like ctrlKey
7197 ctrlKey: event.ctrlKey || event.metaKey || false,
7198 altKey: event.altKey,
7199 // in getKey these will be normalized for the mac
7200 keyCode: event.keyCode,
7201 charCode: event.charCode,
7202 // cache the targets for the delayed and or buffered events
7203 target: Ext.EventManager.getTarget(event),
7204 relatedTarget: Ext.EventManager.getRelatedTarget(event),
7205 currentTarget: event.currentTarget,
7206 xy: (freezeEvent ? me.getXY() : null)
7220 Ext.apply(me, options);
7225 * Stop the event (preventDefault and stopPropagation)
7227 stopEvent: function(){
7228 this.stopPropagation();
7229 this.preventDefault();
7233 * Prevents the browsers default handling of the event.
7235 preventDefault: function(){
7236 if (this.browserEvent) {
7237 Ext.EventManager.preventDefault(this.browserEvent);
7242 * Cancels bubbling of the event.
7244 stopPropagation: function(){
7245 var browserEvent = this.browserEvent;
7248 if (browserEvent.type == 'mousedown') {
7249 Ext.EventManager.stoppedMouseDownEvent.fire(this);
7251 Ext.EventManager.stopPropagation(browserEvent);
7256 * Gets the character code for the event.
7259 getCharCode: function(){
7260 return this.charCode || this.keyCode;
7264 * Returns a normalized keyCode for the event.
7265 * @return {Number} The key code
7268 return this.normalizeKey(this.keyCode || this.charCode);
7272 * Normalize key codes across browsers
7274 * @param {Number} key The key code
7275 * @return {Number} The normalized code
7277 normalizeKey: function(key){
7278 // can't feature detect this
7279 return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
7283 * Gets the x coordinate of the event.
7285 * @deprecated 4.0 Replaced by {@link #getX}
7287 getPageX: function(){
7292 * Gets the y coordinate of the event.
7294 * @deprecated 4.0 Replaced by {@link #getY}
7296 getPageY: function(){
7301 * Gets the x coordinate of the event.
7305 return this.getXY()[0];
7309 * Gets the y coordinate of the event.
7313 return this.getXY()[1];
7317 * Gets the page coordinates of the event.
7318 * @return {Array} The xy values like [x, y]
7323 this.xy = Ext.EventManager.getPageXY(this.browserEvent);
7329 * Gets the target for the event.
7330 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7331 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
7332 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
7333 * @return {HTMLelement}
7335 getTarget : function(selector, maxDepth, returnEl){
7337 return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
7339 return returnEl ? Ext.get(this.target) : this.target;
7343 * Gets the related target.
7344 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7345 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
7346 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
7347 * @return {HTMLElement}
7349 getRelatedTarget : function(selector, maxDepth, returnEl){
7351 return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
7353 return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
7357 * Correctly scales a given wheel delta.
7358 * @param {Number} delta The delta value.
7360 correctWheelDelta : function (delta) {
7361 var scale = this.WHEEL_SCALE,
7362 ret = Math.round(delta / scale + 0.5);
7364 if (!ret && delta) {
7365 ret = (delta < 0) ? -1 : 1; // don't allow non-zero deltas to go to zero!
7372 * Returns the mouse wheel deltas for this event.
7373 * @return {Object} An object with "x" and "y" properties holding the mouse wheel deltas.
7375 getWheelDeltas : function () {
7377 event = me.browserEvent,
7378 dx = 0, dy = 0; // the deltas
7380 if (Ext.isDefined(event.wheelDeltaX)) { // WebKit has both dimensions
7381 dx = event.wheelDeltaX;
7382 dy = event.wheelDeltaY;
7383 } else if (event.wheelDelta) { // old WebKit and IE
7384 dy = event.wheelDelta;
7385 } else if (event.detail) { // Gecko
7386 dy = -event.detail; // gecko is backwards
7388 // Gecko sometimes returns really big values if the user changes settings to
7389 // scroll a whole page per scroll
7392 } else if (dy < -100) {
7396 // Firefox 3.1 adds an axis field to the event to indicate direction of
7397 // scroll. See https://developer.mozilla.org/en/Gecko-Specific_DOM_Events
7398 if (Ext.isDefined(event.axis) && event.axis === event.HORIZONTAL_AXIS) {
7405 x: me.correctWheelDelta(dx),
7406 y: me.correctWheelDelta(dy)
7411 * Normalizes mouse wheel y-delta across browsers. To get x-delta information, use
7412 * {@link #getWheelDeltas} instead.
7413 * @return {Number} The mouse wheel y-delta
7415 getWheelDelta : function(){
7416 var deltas = this.getWheelDeltas();
7422 * Returns true if the target of this event is a child of el. Unless the allowEl parameter is set, it will return false if if the target is el.
7423 * Example usage:<pre><code>
7424 // Handle click on any child of an element
7425 Ext.getBody().on('click', function(e){
7426 if(e.within('some-el')){
7427 alert('Clicked on a child of some-el!');
7431 // Handle click directly on an element, ignoring clicks on child nodes
7432 Ext.getBody().on('click', function(e,t){
7433 if((t.id == 'some-el') && !e.within(t, true)){
7434 alert('Clicked directly on some-el!');
7438 * @param {Mixed} el The id, DOM element or Ext.core.Element to check
7439 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7440 * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
7443 within : function(el, related, allowEl){
7445 var t = related ? this.getRelatedTarget() : this.getTarget(),
7449 result = Ext.fly(el).contains(t);
7450 if (!result && allowEl) {
7451 result = t == Ext.getDom(el);
7460 * Checks if the key pressed was a "navigation" key
7461 * @return {Boolean} True if the press is a navigation keypress
7463 isNavKeyPress : function(){
7465 k = this.normalizeKey(me.keyCode);
7467 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
7474 * Checks if the key pressed was a "special" key
7475 * @return {Boolean} True if the press is a special keypress
7477 isSpecialKey : function(){
7478 var k = this.normalizeKey(this.keyCode);
7479 return (this.type == 'keypress' && this.ctrlKey) ||
7480 this.isNavKeyPress() ||
7481 (k == this.BACKSPACE) || // Backspace
7482 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
7483 (k >= 44 && k <= 46); // Print Screen, Insert, Delete
7487 * Returns a point object that consists of the object coordinates.
7488 * @return {Ext.util.Point} point
7490 getPoint : function(){
7491 var xy = this.getXY();
7492 return Ext.create('Ext.util.Point', xy[0], xy[1]);
7496 * Returns true if the control, meta, shift or alt key was pressed during this event.
7499 hasModifier : function(){
7500 return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
7504 * Injects a DOM event using the data in this object and (optionally) a new target.
7505 * This is a low-level technique and not likely to be used by application code. The
7506 * currently supported event types are:
7507 * <p><b>HTMLEvents</b></p>
7518 * <p><b>MouseEvents</b></p>
7522 * <li>mousedown</li>
7524 * <li>mouseover</li>
7525 * <li>mousemove</li>
7528 * <p><b>UIEvents</b></p>
7536 * @param {Element/HTMLElement} target If specified, the target for the event. This
7537 * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
7538 * is used to determine the target.
7540 injectEvent: function () {
7542 dispatchers = {}; // keyed by event type (e.g., 'mousedown')
7544 // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
7546 // IE9 has createEvent, but this code causes major problems with htmleditor (it
7547 // blocks all mouse events and maybe more). TODO
7549 if (!Ext.isIE && document.createEvent) { // if (DOM compliant)
7551 createHtmlEvent: function (doc, type, bubbles, cancelable) {
7552 var event = doc.createEvent('HTMLEvents');
7554 event.initEvent(type, bubbles, cancelable);
7558 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
7559 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
7560 button, relatedTarget) {
7561 var event = doc.createEvent('MouseEvents'),
7562 view = doc.defaultView || window;
7564 if (event.initMouseEvent) {
7565 event.initMouseEvent(type, bubbles, cancelable, view, detail,
7566 clientX, clientY, clientX, clientY, ctrlKey, altKey,
7567 shiftKey, metaKey, button, relatedTarget);
7568 } else { // old Safari
7569 event = doc.createEvent('UIEvents');
7570 event.initEvent(type, bubbles, cancelable);
7572 event.detail = detail;
7573 event.screenX = clientX;
7574 event.screenY = clientY;
7575 event.clientX = clientX;
7576 event.clientY = clientY;
7577 event.ctrlKey = ctrlKey;
7578 event.altKey = altKey;
7579 event.metaKey = metaKey;
7580 event.shiftKey = shiftKey;
7581 event.button = button;
7582 event.relatedTarget = relatedTarget;
7588 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
7589 var event = doc.createEvent('UIEvents'),
7590 view = doc.defaultView || window;
7592 event.initUIEvent(type, bubbles, cancelable, view, detail);
7596 fireEvent: function (target, type, event) {
7597 target.dispatchEvent(event);
7600 fixTarget: function (target) {
7601 // Safari3 doesn't have window.dispatchEvent()
7602 if (target == window && !target.dispatchEvent) {
7609 } else if (document.createEventObject) { // else if (IE)
7610 var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
7613 createHtmlEvent: function (doc, type, bubbles, cancelable) {
7614 var event = doc.createEventObject();
7615 event.bubbles = bubbles;
7616 event.cancelable = cancelable;
7620 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
7621 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
7622 button, relatedTarget) {
7623 var event = doc.createEventObject();
7624 event.bubbles = bubbles;
7625 event.cancelable = cancelable;
7626 event.detail = detail;
7627 event.screenX = clientX;
7628 event.screenY = clientY;
7629 event.clientX = clientX;
7630 event.clientY = clientY;
7631 event.ctrlKey = ctrlKey;
7632 event.altKey = altKey;
7633 event.shiftKey = shiftKey;
7634 event.metaKey = metaKey;
7635 event.button = crazyIEButtons[button] || button;
7636 event.relatedTarget = relatedTarget; // cannot assign to/fromElement
7640 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
7641 var event = doc.createEventObject();
7642 event.bubbles = bubbles;
7643 event.cancelable = cancelable;
7647 fireEvent: function (target, type, event) {
7648 target.fireEvent('on' + type, event);
7651 fixTarget: function (target) {
7652 if (target == document) {
7653 // IE6,IE7 thinks window==document and doesn't have window.fireEvent()
7654 // IE6,IE7 cannot properly call document.fireEvent()
7655 return document.documentElement;
7667 load: [false, false],
7668 unload: [false, false],
7669 select: [true, false],
7670 change: [true, false],
7671 submit: [true, true],
7672 reset: [true, false],
7673 resize: [true, false],
7674 scroll: [true, false]
7676 function (name, value) {
7677 var bubbles = value[0], cancelable = value[1];
7678 dispatchers[name] = function (targetEl, srcEvent) {
7679 var e = API.createHtmlEvent(name, bubbles, cancelable);
7680 API.fireEvent(targetEl, name, e);
7687 function createMouseEventDispatcher (type, detail) {
7688 var cancelable = (type != 'mousemove');
7689 return function (targetEl, srcEvent) {
7690 var xy = srcEvent.getXY(),
7691 e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
7692 detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
7693 srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
7694 srcEvent.relatedTarget);
7695 API.fireEvent(targetEl, type, e);
7699 Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
7700 function (eventName) {
7701 dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
7708 focusin: [true, false],
7709 focusout: [true, false],
7710 activate: [true, true],
7711 focus: [false, false],
7712 blur: [false, false]
7714 function (name, value) {
7715 var bubbles = value[0], cancelable = value[1];
7716 dispatchers[name] = function (targetEl, srcEvent) {
7717 var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
7718 API.fireEvent(targetEl, name, e);
7724 // not even sure what ancient browsers fall into this category...
7726 dispatchers = {}; // never mind all those we just built :P
7729 fixTarget: function (t) {
7735 function cannotInject (target, srcEvent) {
7737 // TODO log something
7741 return function (target) {
7743 dispatcher = dispatchers[me.type] || cannotInject,
7744 t = target ? (target.dom || target) : me.getTarget();
7746 t = API.fixTarget(t);
7749 }() // call to produce method
7753 Ext.EventObject = new Ext.EventObjectImpl();
7759 * @class Ext.core.Element
7763 activeElement = null,
7764 isCSS1 = doc.compatMode == "CSS1Compat",
7765 ELEMENT = Ext.core.Element,
7768 _fly = new Ext.core.Element.Flyweight();
7774 // If the browser does not support document.activeElement we need some assistance.
7775 // This covers old Safari 3.2 (4.0 added activeElement along with just about all
7776 // other browsers). We need this support to handle issues with old Safari.
7777 if (!('activeElement' in doc) && doc.addEventListener) {
7778 doc.addEventListener('focus',
7780 if (ev && ev.target) {
7781 activeElement = (ev.target == doc) ? null : ev.target;
7787 * Helper function to create the function that will restore the selection.
7789 function makeSelectionRestoreFn (activeEl, start, end) {
7790 return function () {
7791 activeEl.selectionStart = start;
7792 activeEl.selectionEnd = end;
7796 Ext.apply(ELEMENT, {
7797 isAncestor : function(p, c) {
7804 return p.contains(c);
7805 } else if (p.compareDocumentPosition) {
7806 return !!(p.compareDocumentPosition(c) & 16);
7808 while ((c = c.parentNode)) {
7809 ret = c == p || ret;
7817 * Returns the active element in the DOM. If the browser supports activeElement
7818 * on the document, this is returned. If not, the focus is tracked and the active
7819 * element is maintained internally.
7820 * @return {HTMLElement} The active (focused) element in the document.
7822 getActiveElement: function () {
7823 return doc.activeElement || activeElement;
7827 * Creates a function to call to clean up problems with the work-around for the
7828 * WebKit RightMargin bug. The work-around is to add "display: 'inline-block'" to
7829 * the element before calling getComputedStyle and then to restore its original
7830 * display value. The problem with this is that it corrupts the selection of an
7831 * INPUT or TEXTAREA element (as in the "I-beam" goes away but ths focus remains).
7832 * To cleanup after this, we need to capture the selection of any such element and
7833 * then restore it after we have restored the display style.
7835 * @param target {Element} The top-most element being adjusted.
7838 getRightMarginFixCleaner: function (target) {
7839 var supports = Ext.supports,
7840 hasInputBug = supports.DisplayChangeInputSelectionBug,
7841 hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug;
7843 if (hasInputBug || hasTextAreaBug) {
7844 var activeEl = doc.activeElement || activeElement, // save a call
7845 tag = activeEl && activeEl.tagName,
7849 if ((hasTextAreaBug && tag == 'TEXTAREA') ||
7850 (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
7851 if (ELEMENT.isAncestor(target, activeEl)) {
7852 start = activeEl.selectionStart;
7853 end = activeEl.selectionEnd;
7855 if (Ext.isNumber(start) && Ext.isNumber(end)) { // to be safe...
7856 // We don't create the raw closure here inline because that
7857 // will be costly even if we don't want to return it (nested
7858 // function decls and exprs are often instantiated on entry
7859 // regardless of whether execution ever reaches them):
7860 return makeSelectionRestoreFn(activeEl, start, end);
7866 return Ext.emptyFn; // avoid special cases, just return a nop
7869 getViewWidth : function(full) {
7870 return full ? ELEMENT.getDocumentWidth() : ELEMENT.getViewportWidth();
7873 getViewHeight : function(full) {
7874 return full ? ELEMENT.getDocumentHeight() : ELEMENT.getViewportHeight();
7877 getDocumentHeight: function() {
7878 return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, ELEMENT.getViewportHeight());
7881 getDocumentWidth: function() {
7882 return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, ELEMENT.getViewportWidth());
7885 getViewportHeight: function(){
7887 (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
7891 getViewportWidth : function() {
7892 return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
7893 Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
7896 getY : function(el) {
7897 return ELEMENT.getXY(el)[1];
7900 getX : function(el) {
7901 return ELEMENT.getXY(el)[0];
7904 getXY : function(el) {
7915 bd = (doc.body || doc.documentElement),
7918 el = Ext.getDom(el);
7921 hasAbsolute = fly(el).isStyle("position", "absolute");
7923 if (el.getBoundingClientRect) {
7924 b = el.getBoundingClientRect();
7925 scroll = fly(document).getScroll();
7926 ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
7935 hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");
7938 y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
7939 x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
7941 if (p != el && !pe.isStyle('overflow','visible')) {
7949 if (Ext.isSafari && hasAbsolute) {
7954 if (Ext.isGecko && !hasAbsolute) {
7956 x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
7957 y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
7961 while (p && p != bd) {
7962 if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
7974 setXY : function(el, xy) {
7975 (el = Ext.fly(el, '_setXY')).position();
7977 var pts = el.translatePoints(xy),
7978 style = el.dom.style,
7982 if (!isNaN(pts[pos])) {
7983 style[pos] = pts[pos] + "px";
7988 setX : function(el, x) {
7989 ELEMENT.setXY(el, [x, false]);
7992 setY : function(el, y) {
7993 ELEMENT.setXY(el, [false, y]);
7997 * Serializes a DOM form into a url encoded string
7998 * @param {Object} form The form
7999 * @return {String} The url encoded form
8001 serializeForm: function(form) {
8002 var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
8004 encoder = encodeURIComponent,
8010 Ext.each(fElements, function(element){
8011 name = element.name;
8012 type = element.type;
8014 if (!element.disabled && name) {
8015 if (/select-(one|multiple)/i.test(type)) {
8016 Ext.each(element.options, function(opt){
8018 hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
8019 data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
8022 } else if (!(/file|undefined|reset|button/i.test(type))) {
8023 if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
8024 data += encoder(name) + '=' + encoder(element.value) + '&';
8025 hasSubmit = /submit/i.test(type);
8030 return data.substr(0, data.length - 1);
8036 * @class Ext.core.Element
8039 Ext.core.Element.addMethods({
8042 * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if
8043 * the mouse was not moved back into the Element within the delay. If the mouse <i>was</i> moved
8044 * back in, the function is not called.
8045 * @param {Number} delay The delay <b>in milliseconds</b> to wait for possible mouse re-entry before calling the handler function.
8046 * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time.
8047 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to this Element.
8048 * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:</pre><code>
8049 // Hide the menu if the mouse moves out for 250ms or more
8050 this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
8053 // Remove mouseleave monitor on menu destroy
8054 this.menuEl.un(this.mouseLeaveMonitor);
8057 monitorMouseLeave: function(delay, handler, scope) {
8061 mouseleave: function(e) {
8062 timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
8064 mouseenter: function() {
8065 clearTimeout(timer);
8075 * Stops the specified event(s) from bubbling and optionally prevents the default action
8076 * @param {String/Array} eventName an event / array of events to stop from bubbling
8077 * @param {Boolean} preventDefault (optional) true to prevent the default action too
8078 * @return {Ext.core.Element} this
8080 swallowEvent : function(eventName, preventDefault) {
8083 e.stopPropagation();
8084 if (preventDefault) {
8089 if (Ext.isArray(eventName)) {
8090 Ext.each(eventName, function(e) {
8095 me.on(eventName, fn);
8100 * Create an event handler on this element such that when the event fires and is handled by this element,
8101 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
8102 * @param {String} eventName The type of event to relay
8103 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
8104 * for firing the relayed event
8106 relayEvent : function(eventName, observable) {
8107 this.on(eventName, function(e) {
8108 observable.fireEvent(eventName, e);
8113 * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
8114 * @param {Boolean} forceReclean (optional) By default the element
8115 * keeps track if it has been cleaned already so
8116 * you can call this over and over. However, if you update the element and
8117 * need to force a reclean, you can pass true.
8119 clean : function(forceReclean) {
8126 if (Ext.core.Element.data(dom, 'isCleaned') && forceReclean !== true) {
8132 if (n.nodeType == 3) {
8133 // Remove empty/whitespace text nodes
8134 if (!(/\S/.test(n.nodeValue))) {
8136 // Combine adjacent text nodes
8137 } else if (nx && nx.nodeType == 3) {
8138 n.appendData(Ext.String.trim(nx.data));
8139 dom.removeChild(nx);
8144 // Recursively clean
8151 Ext.core.Element.data(dom, 'isCleaned', true);
8156 * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object
8157 * parameter as {@link Ext.ElementLoader#load}
8158 * @return {Ext.core.Element} this
8160 load : function(options) {
8161 this.getLoader().load(options);
8166 * Gets this element's {@link Ext.ElementLoader ElementLoader}
8167 * @return {Ext.ElementLoader} The loader
8169 getLoader : function() {
8171 data = Ext.core.Element.data,
8172 loader = data(dom, 'loader');
8175 loader = Ext.create('Ext.ElementLoader', {
8178 data(dom, 'loader', loader);
8184 * Update the innerHTML of this element, optionally searching for and processing scripts
8185 * @param {String} html The new HTML
8186 * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
8187 * @param {Function} callback (optional) For async script loading you can be notified when the update completes
8188 * @return {Ext.core.Element} this
8190 update : function(html, loadScripts, callback) {
8202 if (loadScripts !== true) {
8203 dom.innerHTML = html;
8204 Ext.callback(callback, me);
8209 html += '<span id="' + id + '"></span>';
8211 interval = setInterval(function(){
8212 if (!document.getElementById(id)) {
8215 clearInterval(interval);
8217 hd = DOC.getElementsByTagName("head")[0],
8218 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
8219 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
8220 typeRe = /\stype=([\'\"])(.*?)\1/i,
8228 while ((match = re.exec(html))) {
8230 srcMatch = attrs ? attrs.match(srcRe) : false;
8231 if (srcMatch && srcMatch[2]) {
8232 s = DOC.createElement("script");
8233 s.src = srcMatch[2];
8234 typeMatch = attrs.match(typeRe);
8235 if (typeMatch && typeMatch[2]) {
8236 s.type = typeMatch[2];
8239 } else if (match[2] && match[2].length > 0) {
8240 if (window.execScript) {
8241 window.execScript(match[2]);
8243 window.eval(match[2]);
8248 el = DOC.getElementById(id);
8252 Ext.callback(callback, me);
8254 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, '');
8258 // inherit docs, overridden so we can add removeAnchor
8259 removeAllListeners : function() {
8260 this.removeAnchor();
8261 Ext.EventManager.removeAll(this.dom);
8266 * Creates a proxy element of this element
8267 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8268 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8269 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8270 * @return {Ext.core.Element} The new proxy element
8272 createProxy : function(config, renderTo, matchBox) {
8273 config = (typeof config == 'object') ? config : {tag : "div", cls: config};
8276 proxy = renderTo ? Ext.core.DomHelper.append(renderTo, config, true) :
8277 Ext.core.DomHelper.insertBefore(me.dom, config, true);
8279 proxy.setVisibilityMode(Ext.core.Element.DISPLAY);
8281 if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
8282 proxy.setBox(me.getBox());
8287 Ext.core.Element.prototype.clearListeners = Ext.core.Element.prototype.removeAllListeners;
8290 * @class Ext.core.Element
8292 Ext.core.Element.addMethods({
8294 * Gets the x,y coordinates specified by the anchor position on the element.
8295 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo}
8296 * for details on supported anchor positions.
8297 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
8298 * of page coordinates
8299 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8300 * {width: (target width), height: (target height)} (defaults to the element's current size)
8301 * @return {Array} [x, y] An array containing the element's x and y coordinates
8303 getAnchorXY : function(anchor, local, s){
8304 //Passing a different size is useful for pre-calculating anchors,
8305 //especially for anchored animations that change the el size.
8306 anchor = (anchor || "tl").toLowerCase();
8310 vp = me.dom == document.body || me.dom == document,
8311 w = s.width || vp ? Ext.core.Element.getViewWidth() : me.getWidth(),
8312 h = s.height || vp ? Ext.core.Element.getViewHeight() : me.getHeight(),
8316 scroll = me.getScroll(),
8317 extraX = vp ? scroll.left : !local ? o[0] : 0,
8318 extraY = vp ? scroll.top : !local ? o[1] : 0,
8320 c : [r(w * 0.5), r(h * 0.5)],
8321 t : [r(w * 0.5), 0],
8322 l : [0, r(h * 0.5)],
8323 r : [w, r(h * 0.5)],
8324 b : [r(w * 0.5), h],
8332 return [xy[0] + extraX, xy[1] + extraY];
8336 * Anchors an element to another element and realigns it when the window is resized.
8337 * @param {Mixed} element The element to align to.
8338 * @param {String} position The position to align to.
8339 * @param {Array} offsets (optional) Offset the positioning by [x, y]
8340 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8341 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8342 * is a number, it is used as the buffer delay (defaults to 50ms).
8343 * @param {Function} callback The function to call after the animation finishes
8344 * @return {Ext.core.Element} this
8346 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8349 scroll = !Ext.isEmpty(monitorScroll),
8350 action = function(){
8351 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
8352 Ext.callback(callback, Ext.fly(dom));
8354 anchor = this.getAnchor();
8356 // previous listener anchor, remove it
8357 this.removeAnchor();
8363 Ext.EventManager.onWindowResize(action, null);
8366 Ext.EventManager.on(window, 'scroll', action, null,
8367 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
8369 action.call(me); // align immediately
8374 * Remove any anchor to this element. See {@link #anchorTo}.
8375 * @return {Ext.core.Element} this
8377 removeAnchor : function(){
8379 anchor = this.getAnchor();
8381 if(anchor && anchor.fn){
8382 Ext.EventManager.removeResizeListener(anchor.fn);
8384 Ext.EventManager.un(window, 'scroll', anchor.fn);
8392 getAnchor : function(){
8393 var data = Ext.core.Element.data,
8398 var anchor = data(dom, '_anchor');
8401 anchor = data(dom, '_anchor', {});
8406 getAlignVector: function(el, spec, offset) {
8408 side = {t:"top", l:"left", r:"right", b: "bottom"},
8409 thisRegion = me.getRegion(),
8416 sourceClass: 'Ext.core.Element',
8417 sourceMethod: 'getAlignVector',
8418 msg: 'Attempted to align an element that doesn\'t exist'
8423 elRegion = el.getRegion();
8427 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8428 * supported position values.
8429 * @param {Mixed} element The element to align to.
8430 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
8431 * @param {Array} offsets (optional) Offset the positioning by [x, y]
8432 * @return {Array} [x, y]
8434 getAlignToXY : function(el, p, o){
8440 sourceClass: 'Ext.core.Element',
8441 sourceMethod: 'getAlignToXY',
8442 msg: 'Attempted to align an element that doesn\'t exist'
8448 p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
8456 //constrain the aligned el to viewport if necessary
8460 dw = Ext.core.Element.getViewWidth() -10, // 10px of margin for ie
8461 dh = Ext.core.Element.getViewHeight()-10, // 10px of margin for ie
8469 docElement = doc.documentElement,
8471 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
8472 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
8473 c = false, //constrain to viewport
8476 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8481 sourceClass: 'Ext.core.Element',
8482 sourceMethod: 'getAlignToXY',
8486 msg: 'Attemmpted to align an element with an invalid position: "' + p + '"'
8495 //Subtract the aligned el's internal xy from the target's offset xy
8496 //plus custom offset to get the aligned el's new offset xy
8497 a1 = me.getAnchorXY(p1, true);
8498 a2 = el.getAnchorXY(p2, false);
8500 x = a2[0] - a1[0] + o[0];
8501 y = a2[1] - a1[1] + o[1];
8507 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8508 //perpendicular to the vp border, allow the aligned el to slide on that border,
8509 //otherwise swap the aligned el to the opposite border of the target.
8511 p1x = p1.charAt(p1.length-1);
8513 p2x = p2.charAt(p2.length-1);
8514 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8515 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8518 if (x + w > dw + scrollX) {
8519 x = swapX ? r.left-w : dw+scrollX-w;
8522 x = swapX ? r.right : scrollX;
8524 if (y + h > dh + scrollY) {
8525 y = swapY ? r.top-h : dh+scrollY-h;
8528 y = swapY ? r.bottom : scrollY;
8535 * Aligns this element with another element relative to the specified anchor points. If the other element is the
8536 * document it aligns it to the viewport.
8537 * The position parameter is optional, and can be specified in any one of the following formats:
8539 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8540 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8541 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
8542 * deprecated in favor of the newer two anchor syntax below</i>.</li>
8543 * <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8544 * element's anchor point, and the second value is used as the target's anchor point.</li>
8546 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
8547 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8548 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
8549 * that specified in order to enforce the viewport constraints.
8550 * Following are all of the supported anchor positions:
8553 ----- -----------------------------
8554 tl The top left corner (default)
8555 t The center of the top edge
8556 tr The top right corner
8557 l The center of the left edge
8558 c In the center of the element
8559 r The center of the right edge
8560 bl The bottom left corner
8561 b The center of the bottom edge
8562 br The bottom right corner
8566 // align el to other-el using the default positioning ("tl-bl", non-constrained)
8567 el.alignTo("other-el");
8569 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8570 el.alignTo("other-el", "tr?");
8572 // align the bottom right corner of el with the center left edge of other-el
8573 el.alignTo("other-el", "br-l?");
8575 // align the center of el with the bottom left corner of other-el and
8576 // adjust the x position by -6 pixels (and the y position by 0)
8577 el.alignTo("other-el", "c-bl", [-6, 0]);
8579 * @param {Mixed} element The element to align to.
8580 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
8581 * @param {Array} offsets (optional) Offset the positioning by [x, y]
8582 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8583 * @return {Ext.core.Element} this
8585 alignTo : function(element, position, offsets, animate){
8587 return me.setXY(me.getAlignToXY(element, position, offsets),
8588 me.anim && !!animate ? me.anim(animate) : false);
8591 // private ==> used outside of core
8592 adjustForConstraints : function(xy, parent) {
8593 var vector = this.getConstrainVector(parent, xy);
8602 * <p>Returns the <code>[X, Y]</code> vector by which this element must be translated to make a best attempt
8603 * to constrain within the passed constraint. Returns <code>false</code> is this element does not need to be moved.</p>
8604 * <p>Priority is given to constraining the top and left within the constraint.</p>
8605 * <p>The constraint may either be an existing element into which this element is to be constrained, or
8606 * an {@link Ext.util.Region Region} into which this element is to be constrained.</p>
8607 * @param constrainTo {Mixed} The Element or {@link Ext.util.Region Region} into which this element is to be constrained.
8608 * @param proposedPosition {Array} A proposed <code>[X, Y]</code> position to test for validity and to produce a vector for instead
8609 * of using this Element's current position;
8610 * @returns {Array} <b>If</b> this element <i>needs</i> to be translated, an <code>[X, Y]</code>
8611 * vector by which this element must be translated. Otherwise, <code>false</code>.
8613 getConstrainVector: function(constrainTo, proposedPosition) {
8614 if (!(constrainTo instanceof Ext.util.Region)) {
8615 constrainTo = Ext.get(constrainTo).getViewRegion();
8617 var thisRegion = this.getRegion(),
8619 shadowSize = this.shadow && this.shadow.offset,
8622 // Shift this region to occupy the proposed position
8623 if (proposedPosition) {
8624 thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
8627 // Reduce the constrain region to allow for shadow
8628 // TODO: Rewrite the Shadow class. When that's done, get the extra for each side from the Shadow.
8630 constrainTo.adjust(0, -shadowSize, -shadowSize, shadowSize);
8633 // Constrain the X coordinate by however much this Element overflows
8634 if (thisRegion.right > constrainTo.right) {
8636 vector[0] = (constrainTo.right - thisRegion.right); // overflowed the right
8638 if (thisRegion.left + vector[0] < constrainTo.left) {
8640 vector[0] = (constrainTo.left - thisRegion.left); // overflowed the left
8643 // Constrain the Y coordinate by however much this Element overflows
8644 if (thisRegion.bottom > constrainTo.bottom) {
8646 vector[1] = (constrainTo.bottom - thisRegion.bottom); // overflowed the bottom
8648 if (thisRegion.top + vector[1] < constrainTo.top) {
8650 vector[1] = (constrainTo.top - thisRegion.top); // overflowed the top
8652 return overflowed ? vector : false;
8656 * Calculates the x, y to center this element on the screen
8657 * @return {Array} The x, y values [x, y]
8659 getCenterXY : function(){
8660 return this.getAlignToXY(document, 'c-c');
8664 * Centers the Element in either the viewport, or another Element.
8665 * @param {Mixed} centerIn (optional) The element in which to center the element.
8667 center : function(centerIn){
8668 return this.alignTo(centerIn || document, 'c-c');
8673 * @class Ext.core.Element
8677 var ELEMENT = Ext.core.Element,
8682 POSITION = "position",
8684 RELATIVE = "relative",
8688 Ext.override(Ext.core.Element, {
8690 * Gets the current X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8691 * @return {Number} The X position of the element
8694 return ELEMENT.getX(this.dom);
8698 * Gets the current Y position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8699 * @return {Number} The Y position of the element
8702 return ELEMENT.getY(this.dom);
8706 * Gets the current position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8707 * @return {Array} The XY position of the element
8710 return ELEMENT.getXY(this.dom);
8714 * Returns the offsets of this element from the passed element. Both element must be part of the DOM tree and not have display:none to have page coordinates.
8715 * @param {Mixed} element The element to get the offsets from.
8716 * @return {Array} The XY page offsets (e.g. [100, -200])
8718 getOffsetsTo : function(el){
8719 var o = this.getXY(),
8720 e = Ext.fly(el, '_internal').getXY();
8721 return [o[0]-e[0],o[1]-e[1]];
8725 * Sets the X position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8726 * @param {Number} The X position of the element
8727 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8728 * @return {Ext.core.Element} this
8730 setX : function(x, animate){
8731 return this.setXY([x, this.getY()], animate);
8735 * Sets the Y position of the element based on page coordinates. Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8736 * @param {Number} The Y position of the element
8737 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8738 * @return {Ext.core.Element} this
8740 setY : function(y, animate){
8741 return this.setXY([this.getX(), y], animate);
8745 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8746 * @param {String} left The left CSS property value
8747 * @return {Ext.core.Element} this
8749 setLeft : function(left){
8750 this.setStyle(LEFT, this.addUnits(left));
8755 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8756 * @param {String} top The top CSS property value
8757 * @return {Ext.core.Element} this
8759 setTop : function(top){
8760 this.setStyle(TOP, this.addUnits(top));
8765 * Sets the element's CSS right style.
8766 * @param {String} right The right CSS property value
8767 * @return {Ext.core.Element} this
8769 setRight : function(right){
8770 this.setStyle(RIGHT, this.addUnits(right));
8775 * Sets the element's CSS bottom style.
8776 * @param {String} bottom The bottom CSS property value
8777 * @return {Ext.core.Element} this
8779 setBottom : function(bottom){
8780 this.setStyle(BOTTOM, this.addUnits(bottom));
8785 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8786 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8787 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8788 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8789 * @return {Ext.core.Element} this
8791 setXY: function(pos, animate) {
8793 if (!animate || !me.anim) {
8794 ELEMENT.setXY(me.dom, pos);
8797 if (!Ext.isObject(animate)) {
8800 me.animate(Ext.applyIf({ to: { x: pos[0], y: pos[1] } }, animate));
8806 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8807 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8808 * @param {Number} x X value for new position (coordinates are page-based)
8809 * @param {Number} y Y value for new position (coordinates are page-based)
8810 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8811 * @return {Ext.core.Element} this
8813 setLocation : function(x, y, animate){
8814 return this.setXY([x, y], animate);
8818 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8819 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8820 * @param {Number} x X value for new position (coordinates are page-based)
8821 * @param {Number} y Y value for new position (coordinates are page-based)
8822 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8823 * @return {Ext.core.Element} this
8825 moveTo : function(x, y, animate){
8826 return this.setXY([x, y], animate);
8830 * Gets the left X coordinate
8831 * @param {Boolean} local True to get the local css position instead of page coordinate
8834 getLeft : function(local){
8835 return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
8839 * Gets the right X coordinate of the element (element X position + element width)
8840 * @param {Boolean} local True to get the local css position instead of page coordinate
8843 getRight : function(local){
8845 return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
8849 * Gets the top Y coordinate
8850 * @param {Boolean} local True to get the local css position instead of page coordinate
8853 getTop : function(local) {
8854 return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
8858 * Gets the bottom Y coordinate of the element (element Y position + element height)
8859 * @param {Boolean} local True to get the local css position instead of page coordinate
8862 getBottom : function(local){
8864 return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
8868 * Initializes positioning on this element. If a desired position is not passed, it will make the
8869 * the element positioned relative IF it is not already positioned.
8870 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8871 * @param {Number} zIndex (optional) The zIndex to apply
8872 * @param {Number} x (optional) Set the page X position
8873 * @param {Number} y (optional) Set the page Y position
8875 position : function(pos, zIndex, x, y) {
8878 if (!pos && me.isStyle(POSITION, STATIC)){
8879 me.setStyle(POSITION, RELATIVE);
8881 me.setStyle(POSITION, pos);
8884 me.setStyle(ZINDEX, zIndex);
8887 me.setXY([x || false, y || false]);
8892 * Clear positioning back to the default when the document was loaded
8893 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8894 * @return {Ext.core.Element} this
8896 clearPositioning : function(value){
8897 value = value || '';
8910 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8911 * snapshot before performing an update and then restoring the element.
8914 getPositioning : function(){
8915 var l = this.getStyle(LEFT);
8916 var t = this.getStyle(TOP);
8918 "position" : this.getStyle(POSITION),
8920 "right" : l ? "" : this.getStyle(RIGHT),
8922 "bottom" : t ? "" : this.getStyle(BOTTOM),
8923 "z-index" : this.getStyle(ZINDEX)
8928 * Set positioning with an object returned by getPositioning().
8929 * @param {Object} posCfg
8930 * @return {Ext.core.Element} this
8932 setPositioning : function(pc){
8934 style = me.dom.style;
8938 if(pc.right == AUTO){
8941 if(pc.bottom == AUTO){
8949 * Translates the passed page coordinates into left/top css values for this element
8950 * @param {Number/Array} x The page x or an array containing [x, y]
8951 * @param {Number} y (optional) The page y, required if x is not an array
8952 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
8954 translatePoints: function(x, y) {
8955 if (Ext.isArray(x)) {
8960 relative = me.isStyle(POSITION, RELATIVE),
8962 left = parseInt(me.getStyle(LEFT), 10),
8963 top = parseInt(me.getStyle(TOP), 10);
8965 if (!Ext.isNumber(left)) {
8966 left = relative ? 0 : me.dom.offsetLeft;
8968 if (!Ext.isNumber(top)) {
8969 top = relative ? 0 : me.dom.offsetTop;
8971 left = (Ext.isNumber(x)) ? x - o[0] + left : undefined;
8972 top = (Ext.isNumber(y)) ? y - o[1] + top : undefined;
8980 * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8981 * @param {Object} box The box to fill {x, y, width, height}
8982 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8983 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8984 * @return {Ext.core.Element} this
8986 setBox: function(box, adjust, animate) {
8990 if ((adjust && !me.autoBoxAdjust) && !me.isBorderBox()) {
8991 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
8992 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
8994 me.setBounds(box.x, box.y, w, h, animate);
8999 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
9000 * set another Element's size/location to match this element.
9001 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9002 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9003 * @return {Object} box An object in the format<pre><code>
9005 x: <Element's X position>,
9006 y: <Element's Y position>,
9007 width: <Element's width>,
9008 height: <Element's height>,
9009 bottom: <Element's lower bound>,
9010 right: <Element's rightmost bound>
9013 * The returned object may also be addressed as an Array where index 0 contains the X position
9014 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
9016 getBox: function(contentBox, local) {
9021 getBorderWidth = me.getBorderWidth,
9022 getPadding = me.getPadding,
9023 l, r, t, b, w, h, bx;
9027 left = parseInt(me.getStyle("left"), 10) || 0;
9028 top = parseInt(me.getStyle("top"), 10) || 0;
9043 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
9044 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
9045 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
9046 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
9056 bx.right = bx.x + bx.width;
9057 bx.bottom = bx.y + bx.height;
9062 * Move this element relative to its current position.
9063 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
9064 * @param {Number} distance How far to move the element in pixels
9065 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9066 * @return {Ext.core.Element} this
9068 move: function(direction, distance, animate) {
9073 left = [x - distance, y],
9074 right = [x + distance, y],
9075 top = [x, y - distance],
9076 bottom = [x, y + distance],
9090 direction = direction.toLowerCase();
9091 me.moveTo(hash[direction][0], hash[direction][1], animate);
9095 * Quick set left and top adding default units
9096 * @param {String} left The left CSS property value
9097 * @param {String} top The top CSS property value
9098 * @return {Ext.core.Element} this
9100 setLeftTop: function(left, top) {
9102 style = me.dom.style;
9103 style.left = me.addUnits(left);
9104 style.top = me.addUnits(top);
9109 * Returns the region of this element.
9110 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9111 * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
9113 getRegion: function() {
9114 return this.getPageBox(true);
9118 * Returns the <b>content</b> region of this element. That is the region within the borders and padding.
9119 * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
9121 getViewRegion: function() {
9123 isBody = me.dom === document.body,
9124 scroll, pos, top, left, width, height;
9126 // For the body we want to do some special logic
9128 scroll = me.getScroll();
9131 width = Ext.core.Element.getViewportWidth();
9132 height = Ext.core.Element.getViewportHeight();
9136 left = pos[0] + me.getBorderWidth('l') + me.getPadding('l');
9137 top = pos[1] + me.getBorderWidth('t') + me.getPadding('t');
9138 width = me.getWidth(true);
9139 height = me.getHeight(true);
9142 return Ext.create('Ext.util.Region', top, left + width, top + height, left);
9146 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
9147 * set another Element's size/location to match this element.
9148 * @param {Boolean} asRegion(optional) If true an Ext.util.Region will be returned
9149 * @return {Object} box An object in the format<pre><code>
9151 x: <Element's X position>,
9152 y: <Element's Y position>,
9153 width: <Element's width>,
9154 height: <Element's height>,
9155 bottom: <Element's lower bound>,
9156 right: <Element's rightmost bound>
9159 * The returned object may also be addressed as an Array where index 0 contains the X position
9160 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
9162 getPageBox : function(getRegion) {
9165 isDoc = el === document.body,
9166 w = isDoc ? Ext.core.Element.getViewWidth() : el.offsetWidth,
9167 h = isDoc ? Ext.core.Element.getViewHeight() : el.offsetHeight,
9175 return Ext.create('Ext.util.Region', t, r, b, l);
9190 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9191 * @param {Number} x X value for new position (coordinates are page-based)
9192 * @param {Number} y Y value for new position (coordinates are page-based)
9193 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
9194 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
9195 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
9197 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
9198 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
9199 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
9201 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9202 * @return {Ext.core.Element} this
9204 setBounds: function(x, y, width, height, animate) {
9206 if (!animate || !me.anim) {
9207 me.setSize(width, height);
9208 me.setLocation(x, y);
9210 if (!Ext.isObject(animate)) {
9213 me.animate(Ext.applyIf({
9217 width: me.adjustWidth(width),
9218 height: me.adjustHeight(height)
9226 * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
9227 * @param {Ext.util.Region} region The region to fill
9228 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9229 * @return {Ext.core.Element} this
9231 setRegion: function(region, animate) {
9232 return this.setBounds(region.left, region.top, region.right - region.left, region.bottom - region.top, animate);
9238 * @class Ext.core.Element
9240 Ext.override(Ext.core.Element, {
9242 * Returns true if this element is scrollable.
9245 isScrollable : function(){
9247 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9251 * Returns the current scroll position of the element.
9252 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9254 getScroll : function() {
9258 docElement = doc.documentElement,
9263 if (d == doc || d == body) {
9264 if (Ext.isIE && Ext.isStrict) {
9265 l = docElement.scrollLeft;
9266 t = docElement.scrollTop;
9268 l = window.pageXOffset;
9269 t = window.pageYOffset;
9272 left: l || (body ? body.scrollLeft : 0),
9273 top : t || (body ? body.scrollTop : 0)
9286 * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9287 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9288 * @param {Number} value The new scroll value
9289 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9290 * @return {Element} this
9292 scrollTo : function(side, value, animate) {
9293 //check if we're scrolling top or left
9294 var top = /top/i.test(side),
9299 if (!animate || !me.anim) {
9300 // just setting the value, so grab the direction
9301 prop = 'scroll' + (top ? 'Top' : 'Left');
9305 if (!Ext.isObject(animate)) {
9308 obj['scroll' + (top ? 'Top' : 'Left')] = value;
9309 me.animate(Ext.applyIf({
9317 * Scrolls this element into view within the passed container.
9318 * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a
9319 * string (id), dom node, or Ext.core.Element.
9320 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
9321 * @return {Ext.core.Element} this
9323 scrollIntoView : function(container, hscroll) {
9324 container = Ext.getDom(container) || Ext.getBody().dom;
9326 offsets = this.getOffsetsTo(container),
9328 left = offsets[0] + container.scrollLeft,
9329 top = offsets[1] + container.scrollTop,
9330 bottom = top + el.offsetHeight,
9331 right = left + el.offsetWidth,
9333 ctClientHeight = container.clientHeight,
9334 ctScrollTop = parseInt(container.scrollTop, 10),
9335 ctScrollLeft = parseInt(container.scrollLeft, 10),
9336 ctBottom = ctScrollTop + ctClientHeight,
9337 ctRight = ctScrollLeft + container.clientWidth;
9339 if (el.offsetHeight > ctClientHeight || top < ctScrollTop) {
9340 container.scrollTop = top;
9341 } else if (bottom > ctBottom) {
9342 container.scrollTop = bottom - ctClientHeight;
9344 // corrects IE, other browsers will ignore
9345 container.scrollTop = container.scrollTop;
9347 if (hscroll !== false) {
9348 if (el.offsetWidth > container.clientWidth || left < ctScrollLeft) {
9349 container.scrollLeft = left;
9351 else if (right > ctRight) {
9352 container.scrollLeft = right - container.clientWidth;
9354 container.scrollLeft = container.scrollLeft;
9360 scrollChildIntoView : function(child, hscroll) {
9361 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
9365 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9366 * within this element's scrollable range.
9367 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
9368 * @param {Number} distance How far to scroll the element in pixels
9369 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9370 * @return {Boolean} Returns true if a scroll was triggered or false if the element
9371 * was scrolled as far as it could go.
9373 scroll : function(direction, distance, animate) {
9374 if (!this.isScrollable()) {
9378 l = el.scrollLeft, t = el.scrollTop,
9379 w = el.scrollWidth, h = el.scrollHeight,
9380 cw = el.clientWidth, ch = el.clientHeight,
9381 scrolled = false, v,
9383 l: Math.min(l + distance, w-cw),
9384 r: v = Math.max(l - distance, 0),
9385 t: Math.max(t - distance, 0),
9386 b: Math.min(t + distance, h-ch)
9391 direction = direction.substr(0, 1);
9392 if ((v = hash[direction]) > -1) {
9394 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.anim(animate));
9400 * @class Ext.core.Element
9402 Ext.core.Element.addMethods(
9404 var VISIBILITY = "visibility",
9405 DISPLAY = "display",
9408 XMASKED = Ext.baseCSSPrefix + "masked",
9409 XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
9410 data = Ext.core.Element.data;
9414 * Checks whether the element is currently visible using both visibility and display properties.
9415 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
9416 * @return {Boolean} True if the element is currently visible, else false
9418 isVisible : function(deep) {
9419 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
9420 p = this.dom.parentNode;
9422 if (deep !== true || !vis) {
9426 while (p && !(/^body/i.test(p.tagName))) {
9427 if (!Ext.fly(p, '_isVisible').isVisible()) {
9436 * Returns true if display is not "none"
9439 isDisplayed : function() {
9440 return !this.isStyle(DISPLAY, NONE);
9444 * Convenience method for setVisibilityMode(Element.DISPLAY)
9445 * @param {String} display (optional) What to set display to when visible
9446 * @return {Ext.core.Element} this
9448 enableDisplayMode : function(display) {
9449 this.setVisibilityMode(Ext.core.Element.DISPLAY);
9451 if (!Ext.isEmpty(display)) {
9452 data(this.dom, 'originalDisplay', display);
9459 * Puts a mask over this element to disable user interaction. Requires core.css.
9460 * This method can only be applied to elements which accept child nodes.
9461 * @param {String} msg (optional) A message to display in the mask
9462 * @param {String} msgCls (optional) A css class to apply to the msg element
9463 * @return {Element} The mask element
9465 mask : function(msg, msgCls) {
9468 setExpression = dom.style.setExpression,
9469 dh = Ext.core.DomHelper,
9470 EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
9474 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
9475 me.addCls(XMASKEDRELATIVE);
9477 el = data(dom, 'maskMsg');
9481 el = data(dom, 'mask');
9486 mask = dh.append(dom, {cls : Ext.baseCSSPrefix + "mask"}, true);
9487 data(dom, 'mask', mask);
9490 mask.setDisplayed(true);
9492 if (typeof msg == 'string') {
9493 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
9494 data(dom, 'maskMsg', mm);
9495 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
9496 mm.dom.firstChild.innerHTML = msg;
9497 mm.setDisplayed(true);
9500 // NOTE: CSS expressions are resource intensive and to be used only as a last resort
9501 // These expressions are removed as soon as they are no longer necessary - in the unmask method.
9502 // In normal use cases an element will be masked for a limited period of time.
9503 // Fix for https://sencha.jira.com/browse/EXTJSIV-19.
9504 // IE6 strict mode and IE6-9 quirks mode takes off left+right padding when calculating width!
9505 if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
9506 mask.dom.style.setExpression('width', 'this.parentNode.offsetWidth + "px"');
9509 // Some versions and modes of IE subtract top+bottom padding when calculating height.
9510 // Different versions from those which make the same error for width!
9511 if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
9512 mask.dom.style.setExpression('height', 'this.parentNode.offsetHeight + "px"');
9514 // ie will not expand full height automatically
9515 else if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
9516 mask.setSize(undefined, me.getHeight());
9522 * Removes a previously applied mask.
9524 unmask : function() {
9527 mask = data(dom, 'mask'),
9528 maskMsg = data(dom, 'maskMsg');
9531 // Remove resource-intensive CSS expressions as soon as they are not required.
9532 if (mask.dom.style.clearExpression) {
9533 mask.dom.style.clearExpression('width');
9534 mask.dom.style.clearExpression('height');
9538 data(dom, 'maskMsg', undefined);
9542 data(dom, 'mask', undefined);
9543 me.removeCls([XMASKED, XMASKEDRELATIVE]);
9547 * Returns true if this element is masked. Also re-centers any displayed message within the mask.
9550 isMasked : function() {
9552 mask = data(me.dom, 'mask'),
9553 maskMsg = data(me.dom, 'maskMsg');
9555 if (mask && mask.isVisible()) {
9565 * Creates an iframe shim for this element to keep selects and other windowed objects from
9567 * @return {Ext.core.Element} The new shim element
9569 createShim : function() {
9570 var el = document.createElement('iframe'),
9573 el.frameBorder = '0';
9574 el.className = Ext.baseCSSPrefix + 'shim';
9575 el.src = Ext.SSL_SECURE_URL;
9576 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
9577 shim.autoBoxAdjust = false;
9584 * @class Ext.core.Element
9586 Ext.core.Element.addMethods({
9588 * Convenience method for constructing a KeyMap
9589 * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9590 * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
9591 * @param {Function} fn The function to call
9592 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
9593 * @return {Ext.util.KeyMap} The KeyMap created
9595 addKeyListener : function(key, fn, scope){
9597 if(typeof key != 'object' || Ext.isArray(key)){
9613 return Ext.create('Ext.util.KeyMap', this, config);
9617 * Creates a KeyMap for this element
9618 * @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details
9619 * @return {Ext.util.KeyMap} The KeyMap created
9621 addKeyMap : function(config){
9622 return Ext.create('Ext.util.KeyMap', this, config);
9626 //Import the newly-added Ext.core.Element functions into CompositeElementLite. We call this here because
9627 //Element.keys.js is the last extra Ext.core.Element include in the ext-all.js build
9628 Ext.CompositeElementLite.importElementMethods();
9631 * @class Ext.CompositeElementLite
9633 Ext.apply(Ext.CompositeElementLite.prototype, {
9634 addElements : function(els, root){
9638 if(typeof els == "string"){
9639 els = Ext.core.Element.selectorFunction(els, root);
9641 var yels = this.elements;
9642 Ext.each(els, function(e) {
9643 yels.push(Ext.get(e));
9649 * Returns the first Element
9650 * @return {Ext.core.Element}
9653 return this.item(0);
9657 * Returns the last Element
9658 * @return {Ext.core.Element}
9661 return this.item(this.getCount()-1);
9665 * Returns true if this composite contains the passed element
9666 * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
9669 contains : function(el){
9670 return this.indexOf(el) != -1;
9674 * Removes the specified element(s).
9675 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9676 * or an array of any of those.
9677 * @param {Boolean} removeDom (optional) True to also remove the element from the document
9678 * @return {CompositeElement} this
9680 removeElement : function(keys, removeDom){
9682 els = this.elements,
9684 Ext.each(keys, function(val){
9685 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
9693 Ext.Array.erase(els, val, 1);
9701 * @class Ext.CompositeElement
9702 * @extends Ext.CompositeElementLite
9703 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
9704 * members, or to perform collective actions upon the whole set.</p>
9705 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
9706 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
9707 * <p>All methods return <i>this</i> and can be chained.</p>
9710 var els = Ext.select("#some-el div.some-class", true);
9711 // or select directly from an existing element
9712 var el = Ext.get('some-el');
9713 el.select('div.some-class', true);
9715 els.setWidth(100); // all elements become 100 width
9716 els.hide(true); // all elements fade out and hide
9718 els.setWidth(100).hide(true);
9721 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
9723 constructor : function(els, root){
9725 this.add(els, root);
9729 getElement : function(el){
9730 // In this case just return it, since we already have a reference to it
9735 transformElement : function(el){
9740 * Adds elements to this composite.
9741 * @param {String/Array} els A string CSS selector, an array of elements or an element
9742 * @return {CompositeElement} this
9746 * Returns the Element object at the specified index
9747 * @param {Number} index
9748 * @return {Ext.core.Element}
9752 * Iterates each `element` in this `composite` calling the supplied function using {@link Ext#each Ext.each}.
9753 * @param {Function} fn
9755 The function to be called with each
9756 `element`. If the supplied function returns <tt>false</tt>,
9757 iteration stops. This function is called with the following arguments:
9759 - `element` : __Ext.core.Element++
9760 The element at the current `index` in the `composite`
9762 - `composite` : __Object__
9765 - `index` : __Number__
9766 The current index within the `composite`
9768 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed.
9769 * Defaults to the <code>element</code> at the current <code>index</code>
9770 * within the composite.
9771 * @return {CompositeElement} this
9777 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
9778 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9779 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9780 * @param {String/Array} selector The CSS selector or an array of elements
9781 * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
9782 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9783 * @return {CompositeElementLite/CompositeElement}
9784 * @member Ext.core.Element
9787 Ext.core.Element.select = function(selector, unique, root){
9789 if(typeof selector == "string"){
9790 els = Ext.core.Element.selectorFunction(selector, root);
9791 }else if(selector.length !== undefined){
9796 sourceClass: "Ext.core.Element",
9797 sourceMethod: "select",
9801 msg: "Invalid selector specified: " + selector
9805 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
9809 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
9810 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9811 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9812 * @param {String/Array} selector The CSS selector or an array of elements
9813 * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
9814 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9815 * @return {CompositeElementLite/CompositeElement}
9819 Ext.select = Ext.core.Element.select;