2 Ext JS - JavaScript Library
3 Copyright (c) 2006-2011, Sencha Inc.
8 * @class Ext.core.DomHelper
9 * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
10 * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
11 * from your DOM building code.</p>
13 * <p><b><u>DomHelper element specification object</u></b></p>
14 * <p>A specification object is used when creating elements. Attributes of this object
15 * are assumed to be element attributes, except for 4 special attributes:
16 * <div class="mdetail-params"><ul>
17 * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
18 * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
19 * same kind of element definition objects to be created and appended. These can be nested
20 * as deep as you want.</div></li>
21 * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
22 * This will end up being either the "class" attribute on a HTML fragment or className
23 * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
24 * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
27 * <p><b><u>Insertion methods</u></b></p>
28 * <p>Commonly used insertion methods:
29 * <div class="mdetail-params"><ul>
30 * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
31 * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
32 * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
33 * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
34 * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
35 * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
38 * <p><b><u>Example</u></b></p>
39 * <p>This is an example, where an unordered list with 3 children items is appended to an existing
40 * element with id <tt>'my-div'</tt>:<br>
42 var dh = Ext.core.DomHelper; // create shorthand alias
43 // specification object
48 // append children after creating
49 children: [ // may also specify 'cn' instead of 'children'
50 {tag: 'li', id: 'item0', html: 'List Item 0'},
51 {tag: 'li', id: 'item1', html: 'List Item 1'},
52 {tag: 'li', id: 'item2', html: 'List Item 2'}
56 'my-div', // the context element 'my-div' can either be the id or the actual node
57 spec // the specification object
60 * <p>Element creation specification parameters in this class may also be passed as an Array of
61 * specification objects. This can be used to insert multiple sibling nodes into an existing
62 * container very efficiently. For example, to add more list items to the example above:<pre><code>
64 {tag: 'li', id: 'item3', html: 'List Item 3'},
65 {tag: 'li', id: 'item4', html: 'List Item 4'}
69 * <p><b><u>Templating</u></b></p>
70 * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
71 * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
72 * insert new elements. Revisiting the example above, we could utilize templating this time:
75 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
77 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
79 for(var i = 0; i < 5, i++){
80 tpl.append(list, [i]); // use template to append to the actual node
83 * <p>An example using a template:<pre><code>
84 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
86 var tpl = new Ext.core.DomHelper.createTemplate(html);
87 tpl.append('blog-roll', ['link1', 'http://www.edspencer.net/', "Ed's Site"]);
88 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
91 * <p>The same example using named parameters:<pre><code>
92 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
94 var tpl = new Ext.core.DomHelper.createTemplate(html);
95 tpl.append('blog-roll', {
97 url: 'http://www.edspencer.net/',
100 tpl.append('blog-roll', {
102 url: 'http://www.dustindiaz.com/',
103 text: "Dustin's Site"
107 * <p><b><u>Compiling Templates</u></b></p>
108 * <p>Templates are applied using regular expressions. The performance is great, but if
109 * you are adding a bunch of DOM elements using the same template, you can increase
110 * performance even further by {@link Ext.Template#compile "compiling"} the template.
111 * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
112 * broken up at the different variable points and a dynamic function is created and eval'ed.
113 * The generated function performs string concatenation of these parts and the passed
114 * variables instead of using regular expressions.
116 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
118 var tpl = new Ext.core.DomHelper.createTemplate(html);
121 //... use template like normal
124 * <p><b><u>Performance Boost</u></b></p>
125 * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
126 * of DOM can significantly boost performance.</p>
127 * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
128 * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
129 * results in the creation of a text node. Usage:</p>
131 Ext.core.DomHelper.useDom = true; // force it to use DOM; reduces performance
136 Ext.core.DomHelper = function(){
137 var tempTableEl = null,
138 emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
139 tableRe = /^table|tbody|tr|td$/i,
140 confRe = /tag|children|cn|html$/i,
141 tableElRe = /td|tr|tbody/i,
144 // kill repeat to save bytes
145 afterbegin = 'afterbegin',
146 afterend = 'afterend',
147 beforebegin = 'beforebegin',
148 beforeend = 'beforeend',
157 function doInsert(el, o, returnElement, pos, sibling, append){
161 newNode = createDom(o, null);
163 el.appendChild(newNode);
165 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
168 newNode = Ext.core.DomHelper.insertHtml(pos, el, Ext.core.DomHelper.createHtml(o));
170 return returnElement ? Ext.get(newNode, true) : newNode;
173 function createDom(o, parentNode){
181 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
182 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
183 for (var i = 0, l = o.length; i < l; i++) {
186 } else if (typeof o == 'string') { // Allow a string as a child spec.
187 el = doc.createTextNode(o);
189 el = doc.createElement( o.tag || 'div' );
190 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
192 if(!confRe.test(attr)){
198 el.setAttribute(attr, val);
205 Ext.core.DomHelper.applyStyles(el, o.style);
207 if ((cn = o.children || o.cn)) {
210 el.innerHTML = o.html;
214 parentNode.appendChild(el);
219 // build as innerHTML where available
220 function createHtml(o){
228 if(typeof o == "string"){
230 } else if (Ext.isArray(o)) {
231 for (i=0; i < o.length; i++) {
233 b += createHtml(o[i]);
237 b += '<' + (o.tag = o.tag || 'div');
240 if(!confRe.test(attr)){
241 if (typeof val == "object") {
242 b += ' ' + attr + '="';
244 b += key + ':' + val[key] + ';';
248 b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
252 // Now either just close the tag or try to add children and close the tag.
253 if (emptyTags.test(o.tag)) {
257 if ((cn = o.children || o.cn)) {
262 b += '</' + o.tag + '>';
268 function ieTable(depth, s, h, e){
269 tempTableEl.innerHTML = [s, h, e].join('');
276 // If the result is multiple siblings, then encapsulate them into one fragment.
279 var df = document.createDocumentFragment();
292 * Nasty code for IE's broken table implementation
294 function insertIntoTable(tag, where, el, html) {
298 tempTableEl = tempTableEl || document.createElement('div');
300 if(tag == 'td' && (where == afterbegin || where == beforeend) ||
301 !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
304 before = where == beforebegin ? el :
305 where == afterend ? el.nextSibling :
306 where == afterbegin ? el.firstChild : null;
308 if (where == beforebegin || where == afterend) {
312 if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
313 node = ieTable(4, trs, html, tre);
314 } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
315 (tag == 'tr' && (where == beforebegin || where == afterend))) {
316 node = ieTable(3, tbs, html, tbe);
318 node = ieTable(2, ts, html, te);
320 el.insertBefore(node, before);
326 * Fix for IE9 createContextualFragment missing method
328 function createContextualFragment(html){
329 var div = document.createElement("div"),
330 fragment = document.createDocumentFragment(),
334 div.innerHTML = html;
335 childNodes = div.childNodes;
336 length = childNodes.length;
338 for (; i < length; i++) {
339 fragment.appendChild(childNodes[i].cloneNode(true));
347 * Returns the markup for the passed Element(s) config.
348 * @param {Object} o The DOM object spec (and children)
351 markup : function(o){
352 return createHtml(o);
356 * Applies a style specification to an element.
357 * @param {String/HTMLElement} el The element to apply styles to
358 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
359 * a function which returns such a specification.
361 applyStyles : function(el, styles){
364 if (typeof styles == "function") {
365 styles = styles.call();
367 if (typeof styles == "string") {
368 styles = Ext.core.Element.parseStyles(styles);
370 if (typeof styles == "object") {
377 * Inserts an HTML fragment into the DOM.
378 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
379 * @param {HTMLElement/TextNode} el The context element
380 * @param {String} html The HTML fragment
381 * @return {HTMLElement} The new node
383 insertHtml : function(where, el, html){
392 where = where.toLowerCase();
393 // add these here because they are used in both branches of the condition.
394 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
395 hash[afterend] = ['AfterEnd', 'nextSibling'];
397 // if IE and context element is an HTMLElement
398 if (el.insertAdjacentHTML) {
399 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
403 // add these two to the hash.
404 hash[afterbegin] = ['AfterBegin', 'firstChild'];
405 hash[beforeend] = ['BeforeEnd', 'lastChild'];
406 if ((hashVal = hash[where])) {
407 el.insertAdjacentHTML(hashVal[0], html);
408 return el[hashVal[1]];
410 // if (not IE and context element is an HTMLElement) or TextNode
412 // we cannot insert anything inside a textnode so...
413 if (Ext.isTextNode(el)) {
414 where = where === 'afterbegin' ? 'beforebegin' : where;
415 where = where === 'beforeend' ? 'afterend' : where;
417 range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
418 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
422 frag = range.createContextualFragment(html);
424 frag = createContextualFragment(html);
426 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
427 return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
429 rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
432 range[setStart](el[rangeEl]);
433 frag = range.createContextualFragment(html);
435 frag = createContextualFragment(html);
438 if(where == afterbegin){
439 el.insertBefore(frag, el.firstChild);
441 el.appendChild(frag);
451 sourceClass: 'Ext.core.DomHelper',
452 sourceMethod: 'insertHtml',
455 msg: 'Illegal insertion point reached: "' + where + '"'
461 * Creates new DOM element(s) and inserts them before el.
462 * @param {Mixed} el The context element
463 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
464 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
465 * @return {HTMLElement/Ext.core.Element} The new node
467 insertBefore : function(el, o, returnElement){
468 return doInsert(el, o, returnElement, beforebegin);
472 * Creates new DOM element(s) and inserts them after el.
473 * @param {Mixed} el The context element
474 * @param {Object} o The DOM object spec (and children)
475 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
476 * @return {HTMLElement/Ext.core.Element} The new node
478 insertAfter : function(el, o, returnElement){
479 return doInsert(el, o, returnElement, afterend, 'nextSibling');
483 * Creates new DOM element(s) and inserts them as the first child of el.
484 * @param {Mixed} el The context element
485 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
486 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
487 * @return {HTMLElement/Ext.core.Element} The new node
489 insertFirst : function(el, o, returnElement){
490 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
494 * Creates new DOM element(s) and appends them to el.
495 * @param {Mixed} el The context element
496 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
497 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
498 * @return {HTMLElement/Ext.core.Element} The new node
500 append : function(el, o, returnElement){
501 return doInsert(el, o, returnElement, beforeend, '', true);
505 * Creates new DOM element(s) and overwrites the contents of el with them.
506 * @param {Mixed} el The context element
507 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
508 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
509 * @return {HTMLElement/Ext.core.Element} The new node
511 overwrite : function(el, o, returnElement){
513 el.innerHTML = createHtml(o);
514 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
517 createHtml : createHtml,
520 * Creates new DOM element(s) without inserting them to the document.
521 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
522 * @return {HTMLElement} The new uninserted node
524 createDom: createDom,
526 /** True to force the use of DOM instead of html fragments @type Boolean */
530 * Creates a new Ext.Template from the DOM object spec.
531 * @param {Object} o The DOM object spec (and children)
532 * @return {Ext.Template} The new template
534 createTemplate : function(o){
535 var html = Ext.core.DomHelper.createHtml(o);
536 return Ext.create('Ext.Template', html);
543 * This is code is also distributed under MIT license for use
544 * with jQuery and prototype JavaScript libraries.
547 * @class Ext.DomQuery
548 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).
550 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>
553 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.
555 <h4>Element Selectors:</h4>
557 <li> <b>*</b> any element</li>
558 <li> <b>E</b> an element with the tag E</li>
559 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
560 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
561 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
562 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
564 <h4>Attribute Selectors:</h4>
565 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
567 <li> <b>E[foo]</b> has an attribute "foo"</li>
568 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
569 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
570 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
571 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
572 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
573 <li> <b>E[foo!=bar]</b> attribute "foo" does not equal "bar"</li>
575 <h4>Pseudo Classes:</h4>
577 <li> <b>E:first-child</b> E is the first child of its parent</li>
578 <li> <b>E:last-child</b> E is the last child of its parent</li>
579 <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>
580 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
581 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
582 <li> <b>E:only-child</b> E is the only child of its parent</li>
583 <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>
584 <li> <b>E:first</b> the first E in the resultset</li>
585 <li> <b>E:last</b> the last E in the resultset</li>
586 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
587 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
588 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
589 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
590 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
591 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
592 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
593 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
594 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
595 <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
597 <h4>CSS Value Selectors:</h4>
599 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
600 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
601 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
602 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
603 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
604 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
610 Ext.core.DomQuery = Ext.DomQuery = function(){
615 trimRe = /^\s+|\s+$/g,
616 tplRe = /\{(\d+)\}/g,
617 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
618 tagTokenRe = /^(#)?([\w-\*]+)/,
619 nthRe = /(\d*)n\+?(\d*)/,
621 // This is for IE MSXML which does not support expandos.
622 // IE runs the same speed using setAttribute, however FF slows way down
623 // and Safari completely fails so they need to continue to use expandos.
624 isIE = window.ActiveXObject ? true : false,
627 // this eval is stop the compressor from
628 // renaming the variable to something shorter
629 eval("var batch = 30803;");
631 // Retrieve the child node from a particular
632 // parent at the specified index.
633 function child(parent, index){
635 n = parent.firstChild;
647 // retrieve the next element node
649 while((n = n.nextSibling) && n.nodeType != 1);
653 // retrieve the previous element node
655 while((n = n.previousSibling) && n.nodeType != 1);
659 // Mark each child node with a nodeIndex skipping and
660 // removing empty text nodes.
661 function children(parent){
662 var n = parent.firstChild,
666 nextNode = n.nextSibling;
667 // clean worthless empty nodes.
668 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
669 parent.removeChild(n);
671 // add an expando nodeIndex
672 n.nodeIndex = ++nodeIndex;
680 // nodeSet - array of nodes
682 function byClassName(nodeSet, cls){
686 var result = [], ri = -1;
687 for(var i = 0, ci; ci = nodeSet[i]; i++){
688 if((' '+ci.className+' ').indexOf(cls) != -1){
695 function attrValue(n, attr){
696 // if its an array, use the first node.
697 if(!n.tagName && typeof n.length != "undefined"){
707 if(attr == "class" || attr == "className"){
710 return n.getAttribute(attr) || n[attr];
716 // mode - false, /, >, +, ~
717 // tagName - defaults to "*"
718 function getNodes(ns, mode, tagName){
719 var result = [], ri = -1, cs;
723 tagName = tagName || "*";
725 if(typeof ns.getElementsByTagName != "undefined"){
729 // no mode specified, grab all elements by tagName
732 for(var i = 0, ni; ni = ns[i]; i++){
733 cs = ni.getElementsByTagName(tagName);
734 for(var j = 0, ci; ci = cs[j]; j++){
738 // Direct Child mode (/ or >)
739 // E > F or E/F all direct children elements of E that have the tag
740 } else if(mode == "/" || mode == ">"){
741 var utag = tagName.toUpperCase();
742 for(var i = 0, ni, cn; ni = ns[i]; i++){
744 for(var j = 0, cj; cj = cn[j]; j++){
745 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
750 // Immediately Preceding mode (+)
751 // E + F all elements with the tag F that are immediately preceded by an element with the tag E
752 }else if(mode == "+"){
753 var utag = tagName.toUpperCase();
754 for(var i = 0, n; n = ns[i]; i++){
755 while((n = n.nextSibling) && n.nodeType != 1);
756 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
761 // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
762 }else if(mode == "~"){
763 var utag = tagName.toUpperCase();
764 for(var i = 0, n; n = ns[i]; i++){
765 while((n = n.nextSibling)){
766 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
775 function concat(a, b){
779 for(var i = 0, l = b.length; i < l; i++){
785 function byTag(cs, tagName){
786 if(cs.tagName || cs == document){
792 var result = [], ri = -1;
793 tagName = tagName.toLowerCase();
794 for(var i = 0, ci; ci = cs[i]; i++){
795 if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
802 function byId(cs, id){
803 if(cs.tagName || cs == document){
809 var result = [], ri = -1;
810 for(var i = 0, ci; ci = cs[i]; i++){
811 if(ci && ci.id == id){
819 // operators are =, !=, ^=, $=, *=, %=, |= and ~=
821 function byAttribute(cs, attr, value, op, custom){
824 useGetStyle = custom == "{",
825 fn = Ext.DomQuery.operators[op],
830 for(var i = 0, ci; ci = cs[i]; i++){
831 // skip non-element nodes.
832 if(ci.nodeType != 1){
835 // only need to do this for the first node
837 xml = Ext.DomQuery.isXml(ci);
841 // we only need to change the property names if we're dealing with html nodes, not XML
844 a = Ext.DomQuery.getStyle(ci, attr);
845 } else if (attr == "class" || attr == "className"){
847 } else if (attr == "for"){
849 } else if (attr == "href"){
850 // getAttribute href bug
851 // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
852 a = ci.getAttribute("href", 2);
854 a = ci.getAttribute(attr);
857 a = ci.getAttribute(attr);
859 if((fn && fn(a, value)) || (!fn && a)){
866 function byPseudo(cs, name, value){
867 return Ext.DomQuery.pseudos[name](cs, value);
870 function nodupIEXml(cs){
873 cs[0].setAttribute("_nodup", d);
875 for(var i = 1, len = cs.length; i < len; i++){
877 if(!c.getAttribute("_nodup") != d){
878 c.setAttribute("_nodup", d);
882 for(var i = 0, len = cs.length; i < len; i++){
883 cs[i].removeAttribute("_nodup");
892 var len = cs.length, c, i, r = cs, cj, ri = -1;
893 if(!len || typeof cs.nodeType != "undefined" || len == 1){
896 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
897 return nodupIEXml(cs);
901 for(i = 1; c = cs[i]; i++){
906 for(var j = 0; j < i; j++){
909 for(j = i+1; cj = cs[j]; j++){
921 function quickDiffIEXml(c1, c2){
924 for(var i = 0, len = c1.length; i < len; i++){
925 c1[i].setAttribute("_qdiff", d);
927 for(var i = 0, len = c2.length; i < len; i++){
928 if(c2[i].getAttribute("_qdiff") != d){
932 for(var i = 0, len = c1.length; i < len; i++){
933 c1[i].removeAttribute("_qdiff");
938 function quickDiff(c1, c2){
939 var len1 = c1.length,
945 if(isIE && typeof c1[0].selectSingleNode != "undefined"){
946 return quickDiffIEXml(c1, c2);
948 for(var i = 0; i < len1; i++){
951 for(var i = 0, len = c2.length; i < len; i++){
952 if(c2[i]._qdiff != d){
959 function quickId(ns, mode, root, id){
961 var d = root.ownerDocument || root;
962 return d.getElementById(id);
964 ns = getNodes(ns, mode, "*");
969 getStyle : function(el, name){
970 return Ext.fly(el).getStyle(name);
973 * Compiles a selector/xpath query into a reusable function. The returned function
974 * takes one parameter "root" (optional), which is the context node from where the query should start.
975 * @param {String} selector The selector/xpath query
976 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
979 compile : function(path, type){
980 type = type || "select";
983 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
986 matchers = Ext.DomQuery.matchers,
987 matchersLn = matchers.length,
989 // accept leading mode switch
990 lmode = path.match(modeRe);
992 if(lmode && lmode[1]){
993 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
994 path = path.replace(lmode[1], "");
997 // strip leading slashes
998 while(path.substr(0, 1)=="/"){
999 path = path.substr(1);
1002 while(path && lastPath != path){
1004 var tokenMatch = path.match(tagTokenRe);
1005 if(type == "select"){
1008 if(tokenMatch[1] == "#"){
1009 fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
1011 fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1013 path = path.replace(tokenMatch[0], "");
1014 }else if(path.substr(0, 1) != '@'){
1015 fn[fn.length] = 'n = getNodes(n, mode, "*");';
1020 if(tokenMatch[1] == "#"){
1021 fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1023 fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1025 path = path.replace(tokenMatch[0], "");
1028 while(!(modeMatch = path.match(modeRe))){
1029 var matched = false;
1030 for(var j = 0; j < matchersLn; j++){
1031 var t = matchers[j];
1032 var m = path.match(t.re);
1034 fn[fn.length] = t.select.replace(tplRe, function(x, i){
1037 path = path.replace(m[0], "");
1042 // prevent infinite loop on bad selector
1046 sourceClass: 'Ext.DomQuery',
1047 sourceMethod: 'compile',
1048 msg: 'Error parsing selector. Parsing failed at "' + path + '"'
1054 fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1055 path = path.replace(modeMatch[1], "");
1059 fn[fn.length] = "return nodup(n);\n}";
1061 // eval fn and return it
1067 * Selects a group of elements.
1068 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1069 * @param {Node/String} root (optional) The start of the query (defaults to document).
1070 * @return {Array} An Array of DOM elements which match the selector. If there are
1071 * no matches, and empty Array is returned.
1073 jsSelect: function(path, root, type){
1074 // set root to doc if not specified.
1075 root = root || document;
1077 if(typeof root == "string"){
1078 root = document.getElementById(root);
1080 var paths = path.split(","),
1083 // loop over each selector
1084 for(var i = 0, len = paths.length; i < len; i++){
1085 var subPath = paths[i].replace(trimRe, "");
1086 // compile and place in cache
1087 if(!cache[subPath]){
1088 cache[subPath] = Ext.DomQuery.compile(subPath);
1089 if(!cache[subPath]){
1092 sourceClass: 'Ext.DomQuery',
1093 sourceMethod: 'jsSelect',
1094 msg: subPath + ' is not a valid selector'
1099 var result = cache[subPath](root);
1100 if(result && result != document){
1101 results = results.concat(result);
1105 // if there were multiple selectors, make sure dups
1107 if(paths.length > 1){
1108 return nodup(results);
1113 isXml: function(el) {
1114 var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1115 return docEl ? docEl.nodeName !== "HTML" : false;
1118 select : document.querySelectorAll ? function(path, root, type) {
1119 root = root || document;
1120 if (!Ext.DomQuery.isXml(root)) {
1122 var cs = root.querySelectorAll(path);
1123 return Ext.Array.toArray(cs);
1127 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1128 } : function(path, root, type) {
1129 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1133 * Selects a single element.
1134 * @param {String} selector The selector/xpath query
1135 * @param {Node} root (optional) The start of the query (defaults to document).
1136 * @return {Element} The DOM element which matched the selector.
1138 selectNode : function(path, root){
1139 return Ext.DomQuery.select(path, root)[0];
1143 * Selects the value of a node, optionally replacing null with the defaultValue.
1144 * @param {String} selector The selector/xpath query
1145 * @param {Node} root (optional) The start of the query (defaults to document).
1146 * @param {String} defaultValue
1149 selectValue : function(path, root, defaultValue){
1150 path = path.replace(trimRe, "");
1151 if(!valueCache[path]){
1152 valueCache[path] = Ext.DomQuery.compile(path, "select");
1154 var n = valueCache[path](root), v;
1155 n = n[0] ? n[0] : n;
1157 // overcome a limitation of maximum textnode size
1158 // Rumored to potentially crash IE6 but has not been confirmed.
1159 // http://reference.sitepoint.com/javascript/Node/normalize
1160 // https://developer.mozilla.org/En/DOM/Node.normalize
1161 if (typeof n.normalize == 'function') n.normalize();
1163 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1164 return ((v === null||v === undefined||v==='') ? defaultValue : v);
1168 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1169 * @param {String} selector The selector/xpath query
1170 * @param {Node} root (optional) The start of the query (defaults to document).
1171 * @param {Number} defaultValue
1174 selectNumber : function(path, root, defaultValue){
1175 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1176 return parseFloat(v);
1180 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1181 * @param {String/HTMLElement/Array} el An element id, element or array of elements
1182 * @param {String} selector The simple selector to test
1185 is : function(el, ss){
1186 if(typeof el == "string"){
1187 el = document.getElementById(el);
1189 var isArray = Ext.isArray(el),
1190 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1191 return isArray ? (result.length == el.length) : (result.length > 0);
1195 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1196 * @param {Array} el An array of elements to filter
1197 * @param {String} selector The simple selector to test
1198 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1199 * the selector instead of the ones that match
1200 * @return {Array} An Array of DOM elements which match the selector. If there are
1201 * no matches, and empty Array is returned.
1203 filter : function(els, ss, nonMatches){
1204 ss = ss.replace(trimRe, "");
1205 if(!simpleCache[ss]){
1206 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1208 var result = simpleCache[ss](els);
1209 return nonMatches ? quickDiff(result, els) : result;
1213 * Collection of matching regular expressions and code snippets.
1214 * Each capture group within () will be replace the {} in the select
1215 * statement as specified by their index.
1219 select: 'n = byClassName(n, " {1} ");'
1221 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1222 select: 'n = byPseudo(n, "{1}", "{2}");'
1224 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1225 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1228 select: 'n = byId(n, "{1}");'
1231 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1236 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1237 * 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, > <.
1240 "=" : function(a, v){
1243 "!=" : function(a, v){
1246 "^=" : function(a, v){
1247 return a && a.substr(0, v.length) == v;
1249 "$=" : function(a, v){
1250 return a && a.substr(a.length-v.length) == v;
1252 "*=" : function(a, v){
1253 return a && a.indexOf(v) !== -1;
1255 "%=" : function(a, v){
1256 return (a % v) == 0;
1258 "|=" : function(a, v){
1259 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1261 "~=" : function(a, v){
1262 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1267 Object hash of "pseudo class" filter functions which are used when filtering selections.
1268 Each function is passed two parameters:
1271 An Array of DOM elements to filter.
1274 The argument (if any) supplied in the selector.
1276 A filter function returns an Array of DOM elements which conform to the pseudo class.
1277 In addition to the provided pseudo classes listed above such as `first-child` and `nth-child`,
1278 developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.
1280 For example, to filter `a` elements to only return links to __external__ resources:
1282 Ext.DomQuery.pseudos.external = function(c, v){
1283 var r = [], ri = -1;
1284 for(var i = 0, ci; ci = c[i]; i++){
1285 // Include in result set only if it's a link to an external resource
1286 if(ci.hostname != location.hostname){
1293 Then external links could be gathered with the following statement:
1295 var externalLinks = Ext.select("a:external");
1300 "first-child" : function(c){
1301 var r = [], ri = -1, n;
1302 for(var i = 0, ci; ci = n = c[i]; i++){
1303 while((n = n.previousSibling) && n.nodeType != 1);
1311 "last-child" : function(c){
1312 var r = [], ri = -1, n;
1313 for(var i = 0, ci; ci = n = c[i]; i++){
1314 while((n = n.nextSibling) && n.nodeType != 1);
1322 "nth-child" : function(c, a) {
1323 var r = [], ri = -1,
1324 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1325 f = (m[1] || 1) - 0, l = m[2] - 0;
1326 for(var i = 0, n; n = c[i]; i++){
1327 var pn = n.parentNode;
1328 if (batch != pn._batch) {
1330 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1331 if(cn.nodeType == 1){
1338 if (l == 0 || n.nodeIndex == l){
1341 } else if ((n.nodeIndex + l) % f == 0){
1349 "only-child" : function(c){
1350 var r = [], ri = -1;;
1351 for(var i = 0, ci; ci = c[i]; i++){
1352 if(!prev(ci) && !next(ci)){
1359 "empty" : function(c){
1360 var r = [], ri = -1;
1361 for(var i = 0, ci; ci = c[i]; i++){
1362 var cns = ci.childNodes, j = 0, cn, empty = true;
1365 if(cn.nodeType == 1 || cn.nodeType == 3){
1377 "contains" : function(c, v){
1378 var r = [], ri = -1;
1379 for(var i = 0, ci; ci = c[i]; i++){
1380 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
1387 "nodeValue" : function(c, v){
1388 var r = [], ri = -1;
1389 for(var i = 0, ci; ci = c[i]; i++){
1390 if(ci.firstChild && ci.firstChild.nodeValue == v){
1397 "checked" : function(c){
1398 var r = [], ri = -1;
1399 for(var i = 0, ci; ci = c[i]; i++){
1400 if(ci.checked == true){
1407 "not" : function(c, ss){
1408 return Ext.DomQuery.filter(c, ss, true);
1411 "any" : function(c, selectors){
1412 var ss = selectors.split('|'),
1414 for(var i = 0, ci; ci = c[i]; i++){
1415 for(var j = 0; s = ss[j]; j++){
1416 if(Ext.DomQuery.is(ci, s)){
1425 "odd" : function(c){
1426 return this["nth-child"](c, "odd");
1429 "even" : function(c){
1430 return this["nth-child"](c, "even");
1433 "nth" : function(c, a){
1434 return c[a-1] || [];
1437 "first" : function(c){
1441 "last" : function(c){
1442 return c[c.length-1] || [];
1445 "has" : function(c, ss){
1446 var s = Ext.DomQuery.select,
1448 for(var i = 0, ci; ci = c[i]; i++){
1449 if(s(ss, ci).length > 0){
1456 "next" : function(c, ss){
1457 var is = Ext.DomQuery.is,
1459 for(var i = 0, ci; ci = c[i]; i++){
1468 "prev" : function(c, ss){
1469 var is = Ext.DomQuery.is,
1471 for(var i = 0, ci; ci = c[i]; i++){
1484 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
1485 * @param {String} path The selector/xpath query
1486 * @param {Node} root (optional) The start of the query (defaults to document).
1491 Ext.query = Ext.DomQuery.select;
1494 * @class Ext.core.Element
1495 * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
1496 * <p>All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all DOM elements.</p>
1497 * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
1498 * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
1499 * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
1503 var el = Ext.get("my-div");
1505 // by DOM element reference
1506 var el = Ext.get(myDivElement);
1508 * <b>Animations</b><br />
1509 * <p>When an element is manipulated, by default there is no animation.</p>
1511 var el = Ext.get("my-div");
1516 * <p>Many of the functions for manipulating an element have an optional "animate" parameter. This
1517 * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
1519 // default animation
1520 el.setWidth(100, true);
1523 * <p>To configure the effects, an object literal with animation options to use as the Element animation
1524 * configuration object can also be specified. Note that the supported Element animation configuration
1525 * options are a subset of the {@link Ext.fx.Anim} animation options specific to Fx effects. The supported
1526 * Element animation configuration options are:</p>
1528 Option Default Description
1529 --------- -------- ---------------------------------------------
1530 {@link Ext.fx.Anim#duration duration} .35 The duration of the animation in seconds
1531 {@link Ext.fx.Anim#easing easing} easeOut The easing method
1532 {@link Ext.fx.Anim#callback callback} none A function to execute when the anim completes
1533 {@link Ext.fx.Anim#scope scope} this The scope (this) of the callback function
1537 // Element animation options object
1539 {@link Ext.fx.Anim#duration duration}: 1,
1540 {@link Ext.fx.Anim#easing easing}: 'elasticIn',
1541 {@link Ext.fx.Anim#callback callback}: this.foo,
1542 {@link Ext.fx.Anim#scope scope}: this
1544 // animation with some options set
1545 el.setWidth(100, opt);
1547 * <p>The Element animation object being used for the animation will be set on the options
1548 * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
1550 // using the "anim" property to get the Anim object
1551 if(opt.anim.isAnimated()){
1555 * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
1556 * <p><b> Composite (Collections of) Elements</b></p>
1557 * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
1558 * @constructor Create a new Element directly.
1559 * @param {String/HTMLElement} element
1560 * @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).
1566 Ext.Element = Ext.core.Element = function(element, forceNew) {
1567 var dom = typeof element == "string" ? DOC.getElementById(element) : element,
1576 if (!forceNew && id && EC[id]) {
1577 // element object already exists
1588 * The DOM element ID
1591 this.id = id || Ext.id(dom);
1594 var DH = Ext.core.DomHelper,
1595 El = Ext.core.Element;
1600 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
1601 * @param {Object} o The object with the attributes
1602 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
1603 * @return {Ext.core.Element} this
1605 set: function(o, useSet) {
1609 useSet = (useSet !== false) && !!el.setAttribute;
1612 if (o.hasOwnProperty(attr)) {
1614 if (attr == 'style') {
1615 DH.applyStyles(el, val);
1616 } else if (attr == 'cls') {
1618 } else if (useSet) {
1619 el.setAttribute(attr, val);
1631 * Fires when a mouse click is detected within the element.
1632 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1633 * @param {HtmlElement} t The target of the event.
1634 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1637 * @event contextmenu
1638 * Fires when a right click is detected within the element.
1639 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1640 * @param {HtmlElement} t The target of the event.
1641 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1645 * Fires when a mouse double click is detected within the element.
1646 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1647 * @param {HtmlElement} t The target of the event.
1648 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1652 * Fires when a mousedown is detected within the element.
1653 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1654 * @param {HtmlElement} t The target of the event.
1655 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1659 * Fires when a mouseup is detected within the element.
1660 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1661 * @param {HtmlElement} t The target of the event.
1662 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1666 * Fires when a mouseover is detected within the element.
1667 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1668 * @param {HtmlElement} t The target of the event.
1669 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1673 * Fires when a mousemove is detected with the element.
1674 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1675 * @param {HtmlElement} t The target of the event.
1676 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1680 * Fires when a mouseout is detected with the element.
1681 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1682 * @param {HtmlElement} t The target of the event.
1683 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1687 * Fires when the mouse enters the element.
1688 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1689 * @param {HtmlElement} t The target of the event.
1690 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1694 * Fires when the mouse leaves the element.
1695 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1696 * @param {HtmlElement} t The target of the event.
1697 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1703 * Fires when a keypress is detected within the element.
1704 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1705 * @param {HtmlElement} t The target of the event.
1706 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1710 * Fires when a keydown is detected within the element.
1711 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1712 * @param {HtmlElement} t The target of the event.
1713 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1717 * Fires when a keyup is detected within the element.
1718 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1719 * @param {HtmlElement} t The target of the event.
1720 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1724 // HTML frame/object events
1727 * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
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.
1734 * 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.
1735 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1736 * @param {HtmlElement} t The target of the event.
1737 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1741 * Fires when an object/image is stopped from loading before completely loaded.
1742 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1743 * @param {HtmlElement} t The target of the event.
1744 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1748 * Fires when an object/image/frame cannot be loaded properly.
1749 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1750 * @param {HtmlElement} t The target of the event.
1751 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1755 * Fires when a document view is resized.
1756 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1757 * @param {HtmlElement} t The target of the event.
1758 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1762 * Fires when a document view is scrolled.
1763 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1764 * @param {HtmlElement} t The target of the event.
1765 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1771 * Fires when a user selects some text in a text field, including input and textarea.
1772 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1773 * @param {HtmlElement} t The target of the event.
1774 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1778 * Fires when a control loses the input focus and its value has been modified since gaining focus.
1779 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1780 * @param {HtmlElement} t The target of the event.
1781 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1785 * Fires when a form is submitted.
1786 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1787 * @param {HtmlElement} t The target of the event.
1788 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1792 * Fires when a form is reset.
1793 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1794 * @param {HtmlElement} t The target of the event.
1795 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1799 * Fires when an element receives focus either via the pointing device or by tab navigation.
1800 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1801 * @param {HtmlElement} t The target of the event.
1802 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1806 * Fires when an element loses focus either via the pointing device or by tabbing navigation.
1807 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1808 * @param {HtmlElement} t The target of the event.
1809 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1812 // User Interface events
1815 * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
1816 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1817 * @param {HtmlElement} t The target of the event.
1818 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1821 * @event DOMFocusOut
1822 * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
1823 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1824 * @param {HtmlElement} t The target of the event.
1825 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1828 * @event DOMActivate
1829 * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
1830 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1831 * @param {HtmlElement} t The target of the event.
1832 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1835 // DOM Mutation events
1837 * @event DOMSubtreeModified
1838 * Where supported. Fires when the subtree is modified.
1839 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1840 * @param {HtmlElement} t The target of the event.
1841 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1844 * @event DOMNodeInserted
1845 * Where supported. Fires when a node has been added as a child of another node.
1846 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1847 * @param {HtmlElement} t The target of the event.
1848 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1851 * @event DOMNodeRemoved
1852 * Where supported. Fires when a descendant node of the element is removed.
1853 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1854 * @param {HtmlElement} t The target of the event.
1855 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1858 * @event DOMNodeRemovedFromDocument
1859 * Where supported. Fires when a node is being removed from a document.
1860 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1861 * @param {HtmlElement} t The target of the event.
1862 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1865 * @event DOMNodeInsertedIntoDocument
1866 * Where supported. Fires when a node is being inserted into a document.
1867 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1868 * @param {HtmlElement} t The target of the event.
1869 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1872 * @event DOMAttrModified
1873 * Where supported. Fires when an attribute has been modified.
1874 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1875 * @param {HtmlElement} t The target of the event.
1876 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1879 * @event DOMCharacterDataModified
1880 * Where supported. Fires when the character data has been modified.
1881 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1882 * @param {HtmlElement} t The target of the event.
1883 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1887 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
1893 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
1894 * @param {String} selector The simple selector to test
1895 * @return {Boolean} True if this element matches the selector, else false
1897 is: function(simpleSelector) {
1898 return Ext.DomQuery.is(this.dom, simpleSelector);
1902 * Tries to focus the element. Any exceptions are caught and ignored.
1903 * @param {Number} defer (optional) Milliseconds to defer the focus
1904 * @return {Ext.core.Element} this
1906 focus: function(defer,
1910 dom = dom || me.dom;
1912 if (Number(defer)) {
1913 Ext.defer(me.focus, defer, null, [null, dom]);
1922 * Tries to blur the element. Any exceptions are caught and ignored.
1923 * @return {Ext.core.Element} this
1933 * Returns the value of the "value" attribute
1934 * @param {Boolean} asNumber true to parse the value as a number
1935 * @return {String/Number}
1937 getValue: function(asNumber) {
1938 var val = this.dom.value;
1939 return asNumber ? parseInt(val, 10) : val;
1943 * Appends an event handler to this element. The shorthand version {@link #on} is equivalent.
1944 * @param {String} eventName The name of event to handle.
1945 * @param {Function} fn The handler function the event invokes. This function is passed
1946 * the following parameters:<ul>
1947 * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
1948 * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
1949 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
1950 * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
1952 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1953 * <b>If omitted, defaults to this Element.</b>.
1954 * @param {Object} options (optional) An object containing handler configuration properties.
1955 * This may contain any of the following properties:<ul>
1956 * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1957 * <b>If omitted, defaults to this Element.</b></div></li>
1958 * <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>
1959 * <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>
1960 * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
1961 * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
1962 * <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>
1963 * <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>
1964 * <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>
1965 * <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>
1966 * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
1967 * by the specified number of milliseconds. If the event fires again within that time, the original
1968 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
1971 * <b>Combining Options</b><br>
1972 * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
1973 * addListener. The two are equivalent. Using the options argument, it is possible to combine different
1974 * types of listeners:<br>
1976 * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
1977 * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
1979 el.on('click', this.onClick, this, {
1984 });</code></pre></p>
1986 * <b>Attaching multiple handlers in 1 call</b><br>
1987 * The method also allows for a single argument to be passed which is a config object containing properties
1988 * which specify multiple handlers.</p>
1998 fn: this.onMouseOver,
2002 fn: this.onMouseOut,
2007 * Or a shorthand syntax:<br>
2008 * Code:<pre><code></p>
2010 'click' : this.onClick,
2011 'mouseover' : this.onMouseOver,
2012 'mouseout' : this.onMouseOut,
2016 * <p><b>delegate</b></p>
2017 * <p>This is a configuration option that you can pass along when registering a handler for
2018 * an event to assist with event delegation. Event delegation is a technique that is used to
2019 * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
2020 * for a container element as opposed to each element within a container. By setting this
2021 * configuration option to a simple selector, the target element will be filtered to look for
2022 * a descendant of the target.
2023 * For example:<pre><code>
2024 // using this markup:
2026 <p id='p1'>paragraph one</p>
2027 <p id='p2' class='clickable'>paragraph two</p>
2028 <p id='p3'>paragraph three</p>
2030 // utilize event delegation to registering just one handler on the container element:
2031 el = Ext.get('elId');
2036 console.info(t.id); // 'p2'
2040 // filter the target element to be a descendant with the class 'clickable'
2041 delegate: '.clickable'
2045 * @return {Ext.core.Element} this
2047 addListener: function(eventName, fn, scope, options) {
2048 Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
2053 * Removes an event handler from this element. The shorthand version {@link #un} is equivalent.
2054 * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
2055 * listener, the same scope must be specified here.
2058 el.removeListener('click', this.handlerFn);
2060 el.un('click', this.handlerFn);
2062 * @param {String} eventName The name of the event from which to remove the handler.
2063 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2064 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2065 * then this must refer to the same object.
2066 * @return {Ext.core.Element} this
2068 removeListener: function(eventName, fn, scope) {
2069 Ext.EventManager.un(this.dom, eventName, fn, scope || this);
2074 * Removes all previous added listeners from this element
2075 * @return {Ext.core.Element} this
2077 removeAllListeners: function() {
2078 Ext.EventManager.removeAll(this.dom);
2083 * Recursively removes all previous added listeners from this element and its children
2084 * @return {Ext.core.Element} this
2086 purgeAllListeners: function() {
2087 Ext.EventManager.purgeElement(this);
2092 * @private Test if size has a unit, otherwise appends the passed unit string, or the default for this Element.
2093 * @param size {Mixed} The size to set
2094 * @param units {String} The units to append to a numeric size value
2096 addUnits: function(size, units) {
2098 // Most common case first: Size is set to a number
2099 if (Ext.isNumber(size)) {
2100 return size + (units || this.defaultUnit || 'px');
2103 // Size set to a value which means "auto"
2104 if (size === "" || size == "auto" || size === undefined || size === null) {
2108 // Otherwise, warn if it's not a valid CSS measurement
2109 if (!unitPattern.test(size)) {
2111 if (Ext.isDefined(Ext.global.console)) {
2112 Ext.global.console.warn("Warning, size detected as NaN on Element.addUnits.");
2121 * Tests various css rules/browsers to determine if this element uses a border box
2124 isBorderBox: function() {
2125 return Ext.isBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
2129 * <p>Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}</p>
2131 remove: function() {
2137 Ext.removeNode(dom);
2142 * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
2143 * @param {Function} overFn The function to call when the mouse enters the Element.
2144 * @param {Function} outFn The function to call when the mouse leaves the Element.
2145 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
2146 * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
2147 * @return {Ext.core.Element} this
2149 hover: function(overFn, outFn, scope, options) {
2151 me.on('mouseenter', overFn, scope || me.dom, options);
2152 me.on('mouseleave', outFn, scope || me.dom, options);
2157 * Returns true if this element is an ancestor of the passed element
2158 * @param {HTMLElement/String} el The element to check
2159 * @return {Boolean} True if this element is an ancestor of el, else false
2161 contains: function(el) {
2162 return ! el ? false: Ext.core.Element.isAncestor(this.dom, el.dom ? el.dom: el);
2166 * Returns the value of a namespaced attribute from the element's underlying DOM node.
2167 * @param {String} namespace The namespace in which to look for the attribute
2168 * @param {String} name The attribute name
2169 * @return {String} The attribute value
2172 getAttributeNS: function(ns, name) {
2173 return this.getAttribute(name, ns);
2177 * Returns the value of an attribute from the element's underlying DOM node.
2178 * @param {String} name The attribute name
2179 * @param {String} namespace (optional) The namespace in which to look for the attribute
2180 * @return {String} The attribute value
2182 getAttribute: (Ext.isIE && !(Ext.isIE9 && document.documentMode === 9)) ?
2183 function(name, ns) {
2187 type = typeof d[ns + ":" + name];
2188 if (type != 'undefined' && type != 'unknown') {
2189 return d[ns + ":" + name] || null;
2193 if (name === "for") {
2196 return d[name] || null;
2197 }: function(name, ns) {
2200 return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
2202 return d.getAttribute(name) || d[name] || null;
2206 * Update the innerHTML of this element
2207 * @param {String} html The new HTML
2208 * @return {Ext.core.Element} this
2210 update: function(html) {
2212 this.dom.innerHTML = html;
2218 var ep = El.prototype;
2220 El.addMethods = function(o) {
2225 * Appends an event handler (shorthand for {@link #addListener}).
2226 * @param {String} eventName The name of event to handle.
2227 * @param {Function} fn The handler function the event invokes.
2228 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
2229 * @param {Object} options (optional) An object containing standard {@link #addListener} options
2230 * @member Ext.core.Element
2233 ep.on = ep.addListener;
2236 * Removes an event handler from this element (see {@link #removeListener} for additional notes).
2237 * @param {String} eventName The name of the event from which to remove the handler.
2238 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2239 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2240 * then this must refer to the same object.
2241 * @return {Ext.core.Element} this
2242 * @member Ext.core.Element
2245 ep.un = ep.removeListener;
2248 * Removes all previous added listeners from this element
2249 * @return {Ext.core.Element} this
2250 * @member Ext.core.Element
2251 * @method clearListeners
2253 ep.clearListeners = ep.removeAllListeners;
2256 * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}.
2257 * Alias to {@link #remove}.
2258 * @member Ext.core.Element
2261 ep.destroy = ep.remove;
2264 * true to automatically adjust width and height settings for box-model issues (default to true)
2266 ep.autoBoxAdjust = true;
2269 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
2273 * Retrieves Ext.core.Element objects.
2274 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
2275 * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
2276 * its ID, use {@link Ext.ComponentManager#get}.</p>
2277 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
2278 * object was recreated with the same id via AJAX or DOM.</p>
2279 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
2280 * @return {Element} The Element object (or null if no matching element was found)
2282 * @member Ext.core.Element
2285 El.get = function(el) {
2292 if (typeof el == "string") {
2294 if (! (elm = DOC.getElementById(el))) {
2297 if (EC[el] && EC[el].el) {
2301 ex = El.addToCache(new El(elm));
2304 } else if (el.tagName) {
2306 if (! (id = el.id)) {
2309 if (EC[id] && EC[id].el) {
2313 ex = El.addToCache(new El(el));
2316 } else if (el instanceof El) {
2318 // refresh dom element in case no longer valid,
2319 // catch case where it hasn't been appended
2320 // If an el instance is passed, don't pass to getElementById without some kind of id
2321 if (Ext.isIE && (el.id == undefined || el.id == '')) {
2324 el.dom = DOC.getElementById(el.id) || el.dom;
2328 } else if (el.isComposite) {
2330 } else if (Ext.isArray(el)) {
2331 return El.select(el);
2332 } else if (el == DOC) {
2333 // create a bogus element object representing the document object
2335 var f = function() {};
2336 f.prototype = El.prototype;
2345 El.addToCache = function(el, id) {
2357 // private method for getting and setting element data
2358 El.data = function(el, key, value) {
2363 var c = EC[el.id].data;
2364 if (arguments.length == 2) {
2367 return (c[key] = value);
2372 // Garbage collection - uncache elements/purge listeners on orphaned elements
2373 // so we don't hold a reference and cause the browser to retain them
2374 function garbageCollect() {
2375 if (!Ext.enableGarbageCollector) {
2376 clearInterval(El.collectorThreadId);
2384 if (!EC.hasOwnProperty(eid)) {
2388 if (o.skipGarbageCollection) {
2393 // -------------------------------------------------------
2394 // Determining what is garbage:
2395 // -------------------------------------------------------
2397 // dom node is null, definitely garbage
2398 // -------------------------------------------------------
2400 // no parentNode == direct orphan, definitely garbage
2401 // -------------------------------------------------------
2402 // !d.offsetParent && !document.getElementById(eid)
2403 // display none elements have no offsetParent so we will
2404 // also try to look it up by it's id. However, check
2405 // offsetParent first so we don't do unneeded lookups.
2406 // This enables collection of elements that are not orphans
2407 // directly, but somewhere up the line they have an orphan
2409 // -------------------------------------------------------
2410 if (!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))) {
2411 if (d && Ext.enableListenerCollection) {
2412 Ext.EventManager.removeAll(d);
2417 // Cleanup IE Object leaks
2421 if (!EC.hasOwnProperty(eid)) {
2430 El.collectorThreadId = setInterval(garbageCollect, 30000);
2432 var flyFn = function() {};
2433 flyFn.prototype = El.prototype;
2436 El.Flyweight = function(dom) {
2440 El.Flyweight.prototype = new flyFn();
2441 El.Flyweight.prototype.isFlyweight = true;
2442 El._flyweights = {};
2445 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
2446 * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
2447 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
2448 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
2449 * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
2450 * @param {String/HTMLElement} el The dom node or id
2451 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
2452 * (e.g. internally Ext uses "_global")
2453 * @return {Element} The shared Element object (or null if no matching element was found)
2454 * @member Ext.core.Element
2457 El.fly = function(el, named) {
2459 named = named || '_global';
2460 el = Ext.getDom(el);
2462 (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
2463 ret = El._flyweights[named];
2469 * Retrieves Ext.core.Element objects.
2470 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
2471 * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
2472 * its ID, use {@link Ext.ComponentManager#get}.</p>
2473 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
2474 * object was recreated with the same id via AJAX or DOM.</p>
2475 * Shorthand of {@link Ext.core.Element#get}
2476 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
2477 * @return {Element} The Element object (or null if no matching element was found)
2484 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
2485 * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
2486 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
2487 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
2488 * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
2489 * @param {String/HTMLElement} el The dom node or id
2490 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
2491 * (e.g. internally Ext uses "_global")
2492 * @return {Element} The shared Element object (or null if no matching element was found)
2498 // speedy lookup for elements never to box adjust
2499 var noBoxAdjust = Ext.isStrict ? {
2506 if (Ext.isIE || Ext.isGecko) {
2507 noBoxAdjust['button'] = 1;
2512 * @class Ext.core.Element
2514 Ext.core.Element.addMethods({
2516 * 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)
2517 * @param {String} selector The simple selector to test
2518 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
2519 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
2520 * @return {HTMLElement} The matching DOM node (or null if no match was found)
2522 findParent : function(simpleSelector, maxDepth, returnEl) {
2528 maxDepth = maxDepth || 50;
2529 if (isNaN(maxDepth)) {
2530 stopEl = Ext.getDom(maxDepth);
2531 maxDepth = Number.MAX_VALUE;
2533 while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
2534 if (Ext.DomQuery.is(p, simpleSelector)) {
2535 return returnEl ? Ext.get(p) : p;
2544 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
2545 * @param {String} selector The simple selector to test
2546 * @param {Number/Mixed} maxDepth (optional) The max depth to
2547 search as a number or element (defaults to 10 || document.body)
2548 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
2549 * @return {HTMLElement} The matching DOM node (or null if no match was found)
2551 findParentNode : function(simpleSelector, maxDepth, returnEl) {
2552 var p = Ext.fly(this.dom.parentNode, '_internal');
2553 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
2557 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
2558 * This is a shortcut for findParentNode() that always returns an Ext.core.Element.
2559 * @param {String} selector The simple selector to test
2560 * @param {Number/Mixed} maxDepth (optional) The max depth to
2561 search as a number or element (defaults to 10 || document.body)
2562 * @return {Ext.core.Element} The matching DOM node (or null if no match was found)
2564 up : function(simpleSelector, maxDepth) {
2565 return this.findParentNode(simpleSelector, maxDepth, true);
2569 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
2570 * @param {String} selector The CSS selector
2571 * @return {CompositeElement/CompositeElement} The composite element
2573 select : function(selector) {
2574 return Ext.core.Element.select(selector, false, this.dom);
2578 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
2579 * @param {String} selector The CSS selector
2580 * @return {Array} An array of the matched nodes
2582 query : function(selector) {
2583 return Ext.DomQuery.select(selector, this.dom);
2587 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
2588 * @param {String} selector The CSS selector
2589 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
2590 * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
2592 down : function(selector, returnDom) {
2593 var n = Ext.DomQuery.selectNode(selector, this.dom);
2594 return returnDom ? n : Ext.get(n);
2598 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
2599 * @param {String} selector The CSS selector
2600 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
2601 * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
2603 child : function(selector, returnDom) {
2607 id = Ext.get(me).id;
2609 id = id.replace(/[\.:]/g, "\\$0");
2610 node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
2611 return returnDom ? node : Ext.get(node);
2615 * Gets the parent node for this element, optionally chaining up trying to match a selector
2616 * @param {String} selector (optional) Find a parent node that matches the passed simple selector
2617 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2618 * @return {Ext.core.Element/HTMLElement} The parent node or null
2620 parent : function(selector, returnDom) {
2621 return this.matchNode('parentNode', 'parentNode', selector, returnDom);
2625 * Gets the next sibling, skipping text nodes
2626 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
2627 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2628 * @return {Ext.core.Element/HTMLElement} The next sibling or null
2630 next : function(selector, returnDom) {
2631 return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
2635 * Gets the previous sibling, skipping text nodes
2636 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
2637 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2638 * @return {Ext.core.Element/HTMLElement} The previous sibling or null
2640 prev : function(selector, returnDom) {
2641 return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
2646 * Gets the first child, skipping text nodes
2647 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
2648 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2649 * @return {Ext.core.Element/HTMLElement} The first child or null
2651 first : function(selector, returnDom) {
2652 return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
2656 * Gets the last child, skipping text nodes
2657 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
2658 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2659 * @return {Ext.core.Element/HTMLElement} The last child or null
2661 last : function(selector, returnDom) {
2662 return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
2665 matchNode : function(dir, start, selector, returnDom) {
2670 var n = this.dom[start];
2672 if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
2673 return !returnDom ? Ext.get(n) : n;
2682 * @class Ext.core.Element
2684 Ext.core.Element.addMethods({
2686 * Appends the passed element(s) to this element
2687 * @param {String/HTMLElement/Array/Element/CompositeElement} el
2688 * @return {Ext.core.Element} this
2690 appendChild : function(el) {
2691 return Ext.get(el).appendTo(this);
2695 * Appends this element to the passed element
2696 * @param {Mixed} el The new parent element
2697 * @return {Ext.core.Element} this
2699 appendTo : function(el) {
2700 Ext.getDom(el).appendChild(this.dom);
2705 * Inserts this element before the passed element in the DOM
2706 * @param {Mixed} el The element before which this element will be inserted
2707 * @return {Ext.core.Element} this
2709 insertBefore : function(el) {
2710 el = Ext.getDom(el);
2711 el.parentNode.insertBefore(this.dom, el);
2716 * Inserts this element after the passed element in the DOM
2717 * @param {Mixed} el The element to insert after
2718 * @return {Ext.core.Element} this
2720 insertAfter : function(el) {
2721 el = Ext.getDom(el);
2722 el.parentNode.insertBefore(this.dom, el.nextSibling);
2727 * Inserts (or creates) an element (or DomHelper config) as the first child of this element
2728 * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
2729 * @return {Ext.core.Element} The new child
2731 insertFirst : function(el, returnDom) {
2733 if (el.nodeType || el.dom || typeof el == 'string') { // element
2734 el = Ext.getDom(el);
2735 this.dom.insertBefore(el, this.dom.firstChild);
2736 return !returnDom ? Ext.get(el) : el;
2739 return this.createChild(el, this.dom.firstChild, returnDom);
2744 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
2745 * @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.
2746 * @param {String} where (optional) 'before' or 'after' defaults to before
2747 * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.core.Element
2748 * @return {Ext.core.Element} The inserted Element. If an array is passed, the last inserted element is returned.
2750 insertSibling: function(el, where, returnDom){
2752 isAfter = (where || 'before').toLowerCase() == 'after',
2755 if(Ext.isArray(el)){
2757 Ext.each(el, function(e) {
2758 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
2768 if(el.nodeType || el.dom){
2769 rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
2774 if (isAfter && !me.dom.nextSibling) {
2775 rt = Ext.core.DomHelper.append(me.dom.parentNode, el, !returnDom);
2777 rt = Ext.core.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
2784 * Replaces the passed element with this element
2785 * @param {Mixed} el The element to replace
2786 * @return {Ext.core.Element} this
2788 replace : function(el) {
2790 this.insertBefore(el);
2796 * Replaces this element with the passed element
2797 * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
2798 * @return {Ext.core.Element} this
2800 replaceWith: function(el){
2803 if(el.nodeType || el.dom || typeof el == 'string'){
2805 me.dom.parentNode.insertBefore(el, me.dom);
2807 el = Ext.core.DomHelper.insertBefore(me.dom, el);
2810 delete Ext.cache[me.id];
2811 Ext.removeNode(me.dom);
2812 me.id = Ext.id(me.dom = el);
2813 Ext.core.Element.addToCache(me.isFlyweight ? new Ext.core.Element(me.dom) : me);
2818 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
2819 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
2820 * automatically generated with the specified attributes.
2821 * @param {HTMLElement} insertBefore (optional) a child element of this element
2822 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
2823 * @return {Ext.core.Element} The new child element
2825 createChild : function(config, insertBefore, returnDom) {
2826 config = config || {tag:'div'};
2828 return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
2831 return Ext.core.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config, returnDom !== true);
2836 * Creates and wraps this element with another element
2837 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
2838 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.core.Element
2839 * @return {HTMLElement/Element} The newly created wrapper element
2841 wrap : function(config, returnDom) {
2842 var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom),
2843 d = newEl.dom || newEl;
2845 d.appendChild(this.dom);
2850 * Inserts an html fragment into this element
2851 * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
2852 * @param {String} html The HTML fragment
2853 * @param {Boolean} returnEl (optional) True to return an Ext.core.Element (defaults to false)
2854 * @return {HTMLElement/Ext.core.Element} The inserted node (or nearest related if more than 1 inserted)
2856 insertHtml : function(where, html, returnEl) {
2857 var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
2858 return returnEl ? Ext.get(el) : el;
2863 * @class Ext.core.Element
2866 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>';
2867 // local style camelizing for speed
2868 var supports = Ext.supports,
2869 view = document.defaultView,
2870 opacityRe = /alpha\(opacity=(.*)\)/i,
2871 trimRe = /^\s+|\s+$/g,
2874 adjustDirect2DTableRe = /table-row|table-.*-group/,
2875 INTERNAL = '_internal',
2876 PADDING = 'padding',
2886 ISCLIPPED = 'isClipped',
2887 OVERFLOW = 'overflow',
2888 OVERFLOWX = 'overflow-x',
2889 OVERFLOWY = 'overflow-y',
2890 ORIGINALCLIP = 'originalClip',
2891 // special markup used throughout Ext when box wrapping elements
2892 borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
2893 paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
2894 margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
2895 data = Ext.core.Element.data;
2897 Ext.override(Ext.core.Element, {
2900 * TODO: Look at this
2902 // private ==> used by Fx
2903 adjustWidth : function(width) {
2905 isNum = (typeof width == 'number');
2907 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
2908 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
2910 return (isNum && width < 0) ? 0 : width;
2913 // private ==> used by Fx
2914 adjustHeight : function(height) {
2916 isNum = (typeof height == "number");
2918 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
2919 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
2921 return (isNum && height < 0) ? 0 : height;
2926 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
2927 * @param {String/Array} className The CSS classes to add separated by space, or an array of classes
2928 * @return {Ext.core.Element} this
2930 addCls : function(className){
2933 space = ((me.dom.className.replace(trimRe, '') == '') ? "" : " "),
2935 if (!Ext.isDefined(className)) {
2938 // Separate case is for speed
2939 if (!Ext.isArray(className)) {
2940 if (typeof className === 'string') {
2941 className = className.replace(trimRe, '').split(spacesRe);
2942 if (className.length === 1) {
2943 className = className[0];
2944 if (!me.hasCls(className)) {
2945 me.dom.className += space + className;
2948 this.addCls(className);
2952 for (i = 0, len = className.length; i < len; i++) {
2954 if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
2959 me.dom.className += space + cls.join(" ");
2966 * Removes one or more CSS classes from the element.
2967 * @param {String/Array} className The CSS classes to remove separated by space, or an array of classes
2968 * @return {Ext.core.Element} this
2970 removeCls : function(className){
2972 i, idx, len, cls, elClasses;
2973 if (!Ext.isDefined(className)) {
2976 if (!Ext.isArray(className)){
2977 className = className.replace(trimRe, '').split(spacesRe);
2979 if (me.dom && me.dom.className) {
2980 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
2981 for (i = 0, len = className.length; i < len; i++) {
2983 if (typeof cls == 'string') {
2984 cls = cls.replace(trimRe, '');
2985 idx = Ext.Array.indexOf(elClasses, cls);
2987 elClasses.splice(idx, 1);
2991 me.dom.className = elClasses.join(" ");
2997 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
2998 * @param {String/Array} className The CSS class to add, or an array of classes
2999 * @return {Ext.core.Element} this
3001 radioCls : function(className){
3002 var cn = this.dom.parentNode.childNodes,
3004 className = Ext.isArray(className) ? className : [className];
3005 for (i = 0, len = cn.length; i < len; i++) {
3007 if (v && v.nodeType == 1) {
3008 Ext.fly(v, '_internal').removeCls(className);
3011 return this.addCls(className);
3015 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
3016 * @param {String} className The CSS class to toggle
3017 * @return {Ext.core.Element} this
3019 toggleCls : Ext.supports.ClassList ?
3020 function(className) {
3021 this.dom.classList.toggle(Ext.String.trim(className));
3024 function(className) {
3025 return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
3029 * Checks if the specified CSS class exists on this element's DOM node.
3030 * @param {String} className The CSS class to check for
3031 * @return {Boolean} True if the class exists, else false
3033 hasCls : Ext.supports.ClassList ?
3034 function(className) {
3038 className = className.split(spacesRe);
3039 var ln = className.length,
3041 for (; i < ln; i++) {
3042 if (className[i] && this.dom.classList.contains(className[i])) {
3048 function(className){
3049 return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
3053 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
3054 * @param {String} oldClassName The CSS class to replace
3055 * @param {String} newClassName The replacement CSS class
3056 * @return {Ext.core.Element} this
3058 replaceCls : function(oldClassName, newClassName){
3059 return this.removeCls(oldClassName).addCls(newClassName);
3062 isStyle : function(style, val) {
3063 return this.getStyle(style) == val;
3067 * Normalizes currentStyle and computedStyle.
3068 * @param {String} property The style property whose value is returned.
3069 * @return {String} The current value of the style property for this element.
3071 getStyle : function(){
3072 return view && view.getComputedStyle ?
3075 v, cs, out, display;
3080 prop = Ext.core.Element.normalize(prop);
3081 out = (v = el.style[prop]) ? v :
3082 (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
3084 // Ignore cases when the margin is correctly reported as 0, the bug only shows
3086 if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){
3087 display = this.getStyle('display');
3088 el.style.display = 'inline-block';
3089 out = view.getComputedStyle(el, '').marginRight;
3090 el.style.display = display;
3093 if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){
3094 out = 'transparent';
3102 if (el == document) {
3106 if (prop == 'opacity') {
3107 if (el.style.filter.match) {
3108 m = el.style.filter.match(opacityRe);
3110 var fv = parseFloat(m[1]);
3112 return fv ? fv / 100 : 0;
3118 prop = Ext.core.Element.normalize(prop);
3119 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
3124 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
3125 * are convert to standard 6 digit hex color.
3126 * @param {String} attr The css attribute
3127 * @param {String} defaultValue The default value to use when a valid color isn't found
3128 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
3131 getColor : function(attr, defaultValue, prefix){
3132 var v = this.getStyle(attr),
3133 color = prefix || prefix === '' ? prefix : '#',
3136 if(!v || (/transparent|inherit/.test(v))) {
3137 return defaultValue;
3140 Ext.each(v.slice(4, v.length -1).split(','), function(s){
3141 h = parseInt(s, 10);
3142 color += (h < 16 ? '0' : '') + h.toString(16);
3145 v = v.replace('#', '');
3146 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
3148 return(color.length > 5 ? color.toLowerCase() : defaultValue);
3152 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
3153 * @param {String/Object} property The style property to be set, or an object of multiple styles.
3154 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
3155 * @return {Ext.core.Element} this
3157 setStyle : function(prop, value){
3165 if (!Ext.isObject(prop)) {
3170 for (style in prop) {
3171 if (prop.hasOwnProperty(style)) {
3172 value = Ext.value(prop[style], '');
3173 if (style == 'opacity') {
3174 me.setOpacity(value);
3177 me.dom.style[Ext.core.Element.normalize(style)] = value;
3185 * Set the opacity of the element
3186 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
3187 * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
3188 * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
3189 * @return {Ext.core.Element} this
3191 setOpacity: function(opacity, animate) {
3201 style = me.dom.style;
3203 if (!animate || !me.anim) {
3204 if (!Ext.supports.Opacity) {
3205 opacity = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')': '';
3206 val = style.filter.replace(opacityRe, '').replace(trimRe, '');
3209 style.filter = val + (val.length > 0 ? ' ': '') + opacity;
3212 style.opacity = opacity;
3216 if (!Ext.isObject(animate)) {
3222 me.animate(Ext.applyIf({
3234 * Clears any opacity settings from this element. Required in some cases for IE.
3235 * @return {Ext.core.Element} this
3237 clearOpacity : function(){
3238 var style = this.dom.style;
3239 if(!Ext.supports.Opacity){
3240 if(!Ext.isEmpty(style.filter)){
3241 style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
3244 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
3251 * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel.
3252 * @return {Number} 0 or 1
3254 adjustDirect2DDimension: function(dimension) {
3257 display = me.getStyle('display'),
3258 inlineDisplay = dom.style['display'],
3259 inlinePosition = dom.style['position'],
3260 originIndex = dimension === 'width' ? 0 : 1,
3263 if (display === 'inline') {
3264 dom.style['display'] = 'inline-block';
3267 dom.style['position'] = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';
3269 // floating will contain digits that appears after the decimal point
3270 // if height or width are set to auto we fallback to msTransformOrigin calculation
3271 floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
3273 dom.style['position'] = inlinePosition;
3275 if (display === 'inline') {
3276 dom.style['display'] = inlineDisplay;
3283 * Returns the offset height of the element
3284 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
3285 * @return {Number} The element's height
3287 getHeight: function(contentHeight, preciseHeight) {
3290 hidden = Ext.isIE && me.isStyle('display', 'none'),
3291 height, overflow, style, floating;
3293 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3294 // We will put the overflow back to it's original value when we are done measuring.
3295 if (Ext.isIEQuirks) {
3297 overflow = style.overflow;
3298 me.setStyle({ overflow: 'hidden'});
3301 height = dom.offsetHeight;
3303 height = MATH.max(height, hidden ? 0 : dom.clientHeight) || 0;
3305 // IE9 Direct2D dimension rounding bug
3306 if (!hidden && Ext.supports.Direct2DBug) {
3307 floating = me.adjustDirect2DDimension('height');
3308 if (preciseHeight) {
3311 else if (floating > 0 && floating < 0.5) {
3316 if (contentHeight) {
3317 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
3320 if (Ext.isIEQuirks) {
3321 me.setStyle({ overflow: overflow});
3331 * Returns the offset width of the element
3332 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
3333 * @return {Number} The element's width
3335 getWidth: function(contentWidth, preciseWidth) {
3338 hidden = Ext.isIE && me.isStyle('display', 'none'),
3339 rect, width, overflow, style, floating, parentPosition;
3341 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3342 // We will put the overflow back to it's original value when we are done measuring.
3343 if (Ext.isIEQuirks) {
3345 overflow = style.overflow;
3346 me.setStyle({overflow: 'hidden'});
3349 // Fix Opera 10.5x width calculation issues
3350 if (Ext.isOpera10_5) {
3351 if (dom.parentNode.currentStyle.position === 'relative') {
3352 parentPosition = dom.parentNode.style.position;
3353 dom.parentNode.style.position = 'static';
3354 width = dom.offsetWidth;
3355 dom.parentNode.style.position = parentPosition;
3357 width = Math.max(width || 0, dom.offsetWidth);
3359 // Gecko will in some cases report an offsetWidth that is actually less than the width of the
3360 // text contents, because it measures fonts with sub-pixel precision but rounds the calculated
3361 // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise
3362 // subpixel measurements so we can force them to always be rounded up. See
3363 // https://bugzilla.mozilla.org/show_bug.cgi?id=458617
3364 } else if (Ext.supports.BoundingClientRect) {
3365 rect = dom.getBoundingClientRect();
3366 width = rect.right - rect.left;
3367 width = preciseWidth ? width : Math.ceil(width);
3369 width = dom.offsetWidth;
3372 width = MATH.max(width, hidden ? 0 : dom.clientWidth) || 0;
3374 // IE9 Direct2D dimension rounding bug
3375 if (!hidden && Ext.supports.Direct2DBug) {
3376 floating = me.adjustDirect2DDimension('width');
3380 else if (floating > 0 && floating < 0.5) {
3386 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
3389 if (Ext.isIEQuirks) {
3390 me.setStyle({ overflow: overflow});
3400 * Set the width of this Element.
3401 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
3402 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3403 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
3405 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3406 * @return {Ext.core.Element} this
3408 setWidth : function(width, animate){
3410 width = me.adjustWidth(width);
3411 if (!animate || !me.anim) {
3412 me.dom.style.width = me.addUnits(width);
3415 if (!Ext.isObject(animate)) {
3418 me.animate(Ext.applyIf({
3428 * Set the height of this Element.
3430 // change the height to 200px and animate with default configuration
3431 Ext.fly('elementId').setHeight(200, true);
3433 // change the height to 150px and animate with a custom configuration
3434 Ext.fly('elId').setHeight(150, {
3435 duration : .5, // animation will have a duration of .5 seconds
3436 // will change the content to "finished"
3437 callback: function(){ this.{@link #update}("finished"); }
3440 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
3441 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
3442 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
3444 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3445 * @return {Ext.core.Element} this
3447 setHeight : function(height, animate){
3449 height = me.adjustHeight(height);
3450 if (!animate || !me.anim) {
3451 me.dom.style.height = me.addUnits(height);
3454 if (!Ext.isObject(animate)) {
3457 me.animate(Ext.applyIf({
3467 * Gets the width of the border(s) for the specified side(s)
3468 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
3469 * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
3470 * @return {Number} The width of the sides passed added together
3472 getBorderWidth : function(side){
3473 return this.addStyles(side, borders);
3477 * Gets the width of the padding(s) for the specified side(s)
3478 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
3479 * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
3480 * @return {Number} The padding of the sides passed added together
3482 getPadding : function(side){
3483 return this.addStyles(side, paddings);
3487 * Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
3488 * @return {Ext.core.Element} this
3494 if(!data(dom, ISCLIPPED)){
3495 data(dom, ISCLIPPED, true);
3496 data(dom, ORIGINALCLIP, {
3497 o: me.getStyle(OVERFLOW),
3498 x: me.getStyle(OVERFLOWX),
3499 y: me.getStyle(OVERFLOWY)
3501 me.setStyle(OVERFLOW, HIDDEN);
3502 me.setStyle(OVERFLOWX, HIDDEN);
3503 me.setStyle(OVERFLOWY, HIDDEN);
3509 * Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
3510 * @return {Ext.core.Element} this
3512 unclip : function(){
3517 if(data(dom, ISCLIPPED)){
3518 data(dom, ISCLIPPED, false);
3519 clip = data(dom, ORIGINALCLIP);
3521 me.setStyle(OVERFLOW, o.o);
3524 me.setStyle(OVERFLOWX, o.x);
3527 me.setStyle(OVERFLOWY, o.y);
3534 addStyles : function(sides, styles){
3536 sidesArr = sides.match(wordsRe),
3538 len = sidesArr.length,
3540 for (; i < len; i++) {
3542 size = side && parseInt(this.getStyle(styles[side]), 10);
3544 totalSize += MATH.abs(size);
3553 * More flexible version of {@link #setStyle} for setting style properties.
3554 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
3555 * a function which returns such a specification.
3556 * @return {Ext.core.Element} this
3558 applyStyles : function(style){
3559 Ext.core.DomHelper.applyStyles(this.dom, style);
3564 * Returns an object with properties matching the styles requested.
3565 * For example, el.getStyles('color', 'font-size', 'width') might return
3566 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
3567 * @param {String} style1 A style name
3568 * @param {String} style2 A style name
3569 * @param {String} etc.
3570 * @return {Object} The style object
3572 getStyles : function(){
3574 len = arguments.length,
3577 for(; i < len; ++i) {
3578 style = arguments[i];
3579 styles[style] = this.getStyle(style);
3585 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
3586 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
3587 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
3588 * {@link Ext.panel.Panel} when <tt>{@link Ext.panel.Panel#frame frame=true}</tt>, {@link Ext.window.Window}). The markup
3589 * is of this form:</p>
3591 Ext.core.Element.boxMarkup =
3592 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
3593 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
3594 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
3596 * <p>Example usage:</p>
3599 Ext.get("foo").boxWrap();
3601 // You can also add a custom class and use CSS inheritance rules to customize the box look.
3602 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
3603 // for how to create a custom box wrap style.
3604 Ext.get("foo").boxWrap().addCls("x-box-blue");
3606 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
3607 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
3608 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
3609 * also supply all of the necessary rules.
3610 * @return {Ext.core.Element} The outermost wrapping element of the created box structure.
3612 boxWrap : function(cls){
3613 cls = cls || Ext.baseCSSPrefix + 'box';
3614 var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(Ext.core.Element.boxMarkup, cls) + "</div>"));
3615 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
3620 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
3621 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
3622 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3623 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
3624 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
3626 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
3627 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3628 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
3630 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3631 * @return {Ext.core.Element} this
3633 setSize : function(width, height, animate){
3635 if (Ext.isObject(width)){ // in case of object from getSize()
3636 height = width.height;
3637 width = width.width;
3639 width = me.adjustWidth(width);
3640 height = me.adjustHeight(height);
3641 if(!animate || !me.anim){
3642 me.dom.style.width = me.addUnits(width);
3643 me.dom.style.height = me.addUnits(height);
3646 if (!Ext.isObject(animate)) {
3649 me.animate(Ext.applyIf({
3660 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
3661 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
3662 * if a height has not been set using CSS.
3665 getComputedHeight : function(){
3667 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
3669 h = parseFloat(me.getStyle('height')) || 0;
3670 if(!me.isBorderBox()){
3671 h += me.getFrameWidth('tb');
3678 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
3679 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
3680 * if a width has not been set using CSS.
3683 getComputedWidth : function(){
3685 w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);
3688 w = parseFloat(me.getStyle('width')) || 0;
3689 if(!me.isBorderBox()){
3690 w += me.getFrameWidth('lr');
3697 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
3698 for more information about the sides.
3699 * @param {String} sides
3702 getFrameWidth : function(sides, onlyContentBox){
3703 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
3707 * Sets up event handlers to add and remove a css class when the mouse is over this element
3708 * @param {String} className
3709 * @return {Ext.core.Element} this
3711 addClsOnOver : function(className){
3715 Ext.fly(dom, INTERNAL).addCls(className);
3718 Ext.fly(dom, INTERNAL).removeCls(className);
3725 * Sets up event handlers to add and remove a css class when this element has the focus
3726 * @param {String} className
3727 * @return {Ext.core.Element} this
3729 addClsOnFocus : function(className){
3732 me.on("focus", function(){
3733 Ext.fly(dom, INTERNAL).addCls(className);
3735 me.on("blur", function(){
3736 Ext.fly(dom, INTERNAL).removeCls(className);
3742 * 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)
3743 * @param {String} className
3744 * @return {Ext.core.Element} this
3746 addClsOnClick : function(className){
3748 this.on("mousedown", function(){
3749 Ext.fly(dom, INTERNAL).addCls(className);
3750 var d = Ext.getDoc(),
3752 Ext.fly(dom, INTERNAL).removeCls(className);
3753 d.removeListener("mouseup", fn);
3755 d.on("mouseup", fn);
3761 * <p>Returns the dimensions of the element available to lay content out in.<p>
3762 * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
3763 * example:<pre><code>
3764 var vpSize = Ext.getBody().getViewSize();
3766 // all Windows created afterwards will have a default value of 90% height and 95% width
3767 Ext.Window.override({
3768 width: vpSize.width * 0.9,
3769 height: vpSize.height * 0.95
3771 // To handle window resizing you would have to hook onto onWindowResize.
3774 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
3775 * To obtain the size including scrollbars, use getStyleSize
3777 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
3780 getViewSize : function(){
3783 isDoc = (dom == Ext.getDoc().dom || dom == Ext.getBody().dom),
3784 style, overflow, ret;
3786 // If the body, use static methods
3789 width : Ext.core.Element.getViewWidth(),
3790 height : Ext.core.Element.getViewHeight()
3793 // Else use clientHeight/clientWidth
3796 // IE 6 & IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3797 // We will put the overflow back to it's original value when we are done measuring.
3798 if (Ext.isIE6 || Ext.isIEQuirks) {
3800 overflow = style.overflow;
3801 me.setStyle({ overflow: 'hidden'});
3804 width : dom.clientWidth,
3805 height : dom.clientHeight
3807 if (Ext.isIE6 || Ext.isIEQuirks) {
3808 me.setStyle({ overflow: overflow });
3815 * <p>Returns the dimensions of the element available to lay content out in.<p>
3817 * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
3818 * To obtain the size excluding scrollbars, use getViewSize
3820 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
3823 getStyleSize : function(){
3827 isDoc = (d == doc || d == doc.body),
3831 // If the body, use static methods
3834 width : Ext.core.Element.getViewWidth(),
3835 height : Ext.core.Element.getViewHeight()
3838 // Use Styles if they are set
3839 if(s.width && s.width != 'auto'){
3840 w = parseFloat(s.width);
3841 if(me.isBorderBox()){
3842 w -= me.getFrameWidth('lr');
3845 // Use Styles if they are set
3846 if(s.height && s.height != 'auto'){
3847 h = parseFloat(s.height);
3848 if(me.isBorderBox()){
3849 h -= me.getFrameWidth('tb');
3852 // Use getWidth/getHeight if style not set.
3853 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
3857 * Returns the size of the element.
3858 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
3859 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
3861 getSize : function(contentSize){
3862 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
3866 * Forces the browser to repaint this element
3867 * @return {Ext.core.Element} this
3869 repaint : function(){
3871 this.addCls(Ext.baseCSSPrefix + 'repaint');
3872 setTimeout(function(){
3873 Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
3879 * Disables text selection for this element (normalized across browsers)
3880 * @return {Ext.core.Element} this
3882 unselectable : function(){
3884 me.dom.unselectable = "on";
3886 me.swallowEvent("selectstart", true);
3887 me.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
3888 me.addCls(Ext.baseCSSPrefix + 'unselectable');
3894 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
3895 * then it returns the calculated width of the sides (see getPadding)
3896 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
3897 * @return {Object/Number}
3899 getMargin : function(side){
3901 hash = {t:"top", l:"left", r:"right", b: "bottom"},
3906 for (key in me.margins){
3907 o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
3911 return me.addStyles.call(me, side, me.margins);
3917 * @class Ext.core.Element
3920 * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
3924 Ext.core.Element.VISIBILITY = 1;
3926 * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
3930 Ext.core.Element.DISPLAY = 2;
3933 * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen)
3938 Ext.core.Element.OFFSETS = 3;
3941 Ext.core.Element.ASCLASS = 4;
3944 * Defaults to 'x-hide-nosize'
3948 Ext.core.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize';
3950 Ext.core.Element.addMethods(function(){
3951 var El = Ext.core.Element,
3952 OPACITY = "opacity",
3953 VISIBILITY = "visibility",
3954 DISPLAY = "display",
3956 OFFSETS = "offsets",
3957 ASCLASS = "asclass",
3960 ORIGINALDISPLAY = 'originalDisplay',
3961 VISMODE = 'visibilityMode',
3962 ISVISIBLE = 'isVisible',
3964 getDisplay = function(dom){
3965 var d = data(dom, ORIGINALDISPLAY);
3966 if(d === undefined){
3967 data(dom, ORIGINALDISPLAY, d = '');
3971 getVisMode = function(dom){
3972 var m = data(dom, VISMODE);
3973 if(m === undefined){
3974 data(dom, VISMODE, m = 1);
3981 * The element's default display mode (defaults to "")
3984 originalDisplay : "",
3988 * Sets the element's visibility mode. When setVisible() is called it
3989 * will use this to determine whether to set the visibility or the display property.
3990 * @param {Number} visMode Ext.core.Element.VISIBILITY or Ext.core.Element.DISPLAY
3991 * @return {Ext.core.Element} this
3993 setVisibilityMode : function(visMode){
3994 data(this.dom, VISMODE, visMode);
3999 * Checks whether the element is currently visible using both visibility and display properties.
4000 * @return {Boolean} True if the element is currently visible, else false
4002 isVisible : function() {
4005 visible = data(dom, ISVISIBLE);
4007 if(typeof visible == 'boolean'){ //return the cached value if registered
4010 //Determine the current state based on display states
4011 visible = !me.isStyle(VISIBILITY, HIDDEN) &&
4012 !me.isStyle(DISPLAY, NONE) &&
4013 !((getVisMode(dom) == El.ASCLASS) && me.hasCls(me.visibilityCls || El.visibilityCls));
4015 data(dom, ISVISIBLE, visible);
4020 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
4021 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
4022 * @param {Boolean} visible Whether the element is visible
4023 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4024 * @return {Ext.core.Element} this
4026 setVisible : function(visible, animate){
4027 var me = this, isDisplay, isVisibility, isOffsets, isNosize,
4029 visMode = getVisMode(dom);
4032 // hideMode string override
4033 if (typeof animate == 'string'){
4036 visMode = El.DISPLAY;
4039 visMode = El.VISIBILITY;
4042 visMode = El.OFFSETS;
4046 visMode = El.ASCLASS;
4049 me.setVisibilityMode(visMode);
4053 if (!animate || !me.anim) {
4054 if(visMode == El.ASCLASS ){
4056 me[visible?'removeCls':'addCls'](me.visibilityCls || El.visibilityCls);
4058 } else if (visMode == El.DISPLAY){
4060 return me.setDisplayed(visible);
4062 } else if (visMode == El.OFFSETS){
4065 // Remember position for restoring, if we are not already hidden by offsets.
4066 if (!me.hideModeStyles) {
4067 me.hideModeStyles = {
4068 position: me.getStyle('position'),
4069 top: me.getStyle('top'),
4070 left: me.getStyle('left')
4073 me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
4076 // Only "restore" as position if we have actually been hidden using offsets.
4077 // Calling setVisible(true) on a positioned element should not reposition it.
4078 else if (me.hideModeStyles) {
4079 me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
4080 delete me.hideModeStyles;
4085 // Show by clearing visibility style. Explicitly setting to "visible" overrides parent visibility setting.
4086 dom.style.visibility = visible ? '' : HIDDEN;
4089 // closure for composites
4091 me.setOpacity(0.01);
4092 me.setVisible(true);
4094 if (!Ext.isObject(animate)) {
4100 me.animate(Ext.applyIf({
4101 callback: function() {
4102 visible || me.setVisible(false).setOpacity(1);
4105 opacity: (visible) ? 1 : 0
4109 data(dom, ISVISIBLE, visible); //set logical visibility state
4116 * Determine if the Element has a relevant height and width available based
4117 * upon current logical visibility state
4119 hasMetrics : function(){
4121 return this.isVisible() || (getVisMode(dom) == El.OFFSETS) || (getVisMode(dom) == El.VISIBILITY);
4125 * Toggles the element's visibility or display, depending on visibility mode.
4126 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4127 * @return {Ext.core.Element} this
4129 toggle : function(animate){
4131 me.setVisible(!me.isVisible(), me.anim(animate));
4136 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
4137 * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
4138 * @return {Ext.core.Element} this
4140 setDisplayed : function(value) {
4141 if(typeof value == "boolean"){
4142 value = value ? getDisplay(this.dom) : NONE;
4144 this.setStyle(DISPLAY, value);
4149 fixDisplay : function(){
4151 if (me.isStyle(DISPLAY, NONE)) {
4152 me.setStyle(VISIBILITY, HIDDEN);
4153 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
4154 if (me.isStyle(DISPLAY, NONE)) { // if that fails, default to block
4155 me.setStyle(DISPLAY, "block");
4161 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
4162 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
4163 * @return {Ext.core.Element} this
4165 hide : function(animate){
4166 // hideMode override
4167 if (typeof animate == 'string'){
4168 this.setVisible(false, animate);
4171 this.setVisible(false, this.anim(animate));
4176 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
4177 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
4178 * @return {Ext.core.Element} this
4180 show : function(animate){
4181 // hideMode override
4182 if (typeof animate == 'string'){
4183 this.setVisible(true, animate);
4186 this.setVisible(true, this.anim(animate));
4192 * @class Ext.core.Element
4194 Ext.applyIf(Ext.core.Element.prototype, {
4195 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
4196 animate: function(config) {
4199 me = Ext.get(me.dom);
4201 if (Ext.fx.Manager.hasFxBlock(me.id)) {
4204 Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(config)));
4208 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
4209 anim: function(config) {
4210 if (!Ext.isObject(config)) {
4211 return (config) ? {} : false;
4215 duration = config.duration || Ext.fx.Anim.prototype.duration,
4216 easing = config.easing || 'ease',
4219 if (config.stopAnimation) {
4223 Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
4225 // Clear any 'paused' defaults.
4226 Ext.fx.Manager.setFxDefaults(me.id, {
4232 remove: config.remove,
4233 alternate: config.alternate || false,
4236 callback: config.callback,
4237 listeners: config.listeners,
4238 iterations: config.iterations || 1,
4239 scope: config.scope,
4240 block: config.block,
4241 concurrent: config.concurrent,
4242 delay: config.delay || 0,
4244 keyframes: config.keyframes,
4245 from: config.from || {},
4246 to: Ext.apply({}, config)
4248 Ext.apply(animConfig.to, config.to);
4250 // Anim API properties - backward compat
4251 delete animConfig.to.to;
4252 delete animConfig.to.from;
4253 delete animConfig.to.remove;
4254 delete animConfig.to.alternate;
4255 delete animConfig.to.keyframes;
4256 delete animConfig.to.iterations;
4257 delete animConfig.to.listeners;
4258 delete animConfig.to.target;
4259 delete animConfig.to.paused;
4260 delete animConfig.to.callback;
4261 delete animConfig.to.scope;
4262 delete animConfig.to.duration;
4263 delete animConfig.to.easing;
4264 delete animConfig.to.concurrent;
4265 delete animConfig.to.block;
4266 delete animConfig.to.stopAnimation;
4267 delete animConfig.to.delay;
4272 * Slides the element into view. An anchor point can be optionally passed to set the point of
4273 * origin for the slide effect. This function automatically handles wrapping the element with
4274 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
4277 // default: slide the element in from the top
4280 // custom: slide the element in from the right with a 2-second duration
4281 el.slideIn('r', { duration: 2 });
4283 // common config options shown with default values
4289 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
4290 * @param {Object} options (optional) Object literal with any of the Fx config options
4291 * @return {Ext.core.Element} The Element
4293 slideIn: function(anchor, obj, slideOut) {
4295 elStyle = me.dom.style,
4296 beforeAnim, wrapAnim;
4298 anchor = anchor || "t";
4301 beforeAnim = function() {
4302 var animScope = this,
4303 listeners = obj.listeners,
4304 box, position, restoreSize, wrap, anim;
4311 if ((anchor == 't' || anchor == 'b') && box.height == 0) {
4312 box.height = me.dom.scrollHeight;
4314 else if ((anchor == 'l' || anchor == 'r') && box.width == 0) {
4315 box.width = me.dom.scrollWidth;
4318 position = me.getPositioning();
4319 me.setSize(box.width, box.height);
4323 visibility: slideOut ? 'visible' : 'hidden'
4326 wrap.setPositioning(position);
4327 if (wrap.isStyle('position', 'static')) {
4328 wrap.position('relative');
4330 me.clearPositioning('auto');
4333 // This element is temporarily positioned absolute within its wrapper.
4334 // Restore to its default, CSS-inherited visibility setting.
4335 // We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap.
4338 position: 'absolute'
4341 wrap.setSize(box.width, box.height);
4348 width: box.width + 'px',
4352 width: box.width + 'px',
4353 height: box.height + 'px'
4356 elStyle.bottom = '0px';
4362 height: box.height + 'px'
4365 width: box.width + 'px',
4366 height: box.height + 'px'
4369 elStyle.right = '0px';
4374 x: box.x + box.width,
4376 height: box.height + 'px'
4380 width: box.width + 'px',
4381 height: box.height + 'px'
4388 y: box.y + box.height,
4389 width: box.width + 'px',
4394 width: box.width + 'px',
4395 height: box.height + 'px'
4408 width: box.width + 'px',
4409 height: box.height + 'px'
4412 elStyle.bottom = '0px';
4413 elStyle.right = '0px';
4418 x: box.x + box.width,
4424 width: box.width + 'px',
4425 height: box.height + 'px'
4428 elStyle.right = '0px';
4433 x: box.x + box.width,
4434 y: box.y + box.height,
4441 width: box.width + 'px',
4442 height: box.height + 'px'
4449 y: box.y + box.height,
4455 width: box.width + 'px',
4456 height: box.height + 'px'
4459 elStyle.bottom = '0px';
4464 wrapAnim = Ext.apply({}, obj);
4465 delete wrapAnim.listeners;
4466 wrapAnim = Ext.create('Ext.fx.Anim', Ext.applyIf(wrapAnim, {
4470 from: slideOut ? anim.to : anim.from,
4471 to: slideOut ? anim.from : anim.to
4474 // In the absence of a callback, this listener MUST be added first
4475 wrapAnim.on('afteranimate', function() {
4477 me.setPositioning(position);
4478 if (obj.useDisplay) {
4479 me.setDisplayed(false);
4485 me.clearPositioning();
4486 me.setPositioning(position);
4489 wrap.dom.parentNode.insertBefore(me.dom, wrap.dom);
4492 me.setSize(box.width, box.height);
4495 // Add configured listeners after
4497 wrapAnim.on(listeners);
4502 duration: obj.duration ? obj.duration * 2 : 1000,
4509 if (wrapAnim && wrapAnim.running) {
4521 * Slides the element out of view. An anchor point can be optionally passed to set the end point
4522 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
4523 * 'hidden') but block elements will still take up space in the document. The element must be removed
4524 * from the DOM using the 'remove' config option if desired. This function automatically handles
4525 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
4528 // default: slide the element out to the top
4531 // custom: slide the element out to the right with a 2-second duration
4532 el.slideOut('r', { duration: 2 });
4534 // common config options shown with default values
4542 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
4543 * @param {Object} options (optional) Object literal with any of the Fx config options
4544 * @return {Ext.core.Element} The Element
4546 slideOut: function(anchor, o) {
4547 return this.slideIn(anchor, o, true);
4551 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
4552 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
4558 // common config options shown with default values
4565 * @param {Object} options (optional) Object literal with any of the Fx config options
4566 * @return {Ext.core.Element} The Element
4569 puff: function(obj) {
4572 obj = Ext.applyIf(obj || {}, {
4578 beforeAnim = function() {
4582 var box = me.getBox(),
4583 fontSize = me.getStyle('fontSize'),
4584 position = me.getPositioning();
4586 width: box.width * 2,
4587 height: box.height * 2,
4588 x: box.x - (box.width / 2),
4589 y: box.y - (box.height /2),
4593 this.on('afteranimate',function() {
4595 if (obj.useDisplay) {
4596 me.setDisplayed(false);
4601 me.setPositioning(position);
4602 me.setStyle({fontSize: fontSize});
4608 duration: obj.duration,
4620 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
4621 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
4622 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
4628 // all config options shown with default values
4636 * @param {Object} options (optional) Object literal with any of the Fx config options
4637 * @return {Ext.core.Element} The Element
4639 switchOff: function(obj) {
4643 obj = Ext.applyIf(obj || {}, {
4650 beforeAnim = function() {
4651 var animScope = this,
4652 size = me.getSize(),
4657 position = me.getPositioning();
4659 keyframe = Ext.create('Ext.fx.Animator', {
4661 duration: obj.duration,
4669 y: xy[1] + size.height / 2
4673 x: xy[0] + size.width / 2
4677 keyframe.on('afteranimate', function() {
4678 if (obj.useDisplay) {
4679 me.setDisplayed(false);
4684 me.setPositioning(position);
4690 duration: (obj.duration * 2),
4701 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
4704 // default: a single light blue ripple
4707 // custom: 3 red ripples lasting 3 seconds total
4708 el.frame("#ff0000", 3, { duration: 3 });
4710 // common config options shown with default values
4711 el.frame("#C3DAF9", 1, {
4712 duration: 1 //duration of each individual ripple.
4713 // Note: Easing is not configurable and will be ignored if included
4716 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
4717 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
4718 * @param {Object} options (optional) Object literal with any of the Fx config options
4719 * @return {Ext.core.Element} The Element
4721 frame : function(color, count, obj){
4725 color = color || '#C3DAF9';
4729 beforeAnim = function() {
4731 var animScope = this,
4733 proxy = Ext.getBody().createChild({
4735 position : 'absolute',
4736 'pointer-events': 'none',
4738 border : '0px solid ' + color
4742 proxyAnim = Ext.create('Ext.fx.Anim', {
4744 duration: obj.duration || 1000,
4759 height: box.height + 40,
4760 width: box.width + 40
4763 proxyAnim.on('afteranimate', function() {
4770 duration: (obj.duration * 2) || 2000,
4781 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
4782 * ending point of the effect.
4785 // default: slide the element downward while fading out
4788 // custom: slide the element out to the right with a 2-second duration
4789 el.ghost('r', { duration: 2 });
4791 // common config options shown with default values
4797 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
4798 * @param {Object} options (optional) Object literal with any of the Fx config options
4799 * @return {Ext.core.Element} The Element
4801 ghost: function(anchor, obj) {
4805 anchor = anchor || "b";
4806 beforeAnim = function() {
4807 var width = me.getWidth(),
4808 height = me.getHeight(),
4810 position = me.getPositioning(),
4816 to.y = xy[1] - height;
4819 to.x = xy[0] - width;
4822 to.x = xy[0] + width;
4825 to.y = xy[1] + height;
4828 to.x = xy[0] - width;
4829 to.y = xy[1] - height;
4832 to.x = xy[0] - width;
4833 to.y = xy[1] + height;
4836 to.x = xy[0] + width;
4837 to.y = xy[1] + height;
4840 to.x = xy[0] + width;
4841 to.y = xy[1] - height;
4845 this.on('afteranimate', function () {
4849 me.setPositioning(position);
4854 me.animate(Ext.applyIf(obj || {}, {
4867 * Highlights the Element by setting a color (applies to the background-color by default, but can be
4868 * changed using the "attr" config option) and then fading back to the original color. If no original
4869 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
4872 // default: highlight background to yellow
4875 // custom: highlight foreground text to blue for 2 seconds
4876 el.highlight("0000ff", { attr: 'color', duration: 2 });
4878 // common config options shown with default values
4879 el.highlight("ffff9c", {
4880 attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value
4881 endColor: (current color) or "ffffff",
4886 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
4887 * @param {Object} options (optional) Object literal with any of the Fx config options
4888 * @return {Ext.core.Element} The Element
4890 highlight: function(color, o) {
4894 restore, to, attr, lns, event, fn;
4897 lns = o.listeners || {};
4898 attr = o.attr || 'backgroundColor';
4899 from[attr] = color || 'ffff9c';
4903 to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
4909 // Don't apply directly on lns, since we reference it in our own callbacks below
4910 o.listeners = Ext.apply(Ext.apply({}, lns), {
4911 beforeanimate: function() {
4912 restore = dom.style[attr];
4916 event = lns.beforeanimate;
4918 fn = event.fn || event;
4919 return fn.apply(event.scope || lns.scope || window, arguments);
4922 afteranimate: function() {
4924 dom.style[attr] = restore;
4927 event = lns.afteranimate;
4929 fn = event.fn || event;
4930 fn.apply(event.scope || lns.scope || window, arguments);
4935 me.animate(Ext.apply({}, o, {
4946 * Creates a pause before any subsequent queued effects begin. If there are
4947 * no effects queued after the pause it will have no effect.
4952 * @param {Number} seconds The length of time to pause (in seconds)
4953 * @return {Ext.Element} The Element
4955 pause: function(ms) {
4957 Ext.fx.Manager.setFxDefaults(me.id, {
4964 * Fade an element in (from transparent to opaque). The ending opacity can be specified
4965 * using the <tt>{@link #endOpacity}</tt> config option.
4968 // default: fade in from opacity 0 to 100%
4971 // custom: fade in from opacity 0 to 75% over 2 seconds
4972 el.fadeIn({ endOpacity: .75, duration: 2});
4974 // common config options shown with default values
4976 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
4981 * @param {Object} options (optional) Object literal with any of the Fx config options
4982 * @return {Ext.Element} The Element
4984 fadeIn: function(o) {
4985 this.animate(Ext.apply({}, o, {
4992 * Fade an element out (from opaque to transparent). The ending opacity can be specified
4993 * using the <tt>{@link #endOpacity}</tt> config option. Note that IE may require
4994 * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
4997 // default: fade out from the element's current opacity to 0
5000 // custom: fade out from the element's current opacity to 25% over 2 seconds
5001 el.fadeOut({ endOpacity: .25, duration: 2});
5003 // common config options shown with default values
5005 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
5012 * @param {Object} options (optional) Object literal with any of the Fx config options
5013 * @return {Ext.Element} The Element
5015 fadeOut: function(o) {
5016 this.animate(Ext.apply({}, o, {
5024 * Animates the transition of an element's dimensions from a starting height/width
5025 * to an ending height/width. This method is a convenience implementation of {@link shift}.
5028 // change height and width to 100x100 pixels
5031 // common config options shown with default values. The height and width will default to
5032 // the element's existing values if passed as null.
5034 [element's width],
5035 [element's height], {
5041 * @param {Number} width The new width (pass undefined to keep the original width)
5042 * @param {Number} height The new height (pass undefined to keep the original height)
5043 * @param {Object} options (optional) Object literal with any of the Fx config options
5044 * @return {Ext.Element} The Element
5046 scale: function(w, h, o) {
5047 this.animate(Ext.apply({}, o, {
5056 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
5057 * Any of these properties not specified in the config object will not be changed. This effect
5058 * requires that at least one new dimension, position or opacity setting must be passed in on
5059 * the config object in order for the function to have any effect.
5062 // slide the element horizontally to x position 200 while changing the height and opacity
5063 el.shift({ x: 200, height: 50, opacity: .8 });
5065 // common config options shown with default values.
5067 width: [element's width],
5068 height: [element's height],
5069 x: [element's x position],
5070 y: [element's y position],
5071 opacity: [element's opacity],
5076 * @param {Object} options Object literal with any of the Fx config options
5077 * @return {Ext.Element} The Element
5079 shift: function(config) {
5080 this.animate(config);
5086 * @class Ext.core.Element
5088 Ext.applyIf(Ext.core.Element, {
5089 unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
5090 camelRe: /(-[a-z])/gi,
5091 opacityRe: /alpha\(opacity=(.*)\)/i,
5092 cssRe: /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
5095 borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
5096 paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
5097 margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
5099 // Reference the prototype's version of the method. Signatures are identical.
5100 addUnits : Ext.core.Element.prototype.addUnits,
5103 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
5104 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
5106 * @param {Number|String} box The encoded margins
5107 * @return {Object} An object with margin sizes for top, right, bottom and left
5109 parseBox : function(box) {
5110 if (Ext.isObject(box)) {
5113 right: box.right || 0,
5114 bottom: box.bottom || 0,
5118 if (typeof box != 'string') {
5119 box = box.toString();
5121 var parts = box.split(' '),
5125 parts[1] = parts[2] = parts[3] = parts[0];
5128 parts[2] = parts[0];
5129 parts[3] = parts[1];
5132 parts[3] = parts[1];
5136 top :parseFloat(parts[0]) || 0,
5137 right :parseFloat(parts[1]) || 0,
5138 bottom:parseFloat(parts[2]) || 0,
5139 left :parseFloat(parts[3]) || 0
5146 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
5147 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
5149 * @param {Number|String} box The encoded margins
5150 * @param {String} units The type of units to add
5151 * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
5153 unitizeBox : function(box, units) {
5154 var A = this.addUnits,
5155 B = this.parseBox(box);
5157 return A(B.top, units) + ' ' +
5158 A(B.right, units) + ' ' +
5159 A(B.bottom, units) + ' ' +
5165 camelReplaceFn : function(m, a) {
5166 return a.charAt(1).toUpperCase();
5170 * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
5173 * <li>border-width -> borderWidth</li>
5174 * <li>padding-top -> paddingTop</li>
5177 * @param {String} prop The property to normalize
5178 * @return {String} The normalized string
5180 normalize : function(prop) {
5181 if (prop == 'float') {
5182 prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
5184 return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
5188 * Retrieves the document height
5190 * @return {Number} documentHeight
5192 getDocumentHeight: function() {
5193 return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
5197 * Retrieves the document width
5199 * @return {Number} documentWidth
5201 getDocumentWidth: function() {
5202 return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
5206 * Retrieves the viewport height of the window.
5208 * @return {Number} viewportHeight
5210 getViewportHeight: function(){
5211 return window.innerHeight;
5215 * Retrieves the viewport width of the window.
5217 * @return {Number} viewportWidth
5219 getViewportWidth : function() {
5220 return window.innerWidth;
5224 * Retrieves the viewport size of the window.
5226 * @return {Object} object containing width and height properties
5228 getViewSize : function() {
5230 width: window.innerWidth,
5231 height: window.innerHeight
5236 * Retrieves the current orientation of the window. This is calculated by
5237 * determing if the height is greater than the width.
5239 * @return {String} Orientation of window: 'portrait' or 'landscape'
5241 getOrientation : function() {
5242 if (Ext.supports.OrientationChange) {
5243 return (window.orientation == 0) ? 'portrait' : 'landscape';
5246 return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
5250 * Returns the top Element that is located at the passed coordinates
5252 * @param {Number} x The x coordinate
5253 * @param {Number} x The y coordinate
5254 * @return {String} The found Element
5256 fromPoint: function(x, y) {
5257 return Ext.get(document.elementFromPoint(x, y));
5261 * Converts a CSS string into an object with a property for each style.
5263 * The sample code below would return an object with 2 properties, one
5264 * for background-color and one for color.</p>
5266 var css = 'background-color: red;color: blue; ';
5267 console.log(Ext.core.Element.parseStyles(css));
5270 * @param {String} styles A CSS string
5271 * @return {Object} styles
5273 parseStyles: function(styles){
5279 // Since we're using the g flag on the regex, we need to set the lastIndex.
5280 // This automatically happens on some implementations, but not others, see:
5281 // http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
5282 // http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
5283 cssRe.lastIndex = 0;
5284 while ((matches = cssRe.exec(styles))) {
5285 out[matches[1]] = matches[2];
5293 * @class Ext.CompositeElementLite
5294 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
5295 * members, or to perform collective actions upon the whole set.</p>
5296 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
5297 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
5298 * Example:<pre><code>
5299 var els = Ext.select("#some-el div.some-class");
5300 // or select directly from an existing element
5301 var el = Ext.get('some-el');
5302 el.select('div.some-class');
5304 els.setWidth(100); // all elements become 100 width
5305 els.hide(true); // all elements fade out and hide
5307 els.setWidth(100).hide(true);
5310 Ext.CompositeElementLite = function(els, root){
5312 * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
5313 * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
5314 * to augment the capabilities of the CompositeElementLite class may use it when adding
5315 * methods to the class.</p>
5316 * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
5317 * following siblings of selected elements, the code would be</p><code><pre>
5318 Ext.override(Ext.CompositeElementLite, {
5319 nextAll: function() {
5320 var els = this.elements, i, l = els.length, n, r = [], ri = -1;
5322 // Loop through all elements in this Composite, accumulating
5323 // an Array of all siblings.
5324 for (i = 0; i < l; i++) {
5325 for (n = els[i].nextSibling; n; n = n.nextSibling) {
5330 // Add all found siblings to this Composite
5335 * @property elements
5338 this.add(els, root);
5339 this.el = new Ext.core.Element.Flyweight();
5342 Ext.CompositeElementLite.prototype = {
5346 getElement : function(el){
5347 // Set the shared flyweight dom property to the current element
5355 transformElement : function(el){
5356 return Ext.getDom(el);
5360 * Returns the number of elements in this Composite.
5363 getCount : function(){
5364 return this.elements.length;
5367 * Adds elements to this Composite object.
5368 * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
5369 * @return {CompositeElement} This Composite object.
5371 add : function(els, root){
5373 elements = me.elements;
5377 if(typeof els == "string"){
5378 els = Ext.core.Element.selectorFunction(els, root);
5379 }else if(els.isComposite){
5381 }else if(!Ext.isIterable(els)){
5385 for(var i = 0, len = els.length; i < len; ++i){
5386 elements.push(me.transformElement(els[i]));
5391 invoke : function(fn, args){
5398 for(i = 0; i < len; i++) {
5401 Ext.core.Element.prototype[fn].apply(me.getElement(e), args);
5407 * Returns a flyweight Element of the dom element object at the specified index
5408 * @param {Number} index
5409 * @return {Ext.core.Element}
5411 item : function(index){
5413 el = me.elements[index],
5417 out = me.getElement(el);
5422 // fixes scope with flyweight
5423 addListener : function(eventName, handler, scope, opt){
5424 var els = this.elements,
5428 for(i = 0; i<len; i++) {
5431 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
5437 * <p>Calls the passed function for each element in this composite.</p>
5438 * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
5439 * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
5440 * <b>This is the flyweight (shared) Ext.core.Element instance, so if you require a
5441 * a reference to the dom node, use el.dom.</b></div></li>
5442 * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
5443 * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
5445 * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
5446 * @return {CompositeElement} this
5448 each : function(fn, scope){
5454 for(i = 0; i<len; i++) {
5457 e = this.getElement(e);
5458 if(fn.call(scope || e, e, me, i) === false){
5467 * Clears this Composite and adds the elements passed.
5468 * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
5469 * @return {CompositeElement} this
5471 fill : function(els){
5479 * Filters this composite to only elements that match the passed selector.
5480 * @param {String/Function} selector A string CSS selector or a comparison function.
5481 * The comparison function will be called with the following arguments:<ul>
5482 * <li><code>el</code> : Ext.core.Element<div class="sub-desc">The current DOM element.</div></li>
5483 * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
5485 * @return {CompositeElement} this
5487 filter : function(selector){
5490 fn = Ext.isFunction(selector) ? selector
5492 return el.is(selector);
5495 me.each(function(el, self, i) {
5496 if (fn(el, i) !== false) {
5497 els[els.length] = me.transformElement(el);
5506 * Find the index of the passed element within the composite collection.
5507 * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
5508 * @return Number The index of the passed Ext.core.Element in the composite collection, or -1 if not found.
5510 indexOf : function(el){
5511 return Ext.Array.indexOf(this.elements, this.transformElement(el));
5515 * Replaces the specified element with the passed element.
5516 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
5518 * @param {Mixed} replacement The id of an element or the Element itself.
5519 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
5520 * @return {CompositeElement} this
5522 replaceElement : function(el, replacement, domReplace){
5523 var index = !isNaN(el) ? el : this.indexOf(el),
5526 replacement = Ext.getDom(replacement);
5528 d = this.elements[index];
5529 d.parentNode.insertBefore(replacement, d);
5532 this.elements.splice(index, 1, replacement);
5538 * Removes all elements.
5545 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
5549 * Copies all of the functions from Ext.core.Element's prototype onto CompositeElementLite's prototype.
5550 * This is called twice - once immediately below, and once again after additional Ext.core.Element
5551 * are added in Ext JS
5553 Ext.CompositeElementLite.importElementMethods = function() {
5555 ElProto = Ext.core.Element.prototype,
5556 CelProto = Ext.CompositeElementLite.prototype;
5558 for (fnName in ElProto) {
5559 if (typeof ElProto[fnName] == 'function'){
5561 CelProto[fnName] = CelProto[fnName] || function() {
5562 return this.invoke(fnName, arguments);
5564 }).call(CelProto, fnName);
5570 Ext.CompositeElementLite.importElementMethods();
5573 Ext.core.Element.selectorFunction = Ext.DomQuery.select;
5577 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
5578 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
5579 * {@link Ext.CompositeElementLite CompositeElementLite} object.
5580 * @param {String/Array} selector The CSS selector or an array of elements
5581 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
5582 * @return {CompositeElementLite/CompositeElement}
5583 * @member Ext.core.Element
5586 Ext.core.Element.select = function(selector, root){
5588 if(typeof selector == "string"){
5589 els = Ext.core.Element.selectorFunction(selector, root);
5590 }else if(selector.length !== undefined){
5595 sourceClass: "Ext.core.Element",
5596 sourceMethod: "select",
5599 msg: "Invalid selector specified: " + selector
5603 return new Ext.CompositeElementLite(els);
5606 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
5607 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
5608 * {@link Ext.CompositeElementLite CompositeElementLite} object.
5609 * @param {String/Array} selector The CSS selector or an array of elements
5610 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
5611 * @return {CompositeElementLite/CompositeElement}
5615 Ext.select = Ext.core.Element.select;
5618 * @class Ext.util.DelayedTask
5620 * The DelayedTask class provides a convenient way to "buffer" the execution of a method,
5621 * performing setTimeout where a new timeout cancels the old timeout. When called, the
5622 * task will wait the specified time period before executing. If durng that time period,
5623 * the task is called again, the original call will be cancelled. This continues so that
5624 * the function is only called a single time for each iteration.
5626 * This method is especially useful for things like detecting whether a user has finished
5627 * typing in a text field. An example would be performing validation on a keypress. You can
5628 * use this class to buffer the keypress events for a certain number of milliseconds, and
5629 * perform only if they stop for that amount of time.
5633 * var task = new Ext.util.DelayedTask(function(){
5634 * alert(Ext.getDom('myInputField').value.length);
5637 * // Wait 500ms before calling our function. If the user presses another key
5638 * // during that 500ms, it will be cancelled and we'll wait another 500ms.
5639 * Ext.get('myInputField').on('keypress', function(){
5640 * task.{@link #delay}(500);
5643 * Note that we are using a DelayedTask here to illustrate a point. The configuration
5644 * option `buffer` for {@link Ext.util.Observable#addListener addListener/on} will
5645 * also setup a delayed task for you to buffer events.
5647 * @constructor The parameters to this constructor serve as defaults and are not required.
5648 * @param {Function} fn (optional) The default function to call.
5649 * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
5650 * function is called. If not specified, <code>this</code> will refer to the browser window.
5651 * @param {Array} args (optional) The default Array of arguments.
5653 Ext.util.DelayedTask = function(fn, scope, args) {
5659 fn.apply(scope, args || []);
5663 * Cancels any pending timeout and queues a new one
5664 * @param {Number} delay The milliseconds to delay
5665 * @param {Function} newFn (optional) Overrides function passed to constructor
5666 * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
5667 * is specified, <code>this</code> will refer to the browser window.
5668 * @param {Array} newArgs (optional) Overrides args passed to constructor
5670 this.delay = function(delay, newFn, newScope, newArgs) {
5673 scope = newScope || scope;
5674 args = newArgs || args;
5675 id = setInterval(call, delay);
5679 * Cancel the last queued timeout
5681 this.cancel = function(){
5688 Ext.require('Ext.util.DelayedTask', function() {
5690 Ext.util.Event = Ext.extend(Object, (function() {
5691 function createBuffered(handler, listener, o, scope) {
5692 listener.task = new Ext.util.DelayedTask();
5694 listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
5698 function createDelayed(handler, listener, o, scope) {
5700 var task = new Ext.util.DelayedTask();
5701 if (!listener.tasks) {
5702 listener.tasks = [];
5704 listener.tasks.push(task);
5705 task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
5709 function createSingle(handler, listener, o, scope) {
5711 listener.ev.removeListener(listener.fn, scope);
5712 return handler.apply(scope, arguments);
5719 constructor: function(observable, name) {
5721 this.observable = observable;
5722 this.listeners = [];
5725 addListener: function(fn, scope, options) {
5728 scope = scope || me.observable;
5733 sourceClass: Ext.getClassName(this.observable),
5734 sourceMethod: "addListener",
5735 msg: "The specified callback function is undefined"
5740 if (!me.isListening(fn, scope)) {
5741 listener = me.createListener(fn, scope, options);
5743 // if we are currently firing this event, don't disturb the listener loop
5744 me.listeners = me.listeners.slice(0);
5746 me.listeners.push(listener);
5750 createListener: function(fn, scope, o) {
5752 scope = scope || this.observable;
5762 // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
5763 // because the event removal that the single listener does destroys the listener's DelayedTask(s)
5765 handler = createSingle(handler, listener, o, scope);
5768 handler = createDelayed(handler, listener, o, scope);
5771 handler = createBuffered(handler, listener, o, scope);
5774 listener.fireFn = handler;
5778 findListener: function(fn, scope) {
5779 var listeners = this.listeners,
5780 i = listeners.length,
5785 listener = listeners[i];
5788 if (listener.fn == fn && (s == scope || s == this.observable)) {
5797 isListening: function(fn, scope) {
5798 return this.findListener(fn, scope) !== -1;
5801 removeListener: function(fn, scope) {
5806 index = me.findListener(fn, scope);
5808 listener = me.listeners[index];
5811 me.listeners = me.listeners.slice(0);
5814 // cancel and remove a buffered handler that hasn't fired yet
5815 if (listener.task) {
5816 listener.task.cancel();
5817 delete listener.task;
5820 // cancel and remove all delayed handlers that haven't fired yet
5821 k = listener.tasks && listener.tasks.length;
5824 listener.tasks[k].cancel();
5826 delete listener.tasks;
5829 // remove this listener from the listeners array
5830 me.listeners.splice(index, 1);
5837 // Iterate to stop any buffered/delayed events
5838 clearListeners: function() {
5839 var listeners = this.listeners,
5840 i = listeners.length;
5843 this.removeListener(listeners[i].fn, listeners[i].scope);
5849 listeners = me.listeners,
5850 count = listeners.length,
5857 for (i = 0; i < count; i++) {
5858 listener = listeners[i];
5859 args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
5861 args.push(listener.o);
5863 if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
5864 return (me.firing = false);
5876 * @class Ext.EventManager
5877 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
5878 * several useful events directly.
5879 * See {@link Ext.EventObject} for more details on normalized event objects.
5882 Ext.EventManager = {
5884 // --------------------- onReady ---------------------
5887 * Check if we have bound our global onReady listener
5890 hasBoundOnReady: false,
5893 * Check if fireDocReady has been called
5896 hasFiredReady: false,
5899 * Timer for the document ready event in old IE versions
5905 * Checks if we have bound an onreadystatechange event
5908 hasOnReadyStateChange: false,
5911 * Holds references to any onReady functions
5914 readyEvent: new Ext.util.Event(),
5917 * Check the ready state for old IE versions
5919 * @return {Boolean} True if the document is ready
5921 checkReadyState: function(){
5922 var me = Ext.EventManager;
5924 if(window.attachEvent){
5925 // See here for reference: http://javascript.nwbox.com/IEContentLoaded/
5926 if (window != top) {
5930 document.documentElement.doScroll('left');
5937 if (document.readyState == 'complete') {
5941 me.readyTimeout = setTimeout(arguments.callee, 2);
5946 * Binds the appropriate browser event for checking if the DOM has loaded.
5949 bindReadyEvent: function(){
5950 var me = Ext.EventManager;
5951 if (me.hasBoundOnReady) {
5955 if (document.addEventListener) {
5956 document.addEventListener('DOMContentLoaded', me.fireDocReady, false);
5957 // fallback, load will ~always~ fire
5958 window.addEventListener('load', me.fireDocReady, false);
5960 // check if the document is ready, this will also kick off the scroll checking timer
5961 if (!me.checkReadyState()) {
5962 document.attachEvent('onreadystatechange', me.checkReadyState);
5963 me.hasOnReadyStateChange = true;
5965 // fallback, onload will ~always~ fire
5966 window.attachEvent('onload', me.fireDocReady, false);
5968 me.hasBoundOnReady = true;
5972 * We know the document is loaded, so trigger any onReady events.
5975 fireDocReady: function(){
5976 var me = Ext.EventManager;
5978 // only unbind these events once
5979 if (!me.hasFiredReady) {
5980 me.hasFiredReady = true;
5982 if (document.addEventListener) {
5983 document.removeEventListener('DOMContentLoaded', me.fireDocReady, false);
5984 window.removeEventListener('load', me.fireDocReady, false);
5986 if (me.readyTimeout !== null) {
5987 clearTimeout(me.readyTimeout);
5989 if (me.hasOnReadyStateChange) {
5990 document.detachEvent('onreadystatechange', me.checkReadyState);
5992 window.detachEvent('onload', me.fireDocReady);
5994 Ext.supports.init();
5998 me.onWindowUnload();
5999 me.readyEvent.fire();
6004 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
6005 * accessed shorthanded as Ext.onReady().
6006 * @param {Function} fn The method the event invokes.
6007 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
6008 * @param {boolean} options (optional) Options object as passed to {@link Ext.core.Element#addListener}.
6010 onDocumentReady: function(fn, scope, options){
6011 options = options || {};
6012 var me = Ext.EventManager,
6013 readyEvent = me.readyEvent;
6015 // force single to be true so our event is only ever fired once.
6016 options.single = true;
6018 // Document already loaded, let's just fire it
6020 readyEvent.addListener(fn, scope, options);
6023 options.delay = options.delay || 1;
6024 readyEvent.addListener(fn, scope, options);
6025 me.bindReadyEvent();
6030 // --------------------- event binding ---------------------
6033 * Contains a list of all document mouse downs, so we can ensure they fire even when stopEvent is called.
6036 stoppedMouseDownEvent: new Ext.util.Event(),
6039 * Options to parse for the 4th argument to addListener.
6042 propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,
6045 * Get the id of the element. If one has not been assigned, automatically assign it.
6046 * @param {Mixed} element The element to get the id for.
6047 * @return {String} id
6049 getId : function(element) {
6050 var skipGarbageCollection = false,
6053 element = Ext.getDom(element);
6055 if (element === document || element === window) {
6056 id = element === document ? Ext.documentId : Ext.windowId;
6059 id = Ext.id(element);
6061 // skip garbage collection for special elements (window, document, iframes)
6062 if (element && (element.getElementById || element.navigator)) {
6063 skipGarbageCollection = true;
6066 if (!Ext.cache[id]){
6067 Ext.core.Element.addToCache(new Ext.core.Element(element), id);
6068 if (skipGarbageCollection) {
6069 Ext.cache[id].skipGarbageCollection = true;
6076 * Convert a "config style" listener into a set of flat arguments so they can be passed to addListener
6078 * @param {Object} element The element the event is for
6079 * @param {Object} event The event configuration
6080 * @param {Object} isRemove True if a removal should be performed, otherwise an add will be done.
6082 prepareListenerConfig: function(element, config, isRemove){
6087 // loop over all the keys in the object
6088 for (key in config) {
6089 if (config.hasOwnProperty(key)) {
6090 // if the key is something else then an event option
6091 if (!propRe.test(key)) {
6092 value = config[key];
6093 // if the value is a function it must be something like click: function(){}, scope: this
6094 // which means that there might be multiple event listeners with shared options
6095 if (Ext.isFunction(value)) {
6097 args = [element, key, value, config.scope, config];
6099 // if its not a function, it must be an object like click: {fn: function(){}, scope: this}
6100 args = [element, key, value.fn, value.scope, value];
6103 if (isRemove === true) {
6104 me.removeListener.apply(this, args);
6106 me.addListener.apply(me, args);
6114 * Normalize cross browser event differences
6116 * @param {Object} eventName The event name
6117 * @param {Object} fn The function to execute
6118 * @return {Object} The new event name/function
6120 normalizeEvent: function(eventName, fn){
6121 if (/mouseenter|mouseleave/.test(eventName) && !Ext.supports.MouseEnterLeave) {
6123 fn = Ext.Function.createInterceptor(fn, this.contains, this);
6125 eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
6126 } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera){
6127 eventName = 'DOMMouseScroll';
6130 eventName: eventName,
6136 * Checks whether the event's relatedTarget is contained inside (or <b>is</b>) the element.
6138 * @param {Object} event
6140 contains: function(event){
6141 var parent = event.browserEvent.currentTarget,
6142 child = this.getRelatedTarget(event);
6144 if (parent && parent.firstChild) {
6146 if (child === parent) {
6149 child = child.parentNode;
6150 if (child && (child.nodeType != 1)) {
6159 * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
6160 * use {@link Ext.core.Element#addListener} directly on an Element in favor of calling this version.
6161 * @param {String/HTMLElement} el The html element or id to assign the event handler to.
6162 * @param {String} eventName The name of the event to listen for.
6163 * @param {Function} handler The handler function the event invokes. This function is passed
6164 * the following parameters:<ul>
6165 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
6166 * <li>t : Element<div class="sub-desc">The {@link Ext.core.Element Element} which was the target of the event.
6167 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
6168 * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
6170 * @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>.
6171 * @param {Object} options (optional) An object containing handler configuration properties.
6172 * This may contain any of the following properties:<ul>
6173 * <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>
6174 * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
6175 * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
6176 * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
6177 * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
6178 * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
6179 * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
6180 * <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>
6181 * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
6182 * by the specified number of milliseconds. If the event fires again within that time, the original
6183 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
6184 * <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>
6186 * <p>See {@link Ext.core.Element#addListener} for examples of how to use these options.</p>
6188 addListener: function(element, eventName, fn, scope, options){
6189 // Check if we've been passed a "config style" event.
6190 if (Ext.isObject(eventName)) {
6191 this.prepareListenerConfig(element, eventName);
6195 var dom = Ext.getDom(element),
6202 sourceClass: 'Ext.EventManager',
6203 sourceMethod: 'addListener',
6204 targetElement: element,
6205 eventName: eventName,
6206 msg: 'Error adding "' + eventName + '\" listener for nonexistent element "' + element + '"'
6211 sourceClass: 'Ext.EventManager',
6212 sourceMethod: 'addListener',
6213 targetElement: element,
6214 eventName: eventName,
6215 msg: 'Error adding "' + eventName + '\" listener. The handler function is undefined.'
6220 // create the wrapper function
6221 options = options || {};
6223 bind = this.normalizeEvent(eventName, fn);
6224 wrap = this.createListenerWrap(dom, eventName, bind.fn, scope, options);
6227 if (dom.attachEvent) {
6228 dom.attachEvent('on' + bind.eventName, wrap);
6230 dom.addEventListener(bind.eventName, wrap, options.capture || false);
6233 if (dom == document && eventName == 'mousedown') {
6234 this.stoppedMouseDownEvent.addListener(wrap);
6237 // add all required data into the event cache
6238 this.getEventListenerCache(dom, eventName).push({
6246 * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
6247 * you will use {@link Ext.core.Element#removeListener} directly on an Element in favor of calling this version.
6248 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
6249 * @param {String} eventName The name of the event.
6250 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
6251 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
6252 * then this must refer to the same object.
6254 removeListener : function(element, eventName, fn, scope) {
6255 // handle our listener config object syntax
6256 if (Ext.isObject(eventName)) {
6257 this.prepareListenerConfig(element, eventName, true);
6261 var dom = Ext.getDom(element),
6262 cache = this.getEventListenerCache(dom, eventName),
6263 bindName = this.normalizeEvent(eventName).eventName,
6264 i = cache.length, j,
6265 listener, wrap, tasks;
6269 listener = cache[i];
6271 if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
6272 wrap = listener.wrap;
6274 // clear buffered calls
6276 clearTimeout(wrap.task);
6280 // clear delayed calls
6281 j = wrap.tasks && wrap.tasks.length;
6284 clearTimeout(wrap.tasks[j]);
6289 if (dom.detachEvent) {
6290 dom.detachEvent('on' + bindName, wrap);
6292 dom.removeEventListener(bindName, wrap, false);
6295 if (wrap && dom == document && eventName == 'mousedown') {
6296 this.stoppedMouseDownEvent.removeListener(wrap);
6299 // remove listener from cache
6306 * Removes all event handers from an element. Typically you will use {@link Ext.core.Element#removeAllListeners}
6307 * directly on an Element in favor of calling this version.
6308 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
6310 removeAll : function(element){
6311 var dom = Ext.getDom(element),
6316 cache = this.getElementEventCache(dom);
6319 if (cache.hasOwnProperty(ev)) {
6320 this.removeListener(dom, ev);
6323 Ext.cache[dom.id].events = {};
6327 * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.core.Element#purgeAllListeners}
6328 * directly on an Element in favor of calling this version.
6329 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
6330 * @param {String} eventName (optional) The name of the event.
6332 purgeElement : function(element, eventName) {
6333 var dom = Ext.getDom(element),
6337 this.removeListener(dom, eventName);
6340 this.removeAll(dom);
6343 if(dom && dom.childNodes) {
6344 for(len = element.childNodes.length; i < len; i++) {
6345 this.purgeElement(element.childNodes[i], eventName);
6351 * Create the wrapper function for the event
6353 * @param {HTMLElement} dom The dom element
6354 * @param {String} ename The event name
6355 * @param {Function} fn The function to execute
6356 * @param {Object} scope The scope to execute callback in
6357 * @param {Object} o The options
6359 createListenerWrap : function(dom, ename, fn, scope, options) {
6360 options = !Ext.isObject(options) ? {} : options;
6362 var f = ['if(!Ext) {return;}'],
6365 if(options.buffer || options.delay || options.freezeEvent) {
6366 f.push('e = new Ext.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
6368 f.push('e = Ext.EventObject.setEvent(e);');
6371 if (options.delegate) {
6372 f.push('var t = e.getTarget("' + options.delegate + '", this);');
6373 f.push('if(!t) {return;}');
6375 f.push('var t = e.target;');
6378 if (options.target) {
6379 f.push('if(e.target !== options.target) {return;}');
6382 if(options.stopEvent) {
6383 f.push('e.stopEvent();');
6385 if(options.preventDefault) {
6386 f.push('e.preventDefault();');
6388 if(options.stopPropagation) {
6389 f.push('e.stopPropagation();');
6393 if(options.normalized === false) {
6394 f.push('e = e.browserEvent;');
6397 if(options.buffer) {
6398 f.push('(wrap.task && clearTimeout(wrap.task));');
6399 f.push('wrap.task = setTimeout(function(){');
6403 f.push('wrap.tasks = wrap.tasks || [];');
6404 f.push('wrap.tasks.push(setTimeout(function(){');
6407 // finally call the actual handler fn
6408 f.push('fn.call(scope || dom, e, t, options);');
6410 if(options.single) {
6411 f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
6415 f.push('}, ' + options.delay + '));');
6418 if(options.buffer) {
6419 f.push('}, ' + options.buffer + ');');
6422 gen = Ext.functionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join('\n'));
6424 return function wrap(e, args) {
6425 gen.call(dom, e, options, fn, scope, ename, dom, wrap, args);
6430 * Get the event cache for a particular element for a particular event
6432 * @param {HTMLElement} element The element
6433 * @param {Object} eventName The event name
6434 * @return {Array} The events for the element
6436 getEventListenerCache : function(element, eventName) {
6437 var eventCache = this.getElementEventCache(element);
6438 return eventCache[eventName] || (eventCache[eventName] = []);
6442 * Gets the event cache for the object
6444 * @param {HTMLElement} element The element
6445 * @return {Object} The event cache for the object
6447 getElementEventCache : function(element) {
6448 var elementCache = Ext.cache[this.getId(element)];
6449 return elementCache.events || (elementCache.events = {});
6452 // --------------------- utility methods ---------------------
6453 mouseLeaveRe: /(mouseout|mouseleave)/,
6454 mouseEnterRe: /(mouseover|mouseenter)/,
6457 * Stop the event (preventDefault and stopPropagation)
6458 * @param {Event} The event to stop
6460 stopEvent: function(event) {
6461 this.stopPropagation(event);
6462 this.preventDefault(event);
6466 * Cancels bubbling of the event.
6467 * @param {Event} The event to stop bubbling.
6469 stopPropagation: function(event) {
6470 event = event.browserEvent || event;
6471 if (event.stopPropagation) {
6472 event.stopPropagation();
6474 event.cancelBubble = true;
6479 * Prevents the browsers default handling of the event.
6480 * @param {Event} The event to prevent the default
6482 preventDefault: function(event) {
6483 event = event.browserEvent || event;
6484 if (event.preventDefault) {
6485 event.preventDefault();
6487 event.returnValue = false;
6488 // Some keys events require setting the keyCode to -1 to be prevented
6490 // all ctrl + X and F1 -> F12
6491 if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
6495 // see this outdated document http://support.microsoft.com/kb/934364/en-us for more info
6501 * Gets the related target from the event.
6502 * @param {Object} event The event
6503 * @return {HTMLElement} The related target.
6505 getRelatedTarget: function(event) {
6506 event = event.browserEvent || event;
6507 var target = event.relatedTarget;
6509 if (this.mouseLeaveRe.test(event.type)) {
6510 target = event.toElement;
6511 } else if (this.mouseEnterRe.test(event.type)) {
6512 target = event.fromElement;
6515 return this.resolveTextNode(target);
6519 * Gets the x coordinate from the event
6520 * @param {Object} event The event
6521 * @return {Number} The x coordinate
6523 getPageX: function(event) {
6524 return this.getXY(event)[0];
6528 * Gets the y coordinate from the event
6529 * @param {Object} event The event
6530 * @return {Number} The y coordinate
6532 getPageY: function(event) {
6533 return this.getXY(event)[1];
6537 * Gets the x & ycoordinate from the event
6538 * @param {Object} event The event
6539 * @return {Array} The x/y coordinate
6541 getPageXY: function(event) {
6542 event = event.browserEvent || event;
6543 var x = event.pageX,
6545 doc = document.documentElement,
6546 body = document.body;
6548 // pageX/pageY not available (undefined, not null), use clientX/clientY instead
6549 if (!x && x !== 0) {
6550 x = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
6551 y = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
6557 * Gets the target of the event.
6558 * @param {Object} event The event
6559 * @return {HTMLElement} target
6561 getTarget: function(event) {
6562 event = event.browserEvent || event;
6563 return this.resolveTextNode(event.target || event.srcElement);
6567 * Resolve any text nodes accounting for browser differences.
6569 * @param {HTMLElement} node The node
6570 * @return {HTMLElement} The resolved node
6572 // 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.
6573 resolveTextNode: Ext.isGecko ?
6578 // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
6579 var s = HTMLElement.prototype.toString.call(node);
6580 if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]') {
6583 return node.nodeType == 3 ? node.parentNode: node;
6585 return node && node.nodeType == 3 ? node.parentNode: node;
6588 // --------------------- custom event binding ---------------------
6590 // Keep track of the current width/height
6595 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
6596 * passes new viewport width and height to handlers.
6597 * @param {Function} fn The handler function the window resize event invokes.
6598 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
6599 * @param {boolean} options Options object as passed to {@link Ext.core.Element#addListener}
6601 onWindowResize: function(fn, scope, options){
6602 var resize = this.resizeEvent;
6604 this.resizeEvent = resize = new Ext.util.Event();
6605 this.on(window, 'resize', this.fireResize, this, {buffer: 100});
6607 resize.addListener(fn, scope, options);
6611 * Fire the resize event.
6614 fireResize: function(){
6616 w = Ext.core.Element.getViewWidth(),
6617 h = Ext.core.Element.getViewHeight();
6619 //whacky problem in IE where the resize event will sometimes fire even though the w/h are the same.
6620 if(me.curHeight != h || me.curWidth != w){
6623 me.resizeEvent.fire(w, h);
6628 * Removes the passed window resize listener.
6629 * @param {Function} fn The method the event invokes
6630 * @param {Object} scope The scope of handler
6632 removeResizeListener: function(fn, scope){
6633 if (this.resizeEvent) {
6634 this.resizeEvent.removeListener(fn, scope);
6638 onWindowUnload: function() {
6639 var unload = this.unloadEvent;
6641 this.unloadEvent = unload = new Ext.util.Event();
6642 this.addListener(window, 'unload', this.fireUnload, this);
6647 * Fires the unload event for items bound with onWindowUnload
6650 fireUnload: function() {
6651 // wrap in a try catch, could have some problems during unload
6653 this.removeUnloadListener();
6654 // Work around FF3 remembering the last scroll position when refreshing the grid and then losing grid view
6656 var gridviews = Ext.ComponentQuery.query('gridview'),
6658 ln = gridviews.length;
6659 for (; i < ln; i++) {
6660 gridviews[i].scrollToTop();
6663 // Purge all elements in the cache
6667 if (cache.hasOwnProperty(el)) {
6668 Ext.EventManager.removeAll(el);
6676 * Removes the passed window unload listener.
6677 * @param {Function} fn The method the event invokes
6678 * @param {Object} scope The scope of handler
6680 removeUnloadListener: function(){
6681 if (this.unloadEvent) {
6682 this.removeListener(window, 'unload', this.fireUnload);
6687 * note 1: IE fires ONLY the keydown event on specialkey autorepeat
6688 * note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
6689 * (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
6692 useKeyDown: Ext.isWebKit ?
6693 parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
6694 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera),
6697 * Indicates which event to use for getting key presses.
6698 * @return {String} The appropriate event name.
6700 getKeyEvent: function(){
6701 return this.useKeyDown ? 'keydown' : 'keypress';
6706 * Alias for {@link Ext.Loader#onReady Ext.Loader.onReady} with withDomReady set to true
6710 Ext.onReady = function(fn, scope, options) {
6711 Ext.Loader.onReady(fn, scope, true, options);
6715 * Alias for {@link Ext.EventManager#onDocumentReady Ext.EventManager.onDocumentReady}
6717 * @method onDocumentReady
6719 Ext.onDocumentReady = Ext.EventManager.onDocumentReady;
6722 * Alias for {@link Ext.EventManager#addListener Ext.EventManager.addListener}
6723 * @member Ext.EventManager
6726 Ext.EventManager.on = Ext.EventManager.addListener;
6729 * Alias for {@link Ext.EventManager#removeListener Ext.EventManager.removeListener}
6730 * @member Ext.EventManager
6733 Ext.EventManager.un = Ext.EventManager.removeListener;
6736 var initExtCss = function() {
6737 // find the body element
6738 var bd = document.body || document.getElementsByTagName('body')[0],
6739 baseCSSPrefix = Ext.baseCSSPrefix,
6748 html = bd.parentNode;
6750 //Let's keep this human readable!
6752 cls.push(baseCSSPrefix + 'ie');
6755 cls.push(baseCSSPrefix + 'ie6');
6758 cls.push(baseCSSPrefix + 'ie7');
6761 cls.push(baseCSSPrefix + 'ie8');
6764 cls.push(baseCSSPrefix + 'ie9');
6767 cls.push(baseCSSPrefix + 'gecko');
6770 cls.push(baseCSSPrefix + 'gecko3');
6773 cls.push(baseCSSPrefix + 'gecko4');
6776 cls.push(baseCSSPrefix + 'opera');
6779 cls.push(baseCSSPrefix + 'webkit');
6782 cls.push(baseCSSPrefix + 'safari');
6784 if (Ext.isSafari2) {
6785 cls.push(baseCSSPrefix + 'safari2');
6787 if (Ext.isSafari3) {
6788 cls.push(baseCSSPrefix + 'safari3');
6790 if (Ext.isSafari4) {
6791 cls.push(baseCSSPrefix + 'safari4');
6794 cls.push(baseCSSPrefix + 'chrome');
6797 cls.push(baseCSSPrefix + 'mac');
6800 cls.push(baseCSSPrefix + 'linux');
6802 if (!Ext.supports.CSS3BorderRadius) {
6803 cls.push(baseCSSPrefix + 'nbr');
6805 if (!Ext.supports.CSS3LinearGradient) {
6806 cls.push(baseCSSPrefix + 'nlg');
6808 if (!Ext.scopeResetCSS) {
6809 cls.push(baseCSSPrefix + 'reset');
6812 // add to the parent to allow for selectors x-strict x-border-box, also set the isBorderBox property correctly
6814 if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
6815 Ext.isBorderBox = false;
6818 Ext.isBorderBox = true;
6821 htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict'));
6822 if (!Ext.isStrict) {
6823 htmlCls.push(baseCSSPrefix + 'quirks');
6824 if (Ext.isIE && !Ext.isStrict) {
6825 Ext.isIEQuirks = true;
6828 Ext.fly(html, '_internal').addCls(htmlCls);
6831 Ext.fly(bd, '_internal').addCls(cls);
6835 Ext.onReady(initExtCss);
6839 * @class Ext.EventObject
6841 Just as {@link Ext.core.Element} wraps around a native DOM node, Ext.EventObject
6842 wraps the browser's native event-object normalizing cross-browser differences,
6843 such as which mouse button is clicked, keys pressed, mechanisms to stop
6844 event-propagation along with a method to prevent default actions from taking place.
6848 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
6850 var target = e.getTarget(); // same as t (the target HTMLElement)
6854 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.core.Element}
6855 myDiv.on( // 'on' is shorthand for addListener
6856 "click", // perform an action on click of myDiv
6857 handleClick // reference to the action handler
6860 // other methods to do the same:
6861 Ext.EventManager.on("myDiv", 'click', handleClick);
6862 Ext.EventManager.addListener("myDiv", 'click', handleClick);
6867 Ext.define('Ext.EventObjectImpl', {
6868 uses: ['Ext.util.Point'],
6870 /** Key constant @type Number */
6872 /** Key constant @type Number */
6874 /** Key constant @type Number */
6876 /** Key constant @type Number */
6878 /** Key constant @type Number */
6880 /** Key constant @type Number */
6882 /** Key constant @type Number */
6884 /** Key constant @type Number */
6886 /** Key constant @type Number */
6888 /** Key constant @type Number */
6890 /** Key constant @type Number */
6892 /** Key constant @type Number */
6894 /** Key constant @type Number */
6896 /** Key constant @type Number */
6898 /** Key constant @type Number */
6900 /** Key constant @type Number */
6902 /** Key constant @type Number */
6904 /** Key constant @type Number */
6906 /** Key constant @type Number */
6908 /** Key constant @type Number */
6910 /** Key constant @type Number */
6912 /** Key constant @type Number */
6914 /** Key constant @type Number */
6916 /** Key constant @type Number */
6918 /** Key constant @type Number */
6920 /** Key constant @type Number */
6922 /** Key constant @type Number */
6924 /** Key constant @type Number */
6926 /** Key constant @type Number */
6928 /** Key constant @type Number */
6930 /** Key constant @type Number */
6932 /** Key constant @type Number */
6934 /** Key constant @type Number */
6936 /** Key constant @type Number */
6938 /** Key constant @type Number */
6940 /** Key constant @type Number */
6942 /** Key constant @type Number */
6944 /** Key constant @type Number */
6946 /** Key constant @type Number */
6948 /** Key constant @type Number */
6950 /** Key constant @type Number */
6952 /** Key constant @type Number */
6954 /** Key constant @type Number */
6956 /** Key constant @type Number */
6958 /** Key constant @type Number */
6960 /** Key constant @type Number */
6962 /** Key constant @type Number */
6964 /** Key constant @type Number */
6966 /** Key constant @type Number */
6968 /** Key constant @type Number */
6970 /** Key constant @type Number */
6972 /** Key constant @type Number */
6974 /** Key constant @type Number */
6976 /** Key constant @type Number */
6978 /** Key constant @type Number */
6980 /** Key constant @type Number */
6982 /** Key constant @type Number */
6984 /** Key constant @type Number */
6986 /** Key constant @type Number */
6988 /** Key constant @type Number */
6990 /** Key constant @type Number */
6992 /** Key constant @type Number */
6994 /** Key constant @type Number */
6996 /** Key constant @type Number */
6998 /** Key constant @type Number */
7000 /** Key constant @type Number */
7002 /** Key constant @type Number */
7004 /** Key constant @type Number */
7006 /** Key constant @type Number */
7008 /** Key constant @type Number */
7010 /** Key constant @type Number */
7012 /** Key constant @type Number */
7014 /** Key constant @type Number */
7016 /** Key constant @type Number */
7018 /** Key constant @type Number */
7020 /** Key constant @type Number */
7022 /** Key constant @type Number */
7024 /** Key constant @type Number */
7026 /** Key constant @type Number */
7028 /** Key constant @type Number */
7030 /** Key constant @type Number */
7032 /** Key constant @type Number */
7034 /** Key constant @type Number */
7036 /** Key constant @type Number */
7038 /** Key constant @type Number */
7040 /** Key constant @type Number */
7042 /** Key constant @type Number */
7046 * Simple click regex
7049 clickRe: /(dbl)?click/,
7050 // safari keypress events for special keys return bad keycodes
7057 63276: 33, // page up
7058 63277: 34, // page down
7059 63272: 46, // delete
7063 // normalize button clicks, don't see any way to feature detect this.
7064 btnMap: Ext.isIE ? {
7074 constructor: function(event, freezeEvent){
7076 this.setEvent(event.browserEvent || event, freezeEvent);
7080 setEvent: function(event, freezeEvent){
7081 var me = this, button, options;
7083 if (event == me || (event && event.browserEvent)) { // already wrapped
7086 me.browserEvent = event;
7088 // normalize buttons
7089 button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
7090 if (me.clickRe.test(event.type) && button == -1) {
7096 shiftKey: event.shiftKey,
7097 // mac metaKey behaves like ctrlKey
7098 ctrlKey: event.ctrlKey || event.metaKey || false,
7099 altKey: event.altKey,
7100 // in getKey these will be normalized for the mac
7101 keyCode: event.keyCode,
7102 charCode: event.charCode,
7103 // cache the targets for the delayed and or buffered events
7104 target: Ext.EventManager.getTarget(event),
7105 relatedTarget: Ext.EventManager.getRelatedTarget(event),
7106 currentTarget: event.currentTarget,
7107 xy: (freezeEvent ? me.getXY() : null)
7121 Ext.apply(me, options);
7126 * Stop the event (preventDefault and stopPropagation)
7128 stopEvent: function(){
7129 this.stopPropagation();
7130 this.preventDefault();
7134 * Prevents the browsers default handling of the event.
7136 preventDefault: function(){
7137 if (this.browserEvent) {
7138 Ext.EventManager.preventDefault(this.browserEvent);
7143 * Cancels bubbling of the event.
7145 stopPropagation: function(){
7146 var browserEvent = this.browserEvent;
7149 if (browserEvent.type == 'mousedown') {
7150 Ext.EventManager.stoppedMouseDownEvent.fire(this);
7152 Ext.EventManager.stopPropagation(browserEvent);
7157 * Gets the character code for the event.
7160 getCharCode: function(){
7161 return this.charCode || this.keyCode;
7165 * Returns a normalized keyCode for the event.
7166 * @return {Number} The key code
7169 return this.normalizeKey(this.keyCode || this.charCode);
7173 * Normalize key codes across browsers
7175 * @param {Number} key The key code
7176 * @return {Number} The normalized code
7178 normalizeKey: function(key){
7179 // can't feature detect this
7180 return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
7184 * Gets the x coordinate of the event.
7186 * @deprecated 4.0 Replaced by {@link #getX}
7188 getPageX: function(){
7193 * Gets the y coordinate of the event.
7195 * @deprecated 4.0 Replaced by {@link #getY}
7197 getPageY: function(){
7202 * Gets the x coordinate of the event.
7206 return this.getXY()[0];
7210 * Gets the y coordinate of the event.
7214 return this.getXY()[1];
7218 * Gets the page coordinates of the event.
7219 * @return {Array} The xy values like [x, y]
7224 this.xy = Ext.EventManager.getPageXY(this.browserEvent);
7230 * Gets the target for the event.
7231 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7232 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
7233 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
7234 * @return {HTMLelement}
7236 getTarget : function(selector, maxDepth, returnEl){
7238 return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
7240 return returnEl ? Ext.get(this.target) : this.target;
7244 * Gets the related target.
7245 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7246 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
7247 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
7248 * @return {HTMLElement}
7250 getRelatedTarget : function(selector, maxDepth, returnEl){
7252 return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
7254 return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
7258 * Normalizes mouse wheel delta across browsers
7259 * @return {Number} The delta
7261 getWheelDelta : function(){
7262 var event = this.browserEvent,
7265 if (event.wheelDelta) { /* IE/Opera. */
7266 delta = event.wheelDelta / 120;
7267 } else if (event.detail){ /* Mozilla case. */
7268 delta = -event.detail / 3;
7274 * 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.
7275 * Example usage:<pre><code>
7276 // Handle click on any child of an element
7277 Ext.getBody().on('click', function(e){
7278 if(e.within('some-el')){
7279 alert('Clicked on a child of some-el!');
7283 // Handle click directly on an element, ignoring clicks on child nodes
7284 Ext.getBody().on('click', function(e,t){
7285 if((t.id == 'some-el') && !e.within(t, true)){
7286 alert('Clicked directly on some-el!');
7290 * @param {Mixed} el The id, DOM element or Ext.core.Element to check
7291 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7292 * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
7295 within : function(el, related, allowEl){
7297 var t = related ? this.getRelatedTarget() : this.getTarget(),
7301 result = Ext.fly(el).contains(t);
7302 if (!result && allowEl) {
7303 result = t == Ext.getDom(el);
7312 * Checks if the key pressed was a "navigation" key
7313 * @return {Boolean} True if the press is a navigation keypress
7315 isNavKeyPress : function(){
7317 k = this.normalizeKey(me.keyCode);
7319 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
7326 * Checks if the key pressed was a "special" key
7327 * @return {Boolean} True if the press is a special keypress
7329 isSpecialKey : function(){
7330 var k = this.normalizeKey(this.keyCode);
7331 return (this.type == 'keypress' && this.ctrlKey) ||
7332 this.isNavKeyPress() ||
7333 (k == this.BACKSPACE) || // Backspace
7334 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
7335 (k >= 44 && k <= 46); // Print Screen, Insert, Delete
7339 * Returns a point object that consists of the object coordinates.
7340 * @return {Ext.util.Point} point
7342 getPoint : function(){
7343 var xy = this.getXY();
7344 return Ext.create('Ext.util.Point', xy[0], xy[1]);
7348 * Returns true if the control, meta, shift or alt key was pressed during this event.
7351 hasModifier : function(){
7352 return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
7356 * Injects a DOM event using the data in this object and (optionally) a new target.
7357 * This is a low-level technique and not likely to be used by application code. The
7358 * currently supported event types are:
7359 * <p><b>HTMLEvents</b></p>
7370 * <p><b>MouseEvents</b></p>
7374 * <li>mousedown</li>
7376 * <li>mouseover</li>
7377 * <li>mousemove</li>
7380 * <p><b>UIEvents</b></p>
7388 * @param {Element/HTMLElement} target If specified, the target for the event. This
7389 * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
7390 * is used to determine the target.
7392 injectEvent: function () {
7394 dispatchers = {}; // keyed by event type (e.g., 'mousedown')
7396 // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
7398 // IE9 has createEvent, but this code causes major problems with htmleditor (it
7399 // blocks all mouse events and maybe more). TODO
7401 if (!Ext.isIE && document.createEvent) { // if (DOM compliant)
7403 createHtmlEvent: function (doc, type, bubbles, cancelable) {
7404 var event = doc.createEvent('HTMLEvents');
7406 event.initEvent(type, bubbles, cancelable);
7410 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
7411 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
7412 button, relatedTarget) {
7413 var event = doc.createEvent('MouseEvents'),
7414 view = doc.defaultView || window;
7416 if (event.initMouseEvent) {
7417 event.initMouseEvent(type, bubbles, cancelable, view, detail,
7418 clientX, clientY, clientX, clientY, ctrlKey, altKey,
7419 shiftKey, metaKey, button, relatedTarget);
7420 } else { // old Safari
7421 event = doc.createEvent('UIEvents');
7422 event.initEvent(type, bubbles, cancelable);
7424 event.detail = detail;
7425 event.screenX = clientX;
7426 event.screenY = clientY;
7427 event.clientX = clientX;
7428 event.clientY = clientY;
7429 event.ctrlKey = ctrlKey;
7430 event.altKey = altKey;
7431 event.metaKey = metaKey;
7432 event.shiftKey = shiftKey;
7433 event.button = button;
7434 event.relatedTarget = relatedTarget;
7440 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
7441 var event = doc.createEvent('UIEvents'),
7442 view = doc.defaultView || window;
7444 event.initUIEvent(type, bubbles, cancelable, view, detail);
7448 fireEvent: function (target, type, event) {
7449 target.dispatchEvent(event);
7452 fixTarget: function (target) {
7453 // Safari3 doesn't have window.dispatchEvent()
7454 if (target == window && !target.dispatchEvent) {
7461 } else if (document.createEventObject) { // else if (IE)
7462 var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
7465 createHtmlEvent: function (doc, type, bubbles, cancelable) {
7466 var event = doc.createEventObject();
7467 event.bubbles = bubbles;
7468 event.cancelable = cancelable;
7472 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
7473 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
7474 button, relatedTarget) {
7475 var event = doc.createEventObject();
7476 event.bubbles = bubbles;
7477 event.cancelable = cancelable;
7478 event.detail = detail;
7479 event.screenX = clientX;
7480 event.screenY = clientY;
7481 event.clientX = clientX;
7482 event.clientY = clientY;
7483 event.ctrlKey = ctrlKey;
7484 event.altKey = altKey;
7485 event.shiftKey = shiftKey;
7486 event.metaKey = metaKey;
7487 event.button = crazyIEButtons[button] || button;
7488 event.relatedTarget = relatedTarget; // cannot assign to/fromElement
7492 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
7493 var event = doc.createEventObject();
7494 event.bubbles = bubbles;
7495 event.cancelable = cancelable;
7499 fireEvent: function (target, type, event) {
7500 target.fireEvent('on' + type, event);
7503 fixTarget: function (target) {
7504 if (target == document) {
7505 // IE6,IE7 thinks window==document and doesn't have window.fireEvent()
7506 // IE6,IE7 cannot properly call document.fireEvent()
7507 return document.documentElement;
7519 load: [false, false],
7520 unload: [false, false],
7521 select: [true, false],
7522 change: [true, false],
7523 submit: [true, true],
7524 reset: [true, false],
7525 resize: [true, false],
7526 scroll: [true, false]
7528 function (name, value) {
7529 var bubbles = value[0], cancelable = value[1];
7530 dispatchers[name] = function (targetEl, srcEvent) {
7531 var e = API.createHtmlEvent(name, bubbles, cancelable);
7532 API.fireEvent(targetEl, name, e);
7539 function createMouseEventDispatcher (type, detail) {
7540 var cancelable = (type != 'mousemove');
7541 return function (targetEl, srcEvent) {
7542 var xy = srcEvent.getXY(),
7543 e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
7544 detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
7545 srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
7546 srcEvent.relatedTarget);
7547 API.fireEvent(targetEl, type, e);
7551 Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
7552 function (eventName) {
7553 dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
7560 focusin: [true, false],
7561 focusout: [true, false],
7562 activate: [true, true],
7563 focus: [false, false],
7564 blur: [false, false]
7566 function (name, value) {
7567 var bubbles = value[0], cancelable = value[1];
7568 dispatchers[name] = function (targetEl, srcEvent) {
7569 var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
7570 API.fireEvent(targetEl, name, e);
7576 // not even sure what ancient browsers fall into this category...
7578 dispatchers = {}; // never mind all those we just built :P
7581 fixTarget: function (t) {
7587 function cannotInject (target, srcEvent) {
7589 // TODO log something
7593 return function (target) {
7595 dispatcher = dispatchers[me.type] || cannotInject,
7596 t = target ? (target.dom || target) : me.getTarget();
7598 t = API.fixTarget(t);
7601 }() // call to produce method
7605 Ext.EventObject = new Ext.EventObjectImpl();
7611 * @class Ext.core.Element
7615 isCSS1 = doc.compatMode == "CSS1Compat",
7616 ELEMENT = Ext.core.Element,
7619 _fly = new Ext.core.Element.Flyweight();
7625 Ext.apply(ELEMENT, {
7626 isAncestor : function(p, c) {
7633 return p.contains(c);
7634 } else if (p.compareDocumentPosition) {
7635 return !!(p.compareDocumentPosition(c) & 16);
7637 while ((c = c.parentNode)) {
7638 ret = c == p || ret;
7645 getViewWidth : function(full) {
7646 return full ? ELEMENT.getDocumentWidth() : ELEMENT.getViewportWidth();
7649 getViewHeight : function(full) {
7650 return full ? ELEMENT.getDocumentHeight() : ELEMENT.getViewportHeight();
7653 getDocumentHeight: function() {
7654 return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, ELEMENT.getViewportHeight());
7657 getDocumentWidth: function() {
7658 return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, ELEMENT.getViewportWidth());
7661 getViewportHeight: function(){
7663 (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
7667 getViewportWidth : function() {
7668 return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
7669 Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
7672 getY : function(el) {
7673 return ELEMENT.getXY(el)[1];
7676 getX : function(el) {
7677 return ELEMENT.getXY(el)[0];
7680 getXY : function(el) {
7691 bd = (doc.body || doc.documentElement),
7694 el = Ext.getDom(el);
7697 hasAbsolute = fly(el).isStyle("position", "absolute");
7699 if (el.getBoundingClientRect) {
7700 b = el.getBoundingClientRect();
7701 scroll = fly(document).getScroll();
7702 ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
7711 hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");
7714 y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
7715 x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
7717 if (p != el && !pe.isStyle('overflow','visible')) {
7725 if (Ext.isSafari && hasAbsolute) {
7730 if (Ext.isGecko && !hasAbsolute) {
7732 x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
7733 y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
7737 while (p && p != bd) {
7738 if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
7750 setXY : function(el, xy) {
7751 (el = Ext.fly(el, '_setXY')).position();
7753 var pts = el.translatePoints(xy),
7754 style = el.dom.style,
7758 if (!isNaN(pts[pos])) {
7759 style[pos] = pts[pos] + "px";
7764 setX : function(el, x) {
7765 ELEMENT.setXY(el, [x, false]);
7768 setY : function(el, y) {
7769 ELEMENT.setXY(el, [false, y]);
7773 * Serializes a DOM form into a url encoded string
7774 * @param {Object} form The form
7775 * @return {String} The url encoded form
7777 serializeForm: function(form) {
7778 var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
7780 encoder = encodeURIComponent,
7786 Ext.each(fElements, function(element){
7787 name = element.name;
7788 type = element.type;
7790 if (!element.disabled && name) {
7791 if (/select-(one|multiple)/i.test(type)) {
7792 Ext.each(element.options, function(opt){
7794 hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
7795 data += String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
7798 } else if (!(/file|undefined|reset|button/i.test(type))) {
7799 if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
7800 data += encoder(name) + '=' + encoder(element.value) + '&';
7801 hasSubmit = /submit/i.test(type);
7806 return data.substr(0, data.length - 1);
7812 * @class Ext.core.Element
7815 Ext.core.Element.addMethods({
7818 * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if
7819 * the mouse was not moved back into the Element within the delay. If the mouse <i>was</i> moved
7820 * back in, the function is not called.
7821 * @param {Number} delay The delay <b>in milliseconds</b> to wait for possible mouse re-entry before calling the handler function.
7822 * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time.
7823 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to this Element.
7824 * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:</pre><code>
7825 // Hide the menu if the mouse moves out for 250ms or more
7826 this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
7829 // Remove mouseleave monitor on menu destroy
7830 this.menuEl.un(this.mouseLeaveMonitor);
7833 monitorMouseLeave: function(delay, handler, scope) {
7837 mouseleave: function(e) {
7838 timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
7840 mouseenter: function() {
7841 clearTimeout(timer);
7851 * Stops the specified event(s) from bubbling and optionally prevents the default action
7852 * @param {String/Array} eventName an event / array of events to stop from bubbling
7853 * @param {Boolean} preventDefault (optional) true to prevent the default action too
7854 * @return {Ext.core.Element} this
7856 swallowEvent : function(eventName, preventDefault) {
7859 e.stopPropagation();
7860 if (preventDefault) {
7865 if (Ext.isArray(eventName)) {
7866 Ext.each(eventName, function(e) {
7871 me.on(eventName, fn);
7876 * Create an event handler on this element such that when the event fires and is handled by this element,
7877 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
7878 * @param {String} eventName The type of event to relay
7879 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
7880 * for firing the relayed event
7882 relayEvent : function(eventName, observable) {
7883 this.on(eventName, function(e) {
7884 observable.fireEvent(eventName, e);
7889 * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
7890 * @param {Boolean} forceReclean (optional) By default the element
7891 * keeps track if it has been cleaned already so
7892 * you can call this over and over. However, if you update the element and
7893 * need to force a reclean, you can pass true.
7895 clean : function(forceReclean) {
7902 if (Ext.core.Element.data(dom, 'isCleaned') && forceReclean !== true) {
7908 if (n.nodeType == 3) {
7909 // Remove empty/whitespace text nodes
7910 if (!(/\S/.test(n.nodeValue))) {
7912 // Combine adjacent text nodes
7913 } else if (nx && nx.nodeType == 3) {
7914 n.appendData(Ext.String.trim(nx.data));
7915 dom.removeChild(nx);
7920 // Recursively clean
7927 Ext.core.Element.data(dom, 'isCleaned', true);
7932 * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object
7933 * parameter as {@link Ext.ElementLoader#load}
7934 * @return {Ext.core.Element} this
7936 load : function(options) {
7937 this.getLoader().load(options);
7942 * Gets this element's {@link Ext.ElementLoader ElementLoader}
7943 * @return {Ext.ElementLoader} The loader
7945 getLoader : function() {
7947 data = Ext.core.Element.data,
7948 loader = data(dom, 'loader');
7951 loader = Ext.create('Ext.ElementLoader', {
7954 data(dom, 'loader', loader);
7960 * Update the innerHTML of this element, optionally searching for and processing scripts
7961 * @param {String} html The new HTML
7962 * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
7963 * @param {Function} callback (optional) For async script loading you can be notified when the update completes
7964 * @return {Ext.core.Element} this
7966 update : function(html, loadScripts, callback) {
7978 if (loadScripts !== true) {
7979 dom.innerHTML = html;
7980 Ext.callback(callback, me);
7985 html += '<span id="' + id + '"></span>';
7987 interval = setInterval(function(){
7988 if (!document.getElementById(id)) {
7991 clearInterval(interval);
7993 hd = DOC.getElementsByTagName("head")[0],
7994 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
7995 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
7996 typeRe = /\stype=([\'\"])(.*?)\1/i,
8004 while ((match = re.exec(html))) {
8006 srcMatch = attrs ? attrs.match(srcRe) : false;
8007 if (srcMatch && srcMatch[2]) {
8008 s = DOC.createElement("script");
8009 s.src = srcMatch[2];
8010 typeMatch = attrs.match(typeRe);
8011 if (typeMatch && typeMatch[2]) {
8012 s.type = typeMatch[2];
8015 } else if (match[2] && match[2].length > 0) {
8016 if (window.execScript) {
8017 window.execScript(match[2]);
8019 window.eval(match[2]);
8024 el = DOC.getElementById(id);
8028 Ext.callback(callback, me);
8030 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, '');
8034 // inherit docs, overridden so we can add removeAnchor
8035 removeAllListeners : function() {
8036 this.removeAnchor();
8037 Ext.EventManager.removeAll(this.dom);
8042 * Creates a proxy element of this element
8043 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8044 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8045 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8046 * @return {Ext.core.Element} The new proxy element
8048 createProxy : function(config, renderTo, matchBox) {
8049 config = (typeof config == 'object') ? config : {tag : "div", cls: config};
8052 proxy = renderTo ? Ext.core.DomHelper.append(renderTo, config, true) :
8053 Ext.core.DomHelper.insertBefore(me.dom, config, true);
8055 proxy.setVisibilityMode(Ext.core.Element.DISPLAY);
8057 if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
8058 proxy.setBox(me.getBox());
8063 Ext.core.Element.prototype.clearListeners = Ext.core.Element.prototype.removeAllListeners;
8066 * @class Ext.core.Element
8068 Ext.core.Element.addMethods({
8070 * Gets the x,y coordinates specified by the anchor position on the element.
8071 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo}
8072 * for details on supported anchor positions.
8073 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
8074 * of page coordinates
8075 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8076 * {width: (target width), height: (target height)} (defaults to the element's current size)
8077 * @return {Array} [x, y] An array containing the element's x and y coordinates
8079 getAnchorXY : function(anchor, local, s){
8080 //Passing a different size is useful for pre-calculating anchors,
8081 //especially for anchored animations that change the el size.
8082 anchor = (anchor || "tl").toLowerCase();
8086 vp = me.dom == document.body || me.dom == document,
8087 w = s.width || vp ? Ext.core.Element.getViewWidth() : me.getWidth(),
8088 h = s.height || vp ? Ext.core.Element.getViewHeight() : me.getHeight(),
8092 scroll = me.getScroll(),
8093 extraX = vp ? scroll.left : !local ? o[0] : 0,
8094 extraY = vp ? scroll.top : !local ? o[1] : 0,
8096 c : [r(w * 0.5), r(h * 0.5)],
8097 t : [r(w * 0.5), 0],
8098 l : [0, r(h * 0.5)],
8099 r : [w, r(h * 0.5)],
8100 b : [r(w * 0.5), h],
8108 return [xy[0] + extraX, xy[1] + extraY];
8112 * Anchors an element to another element and realigns it when the window is resized.
8113 * @param {Mixed} element The element to align to.
8114 * @param {String} position The position to align to.
8115 * @param {Array} offsets (optional) Offset the positioning by [x, y]
8116 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8117 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8118 * is a number, it is used as the buffer delay (defaults to 50ms).
8119 * @param {Function} callback The function to call after the animation finishes
8120 * @return {Ext.core.Element} this
8122 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8125 scroll = !Ext.isEmpty(monitorScroll),
8126 action = function(){
8127 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
8128 Ext.callback(callback, Ext.fly(dom));
8130 anchor = this.getAnchor();
8132 // previous listener anchor, remove it
8133 this.removeAnchor();
8139 Ext.EventManager.onWindowResize(action, null);
8142 Ext.EventManager.on(window, 'scroll', action, null,
8143 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
8145 action.call(me); // align immediately
8150 * Remove any anchor to this element. See {@link #anchorTo}.
8151 * @return {Ext.core.Element} this
8153 removeAnchor : function(){
8155 anchor = this.getAnchor();
8157 if(anchor && anchor.fn){
8158 Ext.EventManager.removeResizeListener(anchor.fn);
8160 Ext.EventManager.un(window, 'scroll', anchor.fn);
8168 getAnchor : function(){
8169 var data = Ext.core.Element.data,
8174 var anchor = data(dom, '_anchor');
8177 anchor = data(dom, '_anchor', {});
8182 getAlignVector: function(el, spec, offset) {
8184 side = {t:"top", l:"left", r:"right", b: "bottom"},
8185 thisRegion = me.getRegion(),
8192 sourceClass: 'Ext.core.Element',
8193 sourceMethod: 'getAlignVector',
8194 msg: 'Attempted to align an element that doesn\'t exist'
8199 elRegion = el.getRegion();
8203 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8204 * supported position values.
8205 * @param {Mixed} element The element to align to.
8206 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
8207 * @param {Array} offsets (optional) Offset the positioning by [x, y]
8208 * @return {Array} [x, y]
8210 getAlignToXY : function(el, p, o){
8216 sourceClass: 'Ext.core.Element',
8217 sourceMethod: 'getAlignToXY',
8218 msg: 'Attempted to align an element that doesn\'t exist'
8224 p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
8232 //constrain the aligned el to viewport if necessary
8236 dw = Ext.core.Element.getViewWidth() -10, // 10px of margin for ie
8237 dh = Ext.core.Element.getViewHeight()-10, // 10px of margin for ie
8245 docElement = doc.documentElement,
8247 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
8248 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
8249 c = false, //constrain to viewport
8252 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8257 sourceClass: 'Ext.core.Element',
8258 sourceMethod: 'getAlignToXY',
8262 msg: 'Attemmpted to align an element with an invalid position: "' + p + '"'
8271 //Subtract the aligned el's internal xy from the target's offset xy
8272 //plus custom offset to get the aligned el's new offset xy
8273 a1 = me.getAnchorXY(p1, true);
8274 a2 = el.getAnchorXY(p2, false);
8276 x = a2[0] - a1[0] + o[0];
8277 y = a2[1] - a1[1] + o[1];
8283 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8284 //perpendicular to the vp border, allow the aligned el to slide on that border,
8285 //otherwise swap the aligned el to the opposite border of the target.
8287 p1x = p1.charAt(p1.length-1);
8289 p2x = p2.charAt(p2.length-1);
8290 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8291 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8294 if (x + w > dw + scrollX) {
8295 x = swapX ? r.left-w : dw+scrollX-w;
8298 x = swapX ? r.right : scrollX;
8300 if (y + h > dh + scrollY) {
8301 y = swapY ? r.top-h : dh+scrollY-h;
8304 y = swapY ? r.bottom : scrollY;
8311 * Aligns this element with another element relative to the specified anchor points. If the other element is the
8312 * document it aligns it to the viewport.
8313 * The position parameter is optional, and can be specified in any one of the following formats:
8315 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8316 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8317 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
8318 * deprecated in favor of the newer two anchor syntax below</i>.</li>
8319 * <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
8320 * element's anchor point, and the second value is used as the target's anchor point.</li>
8322 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
8323 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8324 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
8325 * that specified in order to enforce the viewport constraints.
8326 * Following are all of the supported anchor positions:
8329 ----- -----------------------------
8330 tl The top left corner (default)
8331 t The center of the top edge
8332 tr The top right corner
8333 l The center of the left edge
8334 c In the center of the element
8335 r The center of the right edge
8336 bl The bottom left corner
8337 b The center of the bottom edge
8338 br The bottom right corner
8342 // align el to other-el using the default positioning ("tl-bl", non-constrained)
8343 el.alignTo("other-el");
8345 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8346 el.alignTo("other-el", "tr?");
8348 // align the bottom right corner of el with the center left edge of other-el
8349 el.alignTo("other-el", "br-l?");
8351 // align the center of el with the bottom left corner of other-el and
8352 // adjust the x position by -6 pixels (and the y position by 0)
8353 el.alignTo("other-el", "c-bl", [-6, 0]);
8355 * @param {Mixed} element The element to align to.
8356 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
8357 * @param {Array} offsets (optional) Offset the positioning by [x, y]
8358 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8359 * @return {Ext.core.Element} this
8361 alignTo : function(element, position, offsets, animate){
8363 return me.setXY(me.getAlignToXY(element, position, offsets),
8364 me.anim && !!animate ? me.anim(animate) : false);
8367 // private ==> used outside of core
8368 adjustForConstraints : function(xy, parent) {
8369 var vector = this.getConstrainVector(parent, xy);
8378 * <p>Returns the <code>[X, Y]</code> vector by which this element must be translated to make a best attempt
8379 * to constrain within the passed constraint. Returns <code>false</code> is this element does not need to be moved.</p>
8380 * <p>Priority is given to constraining the top and left within the constraint.</p>
8381 * <p>The constraint may either be an existing element into which this element is to be constrained, or
8382 * an {@link Ext.util.Region Region} into which this element is to be constrained.</p>
8383 * @param constrainTo {Mixed} The Element or {@link Ext.util.Region Region} into which this element is to be constrained.
8384 * @param proposedPosition {Array} A proposed <code>[X, Y]</code> position to test for validity and to produce a vector for instead
8385 * of using this Element's current position;
8386 * @returns {Array} <b>If</b> this element <i>needs</i> to be translated, an <code>[X, Y]</code>
8387 * vector by which this element must be translated. Otherwise, <code>false</code>.
8389 getConstrainVector: function(constrainTo, proposedPosition) {
8390 if (!(constrainTo instanceof Ext.util.Region)) {
8391 constrainTo = Ext.get(constrainTo).getViewRegion();
8393 var thisRegion = this.getRegion(),
8395 shadowSize = this.shadow && this.shadow.offset,
8398 // Shift this region to occupy the proposed position
8399 if (proposedPosition) {
8400 thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
8403 // Reduce the constrain region to allow for shadow
8404 // TODO: Rewrite the Shadow class. When that's done, get the extra for each side from the Shadow.
8406 constrainTo.adjust(0, -shadowSize, -shadowSize, shadowSize);
8409 // Constrain the X coordinate by however much this Element overflows
8410 if (thisRegion.right > constrainTo.right) {
8412 vector[0] = (constrainTo.right - thisRegion.right); // overflowed the right
8414 if (thisRegion.left + vector[0] < constrainTo.left) {
8416 vector[0] = (constrainTo.left - thisRegion.left); // overflowed the left
8419 // Constrain the Y coordinate by however much this Element overflows
8420 if (thisRegion.bottom > constrainTo.bottom) {
8422 vector[1] = (constrainTo.bottom - thisRegion.bottom); // overflowed the bottom
8424 if (thisRegion.top + vector[1] < constrainTo.top) {
8426 vector[1] = (constrainTo.top - thisRegion.top); // overflowed the top
8428 return overflowed ? vector : false;
8432 * Calculates the x, y to center this element on the screen
8433 * @return {Array} The x, y values [x, y]
8435 getCenterXY : function(){
8436 return this.getAlignToXY(document, 'c-c');
8440 * Centers the Element in either the viewport, or another Element.
8441 * @param {Mixed} centerIn (optional) The element in which to center the element.
8443 center : function(centerIn){
8444 return this.alignTo(centerIn || document, 'c-c');
8449 * @class Ext.core.Element
8453 var ELEMENT = Ext.core.Element,
8458 POSITION = "position",
8460 RELATIVE = "relative",
8464 Ext.override(Ext.core.Element, {
8466 * 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).
8467 * @return {Number} The X position of the element
8470 return ELEMENT.getX(this.dom);
8474 * 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).
8475 * @return {Number} The Y position of the element
8478 return ELEMENT.getY(this.dom);
8482 * 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).
8483 * @return {Array} The XY position of the element
8486 return ELEMENT.getXY(this.dom);
8490 * 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.
8491 * @param {Mixed} element The element to get the offsets from.
8492 * @return {Array} The XY page offsets (e.g. [100, -200])
8494 getOffsetsTo : function(el){
8495 var o = this.getXY(),
8496 e = Ext.fly(el, '_internal').getXY();
8497 return [o[0]-e[0],o[1]-e[1]];
8501 * 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).
8502 * @param {Number} The X position of the element
8503 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8504 * @return {Ext.core.Element} this
8506 setX : function(x, animate){
8507 return this.setXY([x, this.getY()], animate);
8511 * 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).
8512 * @param {Number} The Y position of the element
8513 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8514 * @return {Ext.core.Element} this
8516 setY : function(y, animate){
8517 return this.setXY([this.getX(), y], animate);
8521 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8522 * @param {String} left The left CSS property value
8523 * @return {Ext.core.Element} this
8525 setLeft : function(left){
8526 this.setStyle(LEFT, this.addUnits(left));
8531 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8532 * @param {String} top The top CSS property value
8533 * @return {Ext.core.Element} this
8535 setTop : function(top){
8536 this.setStyle(TOP, this.addUnits(top));
8541 * Sets the element's CSS right style.
8542 * @param {String} right The right CSS property value
8543 * @return {Ext.core.Element} this
8545 setRight : function(right){
8546 this.setStyle(RIGHT, this.addUnits(right));
8551 * Sets the element's CSS bottom style.
8552 * @param {String} bottom The bottom CSS property value
8553 * @return {Ext.core.Element} this
8555 setBottom : function(bottom){
8556 this.setStyle(BOTTOM, this.addUnits(bottom));
8561 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8562 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8563 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8564 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8565 * @return {Ext.core.Element} this
8567 setXY: function(pos, animate) {
8569 if (!animate || !me.anim) {
8570 ELEMENT.setXY(me.dom, pos);
8573 if (!Ext.isObject(animate)) {
8576 me.animate(Ext.applyIf({ to: { x: pos[0], y: pos[1] } }, animate));
8582 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8583 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8584 * @param {Number} x X value for new position (coordinates are page-based)
8585 * @param {Number} y Y value for new position (coordinates are page-based)
8586 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8587 * @return {Ext.core.Element} this
8589 setLocation : function(x, y, animate){
8590 return this.setXY([x, y], animate);
8594 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8595 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8596 * @param {Number} x X value for new position (coordinates are page-based)
8597 * @param {Number} y Y value for new position (coordinates are page-based)
8598 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8599 * @return {Ext.core.Element} this
8601 moveTo : function(x, y, animate){
8602 return this.setXY([x, y], animate);
8606 * Gets the left X coordinate
8607 * @param {Boolean} local True to get the local css position instead of page coordinate
8610 getLeft : function(local){
8611 return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
8615 * Gets the right X coordinate of the element (element X position + element width)
8616 * @param {Boolean} local True to get the local css position instead of page coordinate
8619 getRight : function(local){
8621 return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
8625 * Gets the top Y coordinate
8626 * @param {Boolean} local True to get the local css position instead of page coordinate
8629 getTop : function(local) {
8630 return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
8634 * Gets the bottom Y coordinate of the element (element Y position + element height)
8635 * @param {Boolean} local True to get the local css position instead of page coordinate
8638 getBottom : function(local){
8640 return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
8644 * Initializes positioning on this element. If a desired position is not passed, it will make the
8645 * the element positioned relative IF it is not already positioned.
8646 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8647 * @param {Number} zIndex (optional) The zIndex to apply
8648 * @param {Number} x (optional) Set the page X position
8649 * @param {Number} y (optional) Set the page Y position
8651 position : function(pos, zIndex, x, y) {
8654 if (!pos && me.isStyle(POSITION, STATIC)){
8655 me.setStyle(POSITION, RELATIVE);
8657 me.setStyle(POSITION, pos);
8660 me.setStyle(ZINDEX, zIndex);
8663 me.setXY([x || false, y || false]);
8668 * Clear positioning back to the default when the document was loaded
8669 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8670 * @return {Ext.core.Element} this
8672 clearPositioning : function(value){
8673 value = value || '';
8686 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8687 * snapshot before performing an update and then restoring the element.
8690 getPositioning : function(){
8691 var l = this.getStyle(LEFT);
8692 var t = this.getStyle(TOP);
8694 "position" : this.getStyle(POSITION),
8696 "right" : l ? "" : this.getStyle(RIGHT),
8698 "bottom" : t ? "" : this.getStyle(BOTTOM),
8699 "z-index" : this.getStyle(ZINDEX)
8704 * Set positioning with an object returned by getPositioning().
8705 * @param {Object} posCfg
8706 * @return {Ext.core.Element} this
8708 setPositioning : function(pc){
8710 style = me.dom.style;
8714 if(pc.right == AUTO){
8717 if(pc.bottom == AUTO){
8725 * Translates the passed page coordinates into left/top css values for this element
8726 * @param {Number/Array} x The page x or an array containing [x, y]
8727 * @param {Number} y (optional) The page y, required if x is not an array
8728 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
8730 translatePoints: function(x, y) {
8731 if (Ext.isArray(x)) {
8736 relative = me.isStyle(POSITION, RELATIVE),
8738 left = parseInt(me.getStyle(LEFT), 10),
8739 top = parseInt(me.getStyle(TOP), 10);
8741 if (!Ext.isNumber(left)) {
8742 left = relative ? 0 : me.dom.offsetLeft;
8744 if (!Ext.isNumber(top)) {
8745 top = relative ? 0 : me.dom.offsetTop;
8747 left = (Ext.isNumber(x)) ? x - o[0] + left : undefined;
8748 top = (Ext.isNumber(y)) ? y - o[1] + top : undefined;
8756 * 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.
8757 * @param {Object} box The box to fill {x, y, width, height}
8758 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8759 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8760 * @return {Ext.core.Element} this
8762 setBox: function(box, adjust, animate) {
8766 if ((adjust && !me.autoBoxAdjust) && !me.isBorderBox()) {
8767 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
8768 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
8770 me.setBounds(box.x, box.y, w, h, animate);
8775 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
8776 * set another Element's size/location to match this element.
8777 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8778 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8779 * @return {Object} box An object in the format<pre><code>
8781 x: <Element's X position>,
8782 y: <Element's Y position>,
8783 width: <Element's width>,
8784 height: <Element's height>,
8785 bottom: <Element's lower bound>,
8786 right: <Element's rightmost bound>
8789 * The returned object may also be addressed as an Array where index 0 contains the X position
8790 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
8792 getBox: function(contentBox, local) {
8797 getBorderWidth = me.getBorderWidth,
8798 getPadding = me.getPadding,
8799 l, r, t, b, w, h, bx;
8803 left = parseInt(me.getStyle("left"), 10) || 0;
8804 top = parseInt(me.getStyle("top"), 10) || 0;
8819 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
8820 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
8821 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
8822 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
8832 bx.right = bx.x + bx.width;
8833 bx.bottom = bx.y + bx.height;
8838 * Move this element relative to its current position.
8839 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
8840 * @param {Number} distance How far to move the element in pixels
8841 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8842 * @return {Ext.core.Element} this
8844 move: function(direction, distance, animate) {
8849 left = [x - distance, y],
8850 right = [x + distance, y],
8851 top = [x, y - distance],
8852 bottom = [x, y + distance],
8866 direction = direction.toLowerCase();
8867 me.moveTo(hash[direction][0], hash[direction][1], animate);
8871 * Quick set left and top adding default units
8872 * @param {String} left The left CSS property value
8873 * @param {String} top The top CSS property value
8874 * @return {Ext.core.Element} this
8876 setLeftTop: function(left, top) {
8878 style = me.dom.style;
8879 style.left = me.addUnits(left);
8880 style.top = me.addUnits(top);
8885 * Returns the region of this element.
8886 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8887 * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
8889 getRegion: function() {
8890 return this.getPageBox(true);
8894 * Returns the <b>content</b> region of this element. That is the region within the borders and padding.
8895 * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
8897 getViewRegion: function() {
8899 isBody = me.dom === document.body,
8900 scroll, pos, top, left, width, height;
8902 // For the body we want to do some special logic
8904 scroll = me.getScroll();
8907 width = Ext.core.Element.getViewportWidth();
8908 height = Ext.core.Element.getViewportHeight();
8912 left = pos[0] + me.getBorderWidth('l') + me.getPadding('l');
8913 top = pos[1] + me.getBorderWidth('t') + me.getPadding('t');
8914 width = me.getWidth(true);
8915 height = me.getHeight(true);
8918 return Ext.create('Ext.util.Region', top, left + width, top + height, left);
8922 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
8923 * set another Element's size/location to match this element.
8924 * @param {Boolean} asRegion(optional) If true an Ext.util.Region will be returned
8925 * @return {Object} box An object in the format<pre><code>
8927 x: <Element's X position>,
8928 y: <Element's Y position>,
8929 width: <Element's width>,
8930 height: <Element's height>,
8931 bottom: <Element's lower bound>,
8932 right: <Element's rightmost bound>
8935 * The returned object may also be addressed as an Array where index 0 contains the X position
8936 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
8938 getPageBox : function(getRegion) {
8941 isDoc = el === document.body,
8942 w = isDoc ? Ext.core.Element.getViewWidth() : el.offsetWidth,
8943 h = isDoc ? Ext.core.Element.getViewHeight() : el.offsetHeight,
8951 return Ext.create('Ext.util.Region', t, r, b, l);
8966 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8967 * @param {Number} x X value for new position (coordinates are page-based)
8968 * @param {Number} y Y value for new position (coordinates are page-based)
8969 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
8970 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
8971 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
8973 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
8974 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
8975 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
8977 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8978 * @return {Ext.core.Element} this
8980 setBounds: function(x, y, width, height, animate) {
8982 if (!animate || !me.anim) {
8983 me.setSize(width, height);
8984 me.setLocation(x, y);
8986 if (!Ext.isObject(animate)) {
8989 me.animate(Ext.applyIf({
8993 width: me.adjustWidth(width),
8994 height: me.adjustHeight(height)
9002 * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
9003 * @param {Ext.util.Region} region The region to fill
9004 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9005 * @return {Ext.core.Element} this
9007 setRegion: function(region, animate) {
9008 return this.setBounds(region.left, region.top, region.right - region.left, region.bottom - region.top, animate);
9014 * @class Ext.core.Element
9016 Ext.override(Ext.core.Element, {
9018 * Returns true if this element is scrollable.
9021 isScrollable : function(){
9023 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9027 * Returns the current scroll position of the element.
9028 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9030 getScroll : function() {
9034 docElement = doc.documentElement,
9039 if (d == doc || d == body) {
9040 if (Ext.isIE && Ext.isStrict) {
9041 l = docElement.scrollLeft;
9042 t = docElement.scrollTop;
9044 l = window.pageXOffset;
9045 t = window.pageYOffset;
9048 left: l || (body ? body.scrollLeft : 0),
9049 top : t || (body ? body.scrollTop : 0)
9062 * 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().
9063 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9064 * @param {Number} value The new scroll value
9065 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9066 * @return {Element} this
9068 scrollTo : function(side, value, animate) {
9069 //check if we're scrolling top or left
9070 var top = /top/i.test(side),
9075 if (!animate || !me.anim) {
9076 // just setting the value, so grab the direction
9077 prop = 'scroll' + (top ? 'Top' : 'Left');
9081 if (!Ext.isObject(animate)) {
9084 obj['scroll' + (top ? 'Top' : 'Left')] = value;
9085 me.animate(Ext.applyIf({
9093 * Scrolls this element into view within the passed container.
9094 * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a
9095 * string (id), dom node, or Ext.core.Element.
9096 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
9097 * @return {Ext.core.Element} this
9099 scrollIntoView : function(container, hscroll) {
9100 container = Ext.getDom(container) || Ext.getBody().dom;
9102 offsets = this.getOffsetsTo(container),
9104 left = offsets[0] + container.scrollLeft,
9105 top = offsets[1] + container.scrollTop,
9106 bottom = top + el.offsetHeight,
9107 right = left + el.offsetWidth,
9109 ctClientHeight = container.clientHeight,
9110 ctScrollTop = parseInt(container.scrollTop, 10),
9111 ctScrollLeft = parseInt(container.scrollLeft, 10),
9112 ctBottom = ctScrollTop + ctClientHeight,
9113 ctRight = ctScrollLeft + container.clientWidth;
9115 if (el.offsetHeight > ctClientHeight || top < ctScrollTop) {
9116 container.scrollTop = top;
9117 } else if (bottom > ctBottom) {
9118 container.scrollTop = bottom - ctClientHeight;
9120 // corrects IE, other browsers will ignore
9121 container.scrollTop = container.scrollTop;
9123 if (hscroll !== false) {
9124 if (el.offsetWidth > container.clientWidth || left < ctScrollLeft) {
9125 container.scrollLeft = left;
9127 else if (right > ctRight) {
9128 container.scrollLeft = right - container.clientWidth;
9130 container.scrollLeft = container.scrollLeft;
9136 scrollChildIntoView : function(child, hscroll) {
9137 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
9141 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9142 * within this element's scrollable range.
9143 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
9144 * @param {Number} distance How far to scroll the element in pixels
9145 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9146 * @return {Boolean} Returns true if a scroll was triggered or false if the element
9147 * was scrolled as far as it could go.
9149 scroll : function(direction, distance, animate) {
9150 if (!this.isScrollable()) {
9154 l = el.scrollLeft, t = el.scrollTop,
9155 w = el.scrollWidth, h = el.scrollHeight,
9156 cw = el.clientWidth, ch = el.clientHeight,
9157 scrolled = false, v,
9159 l: Math.min(l + distance, w-cw),
9160 r: v = Math.max(l - distance, 0),
9161 t: Math.max(t - distance, 0),
9162 b: Math.min(t + distance, h-ch)
9167 direction = direction.substr(0, 1);
9168 if ((v = hash[direction]) > -1) {
9170 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.anim(animate));
9176 * @class Ext.core.Element
9178 Ext.core.Element.addMethods(
9180 var VISIBILITY = "visibility",
9181 DISPLAY = "display",
9184 XMASKED = Ext.baseCSSPrefix + "masked",
9185 XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
9186 data = Ext.core.Element.data;
9190 * Checks whether the element is currently visible using both visibility and display properties.
9191 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
9192 * @return {Boolean} True if the element is currently visible, else false
9194 isVisible : function(deep) {
9195 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
9196 p = this.dom.parentNode;
9198 if (deep !== true || !vis) {
9202 while (p && !(/^body/i.test(p.tagName))) {
9203 if (!Ext.fly(p, '_isVisible').isVisible()) {
9212 * Returns true if display is not "none"
9215 isDisplayed : function() {
9216 return !this.isStyle(DISPLAY, NONE);
9220 * Convenience method for setVisibilityMode(Element.DISPLAY)
9221 * @param {String} display (optional) What to set display to when visible
9222 * @return {Ext.core.Element} this
9224 enableDisplayMode : function(display) {
9225 this.setVisibilityMode(Ext.core.Element.DISPLAY);
9227 if (!Ext.isEmpty(display)) {
9228 data(this.dom, 'originalDisplay', display);
9235 * Puts a mask over this element to disable user interaction. Requires core.css.
9236 * This method can only be applied to elements which accept child nodes.
9237 * @param {String} msg (optional) A message to display in the mask
9238 * @param {String} msgCls (optional) A css class to apply to the msg element
9239 * @return {Element} The mask element
9241 mask : function(msg, msgCls) {
9244 setExpression = dom.style.setExpression,
9245 dh = Ext.core.DomHelper,
9246 EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
9250 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
9251 me.addCls(XMASKEDRELATIVE);
9253 el = data(dom, 'maskMsg');
9257 el = data(dom, 'mask');
9262 mask = dh.append(dom, {cls : Ext.baseCSSPrefix + "mask"}, true);
9263 data(dom, 'mask', mask);
9266 mask.setDisplayed(true);
9268 if (typeof msg == 'string') {
9269 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
9270 data(dom, 'maskMsg', mm);
9271 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
9272 mm.dom.firstChild.innerHTML = msg;
9273 mm.setDisplayed(true);
9276 // NOTE: CSS expressions are resource intensive and to be used only as a last resort
9277 // These expressions are removed as soon as they are no longer necessary - in the unmask method.
9278 // In normal use cases an element will be masked for a limited period of time.
9279 // Fix for https://sencha.jira.com/browse/EXTJSIV-19.
9280 // IE6 strict mode and IE6-9 quirks mode takes off left+right padding when calculating width!
9281 if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
9282 mask.dom.style.setExpression('width', 'this.parentNode.offsetWidth + "px"');
9285 // Some versions and modes of IE subtract top+bottom padding when calculating height.
9286 // Different versions from those which make the same error for width!
9287 if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
9288 mask.dom.style.setExpression('height', 'this.parentNode.offsetHeight + "px"');
9290 // ie will not expand full height automatically
9291 else if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
9292 mask.setSize(undefined, me.getHeight());
9298 * Removes a previously applied mask.
9300 unmask : function() {
9303 mask = data(dom, 'mask'),
9304 maskMsg = data(dom, 'maskMsg');
9307 // Remove resource-intensive CSS expressions as soon as they are not required.
9308 if (mask.dom.style.clearExpression) {
9309 mask.dom.style.clearExpression('width');
9310 mask.dom.style.clearExpression('height');
9314 data(dom, 'maskMsg', undefined);
9318 data(dom, 'mask', undefined);
9319 me.removeCls([XMASKED, XMASKEDRELATIVE]);
9323 * Returns true if this element is masked. Also re-centers any displayed message within the mask.
9326 isMasked : function() {
9328 mask = data(me.dom, 'mask'),
9329 maskMsg = data(me.dom, 'maskMsg');
9331 if (mask && mask.isVisible()) {
9341 * Creates an iframe shim for this element to keep selects and other windowed objects from
9343 * @return {Ext.core.Element} The new shim element
9345 createShim : function() {
9346 var el = document.createElement('iframe'),
9349 el.frameBorder = '0';
9350 el.className = Ext.baseCSSPrefix + 'shim';
9351 el.src = Ext.SSL_SECURE_URL;
9352 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
9353 shim.autoBoxAdjust = false;
9360 * @class Ext.core.Element
9362 Ext.core.Element.addMethods({
9364 * Convenience method for constructing a KeyMap
9365 * @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:
9366 * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
9367 * @param {Function} fn The function to call
9368 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
9369 * @return {Ext.util.KeyMap} The KeyMap created
9371 addKeyListener : function(key, fn, scope){
9373 if(typeof key != 'object' || Ext.isArray(key)){
9389 return Ext.create('Ext.util.KeyMap', this, config);
9393 * Creates a KeyMap for this element
9394 * @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details
9395 * @return {Ext.util.KeyMap} The KeyMap created
9397 addKeyMap : function(config){
9398 return Ext.create('Ext.util.KeyMap', this, config);
9402 //Import the newly-added Ext.core.Element functions into CompositeElementLite. We call this here because
9403 //Element.keys.js is the last extra Ext.core.Element include in the ext-all.js build
9404 Ext.CompositeElementLite.importElementMethods();
9407 * @class Ext.CompositeElementLite
9409 Ext.apply(Ext.CompositeElementLite.prototype, {
9410 addElements : function(els, root){
9414 if(typeof els == "string"){
9415 els = Ext.core.Element.selectorFunction(els, root);
9417 var yels = this.elements;
9418 Ext.each(els, function(e) {
9419 yels.push(Ext.get(e));
9425 * Returns the first Element
9426 * @return {Ext.core.Element}
9429 return this.item(0);
9433 * Returns the last Element
9434 * @return {Ext.core.Element}
9437 return this.item(this.getCount()-1);
9441 * Returns true if this composite contains the passed element
9442 * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
9445 contains : function(el){
9446 return this.indexOf(el) != -1;
9450 * Removes the specified element(s).
9451 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9452 * or an array of any of those.
9453 * @param {Boolean} removeDom (optional) True to also remove the element from the document
9454 * @return {CompositeElement} this
9456 removeElement : function(keys, removeDom){
9458 els = this.elements,
9460 Ext.each(keys, function(val){
9461 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
9477 * @class Ext.CompositeElement
9478 * @extends Ext.CompositeElementLite
9479 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
9480 * members, or to perform collective actions upon the whole set.</p>
9481 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
9482 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
9483 * <p>All methods return <i>this</i> and can be chained.</p>
9486 var els = Ext.select("#some-el div.some-class", true);
9487 // or select directly from an existing element
9488 var el = Ext.get('some-el');
9489 el.select('div.some-class', true);
9491 els.setWidth(100); // all elements become 100 width
9492 els.hide(true); // all elements fade out and hide
9494 els.setWidth(100).hide(true);
9497 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
9499 constructor : function(els, root){
9501 this.add(els, root);
9505 getElement : function(el){
9506 // In this case just return it, since we already have a reference to it
9511 transformElement : function(el){
9516 * Adds elements to this composite.
9517 * @param {String/Array} els A string CSS selector, an array of elements or an element
9518 * @return {CompositeElement} this
9522 * Returns the Element object at the specified index
9523 * @param {Number} index
9524 * @return {Ext.core.Element}
9528 * Iterates each `element` in this `composite` calling the supplied function using {@link Ext#each Ext.each}.
9529 * @param {Function} fn
9531 The function to be called with each
9532 `element`. If the supplied function returns <tt>false</tt>,
9533 iteration stops. This function is called with the following arguments:
9535 - `element` : __Ext.core.Element++
9536 The element at the current `index` in the `composite`
9538 - `composite` : __Object__
9541 - `index` : __Number__
9542 The current index within the `composite`
9544 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed.
9545 * Defaults to the <code>element</code> at the current <code>index</code>
9546 * within the composite.
9547 * @return {CompositeElement} this
9553 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
9554 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9555 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9556 * @param {String/Array} selector The CSS selector or an array of elements
9557 * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
9558 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9559 * @return {CompositeElementLite/CompositeElement}
9560 * @member Ext.core.Element
9563 Ext.core.Element.select = function(selector, unique, root){
9565 if(typeof selector == "string"){
9566 els = Ext.core.Element.selectorFunction(selector, root);
9567 }else if(selector.length !== undefined){
9572 sourceClass: "Ext.core.Element",
9573 sourceMethod: "select",
9577 msg: "Invalid selector specified: " + selector
9581 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
9585 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
9586 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9587 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9588 * @param {String/Array} selector The CSS selector or an array of elements
9589 * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
9590 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9591 * @return {CompositeElementLite/CompositeElement}
9595 Ext.select = Ext.core.Element.select;