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>
26 * <p><b>NOTE:</b> For other arbitrary attributes, the value will currently <b>not</b> be automatically
27 * HTML-escaped prior to building the element's HTML string. This means that if your attribute value
28 * contains special characters that would not normally be allowed in a double-quoted attribute value,
29 * you <b>must</b> manually HTML-encode it beforehand (see {@link Ext.String#htmlEncode}) or risk
30 * malformed HTML being created. This behavior may change in a future release.</p>
32 * <p><b><u>Insertion methods</u></b></p>
33 * <p>Commonly used insertion methods:
34 * <div class="mdetail-params"><ul>
35 * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
36 * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
37 * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
38 * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
39 * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
40 * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
43 * <p><b><u>Example</u></b></p>
44 * <p>This is an example, where an unordered list with 3 children items is appended to an existing
45 * element with id <tt>'my-div'</tt>:<br>
47 var dh = Ext.core.DomHelper; // create shorthand alias
48 // specification object
53 // append children after creating
54 children: [ // may also specify 'cn' instead of 'children'
55 {tag: 'li', id: 'item0', html: 'List Item 0'},
56 {tag: 'li', id: 'item1', html: 'List Item 1'},
57 {tag: 'li', id: 'item2', html: 'List Item 2'}
61 'my-div', // the context element 'my-div' can either be the id or the actual node
62 spec // the specification object
65 * <p>Element creation specification parameters in this class may also be passed as an Array of
66 * specification objects. This can be used to insert multiple sibling nodes into an existing
67 * container very efficiently. For example, to add more list items to the example above:<pre><code>
69 {tag: 'li', id: 'item3', html: 'List Item 3'},
70 {tag: 'li', id: 'item4', html: 'List Item 4'}
74 * <p><b><u>Templating</u></b></p>
75 * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
76 * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
77 * insert new elements. Revisiting the example above, we could utilize templating this time:
80 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
82 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
84 for(var i = 0; i < 5, i++){
85 tpl.append(list, [i]); // use template to append to the actual node
88 * <p>An example using a template:<pre><code>
89 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
91 var tpl = new Ext.core.DomHelper.createTemplate(html);
92 tpl.append('blog-roll', ['link1', 'http://www.edspencer.net/', "Ed's Site"]);
93 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
96 * <p>The same example using named parameters:<pre><code>
97 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
99 var tpl = new Ext.core.DomHelper.createTemplate(html);
100 tpl.append('blog-roll', {
102 url: 'http://www.edspencer.net/',
103 text: "Ed's Site"
105 tpl.append('blog-roll', {
107 url: 'http://www.dustindiaz.com/',
108 text: "Dustin's Site"
112 * <p><b><u>Compiling Templates</u></b></p>
113 * <p>Templates are applied using regular expressions. The performance is great, but if
114 * you are adding a bunch of DOM elements using the same template, you can increase
115 * performance even further by {@link Ext.Template#compile "compiling"} the template.
116 * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
117 * broken up at the different variable points and a dynamic function is created and eval'ed.
118 * The generated function performs string concatenation of these parts and the passed
119 * variables instead of using regular expressions.
121 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
123 var tpl = new Ext.core.DomHelper.createTemplate(html);
126 //... use template like normal
129 * <p><b><u>Performance Boost</u></b></p>
130 * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
131 * of DOM can significantly boost performance.</p>
132 * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
133 * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
134 * results in the creation of a text node. Usage:</p>
136 Ext.core.DomHelper.useDom = true; // force it to use DOM; reduces performance
141 Ext.core.DomHelper = function(){
142 var tempTableEl = null,
143 emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
144 tableRe = /^table|tbody|tr|td$/i,
145 confRe = /tag|children|cn|html$/i,
146 tableElRe = /td|tr|tbody/i,
149 // kill repeat to save bytes
150 afterbegin = 'afterbegin',
151 afterend = 'afterend',
152 beforebegin = 'beforebegin',
153 beforeend = 'beforeend',
162 function doInsert(el, o, returnElement, pos, sibling, append){
166 newNode = createDom(o, null);
168 el.appendChild(newNode);
170 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
173 newNode = Ext.core.DomHelper.insertHtml(pos, el, Ext.core.DomHelper.createHtml(o));
175 return returnElement ? Ext.get(newNode, true) : newNode;
178 function createDom(o, parentNode){
186 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
187 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
188 for (var i = 0, l = o.length; i < l; i++) {
191 } else if (typeof o == 'string') { // Allow a string as a child spec.
192 el = doc.createTextNode(o);
194 el = doc.createElement( o.tag || 'div' );
195 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
197 if(!confRe.test(attr)){
203 el.setAttribute(attr, val);
210 Ext.core.DomHelper.applyStyles(el, o.style);
212 if ((cn = o.children || o.cn)) {
215 el.innerHTML = o.html;
219 parentNode.appendChild(el);
224 // build as innerHTML where available
225 function createHtml(o){
233 if(typeof o == "string"){
235 } else if (Ext.isArray(o)) {
236 for (i=0; i < o.length; i++) {
238 b += createHtml(o[i]);
242 b += '<' + (o.tag = o.tag || 'div');
245 if(!confRe.test(attr)){
246 if (typeof val == "object") {
247 b += ' ' + attr + '="';
249 b += key + ':' + val[key] + ';';
253 b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
257 // Now either just close the tag or try to add children and close the tag.
258 if (emptyTags.test(o.tag)) {
262 if ((cn = o.children || o.cn)) {
267 b += '</' + o.tag + '>';
273 function ieTable(depth, s, h, e){
274 tempTableEl.innerHTML = [s, h, e].join('');
281 // If the result is multiple siblings, then encapsulate them into one fragment.
284 var df = document.createDocumentFragment();
297 * Nasty code for IE's broken table implementation
299 function insertIntoTable(tag, where, el, html) {
303 tempTableEl = tempTableEl || document.createElement('div');
305 if(tag == 'td' && (where == afterbegin || where == beforeend) ||
306 !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
309 before = where == beforebegin ? el :
310 where == afterend ? el.nextSibling :
311 where == afterbegin ? el.firstChild : null;
313 if (where == beforebegin || where == afterend) {
317 if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
318 node = ieTable(4, trs, html, tre);
319 } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
320 (tag == 'tr' && (where == beforebegin || where == afterend))) {
321 node = ieTable(3, tbs, html, tbe);
323 node = ieTable(2, ts, html, te);
325 el.insertBefore(node, before);
331 * Fix for IE9 createContextualFragment missing method
333 function createContextualFragment(html){
334 var div = document.createElement("div"),
335 fragment = document.createDocumentFragment(),
339 div.innerHTML = html;
340 childNodes = div.childNodes;
341 length = childNodes.length;
343 for (; i < length; i++) {
344 fragment.appendChild(childNodes[i].cloneNode(true));
352 * Returns the markup for the passed Element(s) config.
353 * @param {Object} o The DOM object spec (and children)
356 markup : function(o){
357 return createHtml(o);
361 * Applies a style specification to an element.
362 * @param {String/HTMLElement} el The element to apply styles to
363 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
364 * a function which returns such a specification.
366 applyStyles : function(el, styles){
369 if (typeof styles == "function") {
370 styles = styles.call();
372 if (typeof styles == "string") {
373 styles = Ext.core.Element.parseStyles(styles);
375 if (typeof styles == "object") {
382 * Inserts an HTML fragment into the DOM.
383 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
384 * @param {HTMLElement/TextNode} el The context element
385 * @param {String} html The HTML fragment
386 * @return {HTMLElement} The new node
388 insertHtml : function(where, el, html){
397 where = where.toLowerCase();
398 // add these here because they are used in both branches of the condition.
399 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
400 hash[afterend] = ['AfterEnd', 'nextSibling'];
402 // if IE and context element is an HTMLElement
403 if (el.insertAdjacentHTML) {
404 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
408 // add these two to the hash.
409 hash[afterbegin] = ['AfterBegin', 'firstChild'];
410 hash[beforeend] = ['BeforeEnd', 'lastChild'];
411 if ((hashVal = hash[where])) {
412 el.insertAdjacentHTML(hashVal[0], html);
413 return el[hashVal[1]];
415 // if (not IE and context element is an HTMLElement) or TextNode
417 // we cannot insert anything inside a textnode so...
418 if (Ext.isTextNode(el)) {
419 where = where === 'afterbegin' ? 'beforebegin' : where;
420 where = where === 'beforeend' ? 'afterend' : where;
422 range = Ext.supports.CreateContextualFragment ? el.ownerDocument.createRange() : undefined;
423 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
427 frag = range.createContextualFragment(html);
429 frag = createContextualFragment(html);
431 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
432 return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
434 rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
437 range[setStart](el[rangeEl]);
438 frag = range.createContextualFragment(html);
440 frag = createContextualFragment(html);
443 if(where == afterbegin){
444 el.insertBefore(frag, el.firstChild);
446 el.appendChild(frag);
456 sourceClass: 'Ext.core.DomHelper',
457 sourceMethod: 'insertHtml',
460 msg: 'Illegal insertion point reached: "' + where + '"'
466 * Creates new DOM element(s) and inserts them before el.
467 * @param {Mixed} el The context element
468 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
469 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
470 * @return {HTMLElement/Ext.core.Element} The new node
472 insertBefore : function(el, o, returnElement){
473 return doInsert(el, o, returnElement, beforebegin);
477 * Creates new DOM element(s) and inserts them after el.
478 * @param {Mixed} el The context element
479 * @param {Object} o The DOM object spec (and children)
480 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
481 * @return {HTMLElement/Ext.core.Element} The new node
483 insertAfter : function(el, o, returnElement){
484 return doInsert(el, o, returnElement, afterend, 'nextSibling');
488 * Creates new DOM element(s) and inserts them as the first child of el.
489 * @param {Mixed} el The context element
490 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
491 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
492 * @return {HTMLElement/Ext.core.Element} The new node
494 insertFirst : function(el, o, returnElement){
495 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
499 * Creates new DOM element(s) and appends them to el.
500 * @param {Mixed} el The context element
501 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
502 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
503 * @return {HTMLElement/Ext.core.Element} The new node
505 append : function(el, o, returnElement){
506 return doInsert(el, o, returnElement, beforeend, '', true);
510 * Creates new DOM element(s) and overwrites the contents of el with them.
511 * @param {Mixed} el The context element
512 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
513 * @param {Boolean} returnElement (optional) true to return a Ext.core.Element
514 * @return {HTMLElement/Ext.core.Element} The new node
516 overwrite : function(el, o, returnElement){
518 el.innerHTML = createHtml(o);
519 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
522 createHtml : createHtml,
525 * Creates new DOM element(s) without inserting them to the document.
526 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
527 * @return {HTMLElement} The new uninserted node
530 createDom: createDom,
532 /** True to force the use of DOM instead of html fragments @type Boolean */
536 * Creates a new Ext.Template from the DOM object spec.
537 * @param {Object} o The DOM object spec (and children)
538 * @return {Ext.Template} The new template
540 createTemplate : function(o){
541 var html = Ext.core.DomHelper.createHtml(o);
542 return Ext.create('Ext.Template', html);
549 * This is code is also distributed under MIT license for use
550 * with jQuery and prototype JavaScript libraries.
553 * @class Ext.DomQuery
554 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).
556 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>
559 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.
561 <h4>Element Selectors:</h4>
563 <li> <b>*</b> any element</li>
564 <li> <b>E</b> an element with the tag E</li>
565 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
566 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
567 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
568 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
570 <h4>Attribute Selectors:</h4>
571 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
573 <li> <b>E[foo]</b> has an attribute "foo"</li>
574 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
575 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
576 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
577 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
578 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
579 <li> <b>E[foo!=bar]</b> attribute "foo" does not equal "bar"</li>
581 <h4>Pseudo Classes:</h4>
583 <li> <b>E:first-child</b> E is the first child of its parent</li>
584 <li> <b>E:last-child</b> E is the last child of its parent</li>
585 <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>
586 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
587 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
588 <li> <b>E:only-child</b> E is the only child of its parent</li>
589 <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>
590 <li> <b>E:first</b> the first E in the resultset</li>
591 <li> <b>E:last</b> the last E in the resultset</li>
592 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
593 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
594 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
595 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
596 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
597 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
598 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
599 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
600 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
601 <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
603 <h4>CSS Value Selectors:</h4>
605 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
606 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
607 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
608 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
609 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
610 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
616 Ext.core.DomQuery = Ext.DomQuery = function(){
621 trimRe = /^\s+|\s+$/g,
622 tplRe = /\{(\d+)\}/g,
623 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
624 tagTokenRe = /^(#)?([\w-\*]+)/,
625 nthRe = /(\d*)n\+?(\d*)/,
627 // This is for IE MSXML which does not support expandos.
628 // IE runs the same speed using setAttribute, however FF slows way down
629 // and Safari completely fails so they need to continue to use expandos.
630 isIE = window.ActiveXObject ? true : false,
633 // this eval is stop the compressor from
634 // renaming the variable to something shorter
635 eval("var batch = 30803;");
637 // Retrieve the child node from a particular
638 // parent at the specified index.
639 function child(parent, index){
641 n = parent.firstChild;
653 // retrieve the next element node
655 while((n = n.nextSibling) && n.nodeType != 1);
659 // retrieve the previous element node
661 while((n = n.previousSibling) && n.nodeType != 1);
665 // Mark each child node with a nodeIndex skipping and
666 // removing empty text nodes.
667 function children(parent){
668 var n = parent.firstChild,
672 nextNode = n.nextSibling;
673 // clean worthless empty nodes.
674 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
675 parent.removeChild(n);
677 // add an expando nodeIndex
678 n.nodeIndex = ++nodeIndex;
686 // nodeSet - array of nodes
688 function byClassName(nodeSet, cls){
692 var result = [], ri = -1;
693 for(var i = 0, ci; ci = nodeSet[i]; i++){
694 if((' '+ci.className+' ').indexOf(cls) != -1){
701 function attrValue(n, attr){
702 // if its an array, use the first node.
703 if(!n.tagName && typeof n.length != "undefined"){
713 if(attr == "class" || attr == "className"){
716 return n.getAttribute(attr) || n[attr];
722 // mode - false, /, >, +, ~
723 // tagName - defaults to "*"
724 function getNodes(ns, mode, tagName){
725 var result = [], ri = -1, cs;
729 tagName = tagName || "*";
731 if(typeof ns.getElementsByTagName != "undefined"){
735 // no mode specified, grab all elements by tagName
738 for(var i = 0, ni; ni = ns[i]; i++){
739 cs = ni.getElementsByTagName(tagName);
740 for(var j = 0, ci; ci = cs[j]; j++){
744 // Direct Child mode (/ or >)
745 // E > F or E/F all direct children elements of E that have the tag
746 } else if(mode == "/" || mode == ">"){
747 var utag = tagName.toUpperCase();
748 for(var i = 0, ni, cn; ni = ns[i]; i++){
750 for(var j = 0, cj; cj = cn[j]; j++){
751 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
756 // Immediately Preceding mode (+)
757 // E + F all elements with the tag F that are immediately preceded by an element with the tag E
758 }else if(mode == "+"){
759 var utag = tagName.toUpperCase();
760 for(var i = 0, n; n = ns[i]; i++){
761 while((n = n.nextSibling) && n.nodeType != 1);
762 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
767 // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
768 }else if(mode == "~"){
769 var utag = tagName.toUpperCase();
770 for(var i = 0, n; n = ns[i]; i++){
771 while((n = n.nextSibling)){
772 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
781 function concat(a, b){
785 for(var i = 0, l = b.length; i < l; i++){
791 function byTag(cs, tagName){
792 if(cs.tagName || cs == document){
798 var result = [], ri = -1;
799 tagName = tagName.toLowerCase();
800 for(var i = 0, ci; ci = cs[i]; i++){
801 if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
808 function byId(cs, id){
809 if(cs.tagName || cs == document){
815 var result = [], ri = -1;
816 for(var i = 0, ci; ci = cs[i]; i++){
817 if(ci && ci.id == id){
825 // operators are =, !=, ^=, $=, *=, %=, |= and ~=
827 function byAttribute(cs, attr, value, op, custom){
830 useGetStyle = custom == "{",
831 fn = Ext.DomQuery.operators[op],
836 for(var i = 0, ci; ci = cs[i]; i++){
837 // skip non-element nodes.
838 if(ci.nodeType != 1){
841 // only need to do this for the first node
843 xml = Ext.DomQuery.isXml(ci);
847 // we only need to change the property names if we're dealing with html nodes, not XML
850 a = Ext.DomQuery.getStyle(ci, attr);
851 } else if (attr == "class" || attr == "className"){
853 } else if (attr == "for"){
855 } else if (attr == "href"){
856 // getAttribute href bug
857 // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
858 a = ci.getAttribute("href", 2);
860 a = ci.getAttribute(attr);
863 a = ci.getAttribute(attr);
865 if((fn && fn(a, value)) || (!fn && a)){
872 function byPseudo(cs, name, value){
873 return Ext.DomQuery.pseudos[name](cs, value);
876 function nodupIEXml(cs){
879 cs[0].setAttribute("_nodup", d);
881 for(var i = 1, len = cs.length; i < len; i++){
883 if(!c.getAttribute("_nodup") != d){
884 c.setAttribute("_nodup", d);
888 for(var i = 0, len = cs.length; i < len; i++){
889 cs[i].removeAttribute("_nodup");
898 var len = cs.length, c, i, r = cs, cj, ri = -1;
899 if(!len || typeof cs.nodeType != "undefined" || len == 1){
902 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
903 return nodupIEXml(cs);
907 for(i = 1; c = cs[i]; i++){
912 for(var j = 0; j < i; j++){
915 for(j = i+1; cj = cs[j]; j++){
927 function quickDiffIEXml(c1, c2){
930 for(var i = 0, len = c1.length; i < len; i++){
931 c1[i].setAttribute("_qdiff", d);
933 for(var i = 0, len = c2.length; i < len; i++){
934 if(c2[i].getAttribute("_qdiff") != d){
938 for(var i = 0, len = c1.length; i < len; i++){
939 c1[i].removeAttribute("_qdiff");
944 function quickDiff(c1, c2){
945 var len1 = c1.length,
951 if(isIE && typeof c1[0].selectSingleNode != "undefined"){
952 return quickDiffIEXml(c1, c2);
954 for(var i = 0; i < len1; i++){
957 for(var i = 0, len = c2.length; i < len; i++){
958 if(c2[i]._qdiff != d){
965 function quickId(ns, mode, root, id){
967 var d = root.ownerDocument || root;
968 return d.getElementById(id);
970 ns = getNodes(ns, mode, "*");
975 getStyle : function(el, name){
976 return Ext.fly(el).getStyle(name);
979 * Compiles a selector/xpath query into a reusable function. The returned function
980 * takes one parameter "root" (optional), which is the context node from where the query should start.
981 * @param {String} selector The selector/xpath query
982 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
985 compile : function(path, type){
986 type = type || "select";
989 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
992 matchers = Ext.DomQuery.matchers,
993 matchersLn = matchers.length,
995 // accept leading mode switch
996 lmode = path.match(modeRe);
998 if(lmode && lmode[1]){
999 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
1000 path = path.replace(lmode[1], "");
1003 // strip leading slashes
1004 while(path.substr(0, 1)=="/"){
1005 path = path.substr(1);
1008 while(path && lastPath != path){
1010 var tokenMatch = path.match(tagTokenRe);
1011 if(type == "select"){
1014 if(tokenMatch[1] == "#"){
1015 fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
1017 fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1019 path = path.replace(tokenMatch[0], "");
1020 }else if(path.substr(0, 1) != '@'){
1021 fn[fn.length] = 'n = getNodes(n, mode, "*");';
1026 if(tokenMatch[1] == "#"){
1027 fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1029 fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1031 path = path.replace(tokenMatch[0], "");
1034 while(!(modeMatch = path.match(modeRe))){
1035 var matched = false;
1036 for(var j = 0; j < matchersLn; j++){
1037 var t = matchers[j];
1038 var m = path.match(t.re);
1040 fn[fn.length] = t.select.replace(tplRe, function(x, i){
1043 path = path.replace(m[0], "");
1048 // prevent infinite loop on bad selector
1052 sourceClass: 'Ext.DomQuery',
1053 sourceMethod: 'compile',
1054 msg: 'Error parsing selector. Parsing failed at "' + path + '"'
1060 fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1061 path = path.replace(modeMatch[1], "");
1065 fn[fn.length] = "return nodup(n);\n}";
1067 // eval fn and return it
1073 * Selects an array of DOM nodes using JavaScript-only implementation.
1075 * Use {@link #select} to take advantage of browsers built-in support for CSS selectors.
1077 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1078 * @param {Node/String} root (optional) The start of the query (defaults to document).
1079 * @return {Array} An Array of DOM elements which match the selector. If there are
1080 * no matches, and empty Array is returned.
1082 jsSelect: function(path, root, type){
1083 // set root to doc if not specified.
1084 root = root || document;
1086 if(typeof root == "string"){
1087 root = document.getElementById(root);
1089 var paths = path.split(","),
1092 // loop over each selector
1093 for(var i = 0, len = paths.length; i < len; i++){
1094 var subPath = paths[i].replace(trimRe, "");
1095 // compile and place in cache
1096 if(!cache[subPath]){
1097 cache[subPath] = Ext.DomQuery.compile(subPath);
1098 if(!cache[subPath]){
1101 sourceClass: 'Ext.DomQuery',
1102 sourceMethod: 'jsSelect',
1103 msg: subPath + ' is not a valid selector'
1108 var result = cache[subPath](root);
1109 if(result && result != document){
1110 results = results.concat(result);
1114 // if there were multiple selectors, make sure dups
1116 if(paths.length > 1){
1117 return nodup(results);
1122 isXml: function(el) {
1123 var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1124 return docEl ? docEl.nodeName !== "HTML" : false;
1128 * Selects an array of DOM nodes by CSS/XPath selector.
1130 * Uses [document.querySelectorAll][0] if browser supports that, otherwise falls back to
1131 * {@link #jsSelect} to do the work.
1133 * Aliased as {@link Ext#query}.
1135 * [0]: https://developer.mozilla.org/en/DOM/document.querySelectorAll
1137 * @param {String} path The selector/xpath query
1138 * @param {Node} root (optional) The start of the query (defaults to document).
1139 * @return {Array} An array of DOM elements (not a NodeList as returned by `querySelectorAll`).
1140 * Empty array when no matches.
1143 select : document.querySelectorAll ? function(path, root, type) {
1144 root = root || document;
1145 if (!Ext.DomQuery.isXml(root)) {
1147 var cs = root.querySelectorAll(path);
1148 return Ext.Array.toArray(cs);
1152 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1153 } : function(path, root, type) {
1154 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1158 * Selects a single element.
1159 * @param {String} selector The selector/xpath query
1160 * @param {Node} root (optional) The start of the query (defaults to document).
1161 * @return {Element} The DOM element which matched the selector.
1163 selectNode : function(path, root){
1164 return Ext.DomQuery.select(path, root)[0];
1168 * Selects the value of a node, optionally replacing null with the defaultValue.
1169 * @param {String} selector The selector/xpath query
1170 * @param {Node} root (optional) The start of the query (defaults to document).
1171 * @param {String} defaultValue
1174 selectValue : function(path, root, defaultValue){
1175 path = path.replace(trimRe, "");
1176 if(!valueCache[path]){
1177 valueCache[path] = Ext.DomQuery.compile(path, "select");
1179 var n = valueCache[path](root), v;
1180 n = n[0] ? n[0] : n;
1182 // overcome a limitation of maximum textnode size
1183 // Rumored to potentially crash IE6 but has not been confirmed.
1184 // http://reference.sitepoint.com/javascript/Node/normalize
1185 // https://developer.mozilla.org/En/DOM/Node.normalize
1186 if (typeof n.normalize == 'function') n.normalize();
1188 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1189 return ((v === null||v === undefined||v==='') ? defaultValue : v);
1193 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1194 * @param {String} selector The selector/xpath query
1195 * @param {Node} root (optional) The start of the query (defaults to document).
1196 * @param {Number} defaultValue
1199 selectNumber : function(path, root, defaultValue){
1200 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1201 return parseFloat(v);
1205 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1206 * @param {String/HTMLElement/Array} el An element id, element or array of elements
1207 * @param {String} selector The simple selector to test
1210 is : function(el, ss){
1211 if(typeof el == "string"){
1212 el = document.getElementById(el);
1214 var isArray = Ext.isArray(el),
1215 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1216 return isArray ? (result.length == el.length) : (result.length > 0);
1220 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1221 * @param {Array} el An array of elements to filter
1222 * @param {String} selector The simple selector to test
1223 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1224 * the selector instead of the ones that match
1225 * @return {Array} An Array of DOM elements which match the selector. If there are
1226 * no matches, and empty Array is returned.
1228 filter : function(els, ss, nonMatches){
1229 ss = ss.replace(trimRe, "");
1230 if(!simpleCache[ss]){
1231 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1233 var result = simpleCache[ss](els);
1234 return nonMatches ? quickDiff(result, els) : result;
1238 * Collection of matching regular expressions and code snippets.
1239 * Each capture group within () will be replace the {} in the select
1240 * statement as specified by their index.
1244 select: 'n = byClassName(n, " {1} ");'
1246 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1247 select: 'n = byPseudo(n, "{1}", "{2}");'
1249 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1250 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1253 select: 'n = byId(n, "{1}");'
1256 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1261 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1262 * 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, > <.
1265 "=" : function(a, v){
1268 "!=" : function(a, v){
1271 "^=" : function(a, v){
1272 return a && a.substr(0, v.length) == v;
1274 "$=" : function(a, v){
1275 return a && a.substr(a.length-v.length) == v;
1277 "*=" : function(a, v){
1278 return a && a.indexOf(v) !== -1;
1280 "%=" : function(a, v){
1281 return (a % v) == 0;
1283 "|=" : function(a, v){
1284 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1286 "~=" : function(a, v){
1287 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1292 Object hash of "pseudo class" filter functions which are used when filtering selections.
1293 Each function is passed two parameters:
1296 An Array of DOM elements to filter.
1299 The argument (if any) supplied in the selector.
1301 A filter function returns an Array of DOM elements which conform to the pseudo class.
1302 In addition to the provided pseudo classes listed above such as `first-child` and `nth-child`,
1303 developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.
1305 For example, to filter `a` elements to only return links to __external__ resources:
1307 Ext.DomQuery.pseudos.external = function(c, v){
1308 var r = [], ri = -1;
1309 for(var i = 0, ci; ci = c[i]; i++){
1310 // Include in result set only if it's a link to an external resource
1311 if(ci.hostname != location.hostname){
1318 Then external links could be gathered with the following statement:
1320 var externalLinks = Ext.select("a:external");
1325 "first-child" : function(c){
1326 var r = [], ri = -1, n;
1327 for(var i = 0, ci; ci = n = c[i]; i++){
1328 while((n = n.previousSibling) && n.nodeType != 1);
1336 "last-child" : function(c){
1337 var r = [], ri = -1, n;
1338 for(var i = 0, ci; ci = n = c[i]; i++){
1339 while((n = n.nextSibling) && n.nodeType != 1);
1347 "nth-child" : function(c, a) {
1348 var r = [], ri = -1,
1349 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1350 f = (m[1] || 1) - 0, l = m[2] - 0;
1351 for(var i = 0, n; n = c[i]; i++){
1352 var pn = n.parentNode;
1353 if (batch != pn._batch) {
1355 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1356 if(cn.nodeType == 1){
1363 if (l == 0 || n.nodeIndex == l){
1366 } else if ((n.nodeIndex + l) % f == 0){
1374 "only-child" : function(c){
1375 var r = [], ri = -1;;
1376 for(var i = 0, ci; ci = c[i]; i++){
1377 if(!prev(ci) && !next(ci)){
1384 "empty" : function(c){
1385 var r = [], ri = -1;
1386 for(var i = 0, ci; ci = c[i]; i++){
1387 var cns = ci.childNodes, j = 0, cn, empty = true;
1390 if(cn.nodeType == 1 || cn.nodeType == 3){
1402 "contains" : function(c, v){
1403 var r = [], ri = -1;
1404 for(var i = 0, ci; ci = c[i]; i++){
1405 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
1412 "nodeValue" : function(c, v){
1413 var r = [], ri = -1;
1414 for(var i = 0, ci; ci = c[i]; i++){
1415 if(ci.firstChild && ci.firstChild.nodeValue == v){
1422 "checked" : function(c){
1423 var r = [], ri = -1;
1424 for(var i = 0, ci; ci = c[i]; i++){
1425 if(ci.checked == true){
1432 "not" : function(c, ss){
1433 return Ext.DomQuery.filter(c, ss, true);
1436 "any" : function(c, selectors){
1437 var ss = selectors.split('|'),
1439 for(var i = 0, ci; ci = c[i]; i++){
1440 for(var j = 0; s = ss[j]; j++){
1441 if(Ext.DomQuery.is(ci, s)){
1450 "odd" : function(c){
1451 return this["nth-child"](c, "odd");
1454 "even" : function(c){
1455 return this["nth-child"](c, "even");
1458 "nth" : function(c, a){
1459 return c[a-1] || [];
1462 "first" : function(c){
1466 "last" : function(c){
1467 return c[c.length-1] || [];
1470 "has" : function(c, ss){
1471 var s = Ext.DomQuery.select,
1473 for(var i = 0, ci; ci = c[i]; i++){
1474 if(s(ss, ci).length > 0){
1481 "next" : function(c, ss){
1482 var is = Ext.DomQuery.is,
1484 for(var i = 0, ci; ci = c[i]; i++){
1493 "prev" : function(c, ss){
1494 var is = Ext.DomQuery.is,
1496 for(var i = 0, ci; ci = c[i]; i++){
1509 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
1510 * @param {String} path The selector/xpath query
1511 * @param {Node} root (optional) The start of the query (defaults to document).
1516 Ext.query = Ext.DomQuery.select;
1519 * @class Ext.core.Element
1520 * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
1521 * <p>All instances of this class inherit the methods of {@link Ext.fx.Anim} making visual effects easily available to all DOM elements.</p>
1522 * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
1523 * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
1524 * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
1528 var el = Ext.get("my-div");
1530 // by DOM element reference
1531 var el = Ext.get(myDivElement);
1533 * <b>Animations</b><br />
1534 * <p>When an element is manipulated, by default there is no animation.</p>
1536 var el = Ext.get("my-div");
1541 * <p>Many of the functions for manipulating an element have an optional "animate" parameter. This
1542 * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
1544 // default animation
1545 el.setWidth(100, true);
1548 * <p>To configure the effects, an object literal with animation options to use as the Element animation
1549 * configuration object can also be specified. Note that the supported Element animation configuration
1550 * options are a subset of the {@link Ext.fx.Anim} animation options specific to Fx effects. The supported
1551 * Element animation configuration options are:</p>
1553 Option Default Description
1554 --------- -------- ---------------------------------------------
1555 {@link Ext.fx.Anim#duration duration} .35 The duration of the animation in seconds
1556 {@link Ext.fx.Anim#easing easing} easeOut The easing method
1557 {@link Ext.fx.Anim#callback callback} none A function to execute when the anim completes
1558 {@link Ext.fx.Anim#scope scope} this The scope (this) of the callback function
1562 // Element animation options object
1564 {@link Ext.fx.Anim#duration duration}: 1,
1565 {@link Ext.fx.Anim#easing easing}: 'elasticIn',
1566 {@link Ext.fx.Anim#callback callback}: this.foo,
1567 {@link Ext.fx.Anim#scope scope}: this
1569 // animation with some options set
1570 el.setWidth(100, opt);
1572 * <p>The Element animation object being used for the animation will be set on the options
1573 * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
1575 // using the "anim" property to get the Anim object
1576 if(opt.anim.isAnimated()){
1580 * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
1581 * <p><b> Composite (Collections of) Elements</b></p>
1582 * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
1583 * @constructor Create a new Element directly.
1584 * @param {String/HTMLElement} element
1585 * @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).
1591 Ext.Element = Ext.core.Element = function(element, forceNew) {
1592 var dom = typeof element == "string" ? DOC.getElementById(element) : element,
1601 if (!forceNew && id && EC[id]) {
1602 // element object already exists
1613 * The DOM element ID
1616 this.id = id || Ext.id(dom);
1619 var DH = Ext.core.DomHelper,
1620 El = Ext.core.Element;
1625 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
1626 * @param {Object} o The object with the attributes
1627 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
1628 * @return {Ext.core.Element} this
1630 set: function(o, useSet) {
1634 useSet = (useSet !== false) && !!el.setAttribute;
1637 if (o.hasOwnProperty(attr)) {
1639 if (attr == 'style') {
1640 DH.applyStyles(el, val);
1641 } else if (attr == 'cls') {
1643 } else if (useSet) {
1644 el.setAttribute(attr, val);
1656 * Fires when a mouse click is detected within the element.
1657 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1658 * @param {HtmlElement} t The target of the event.
1659 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1662 * @event contextmenu
1663 * Fires when a right click is detected within the element.
1664 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1665 * @param {HtmlElement} t The target of the event.
1666 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1670 * Fires when a mouse double click is detected within the element.
1671 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1672 * @param {HtmlElement} t The target of the event.
1673 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1677 * Fires when a mousedown is detected within the element.
1678 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1679 * @param {HtmlElement} t The target of the event.
1680 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1684 * Fires when a mouseup is detected within the element.
1685 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1686 * @param {HtmlElement} t The target of the event.
1687 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1691 * Fires when a mouseover is detected within the element.
1692 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1693 * @param {HtmlElement} t The target of the event.
1694 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1698 * Fires when a mousemove is detected with the element.
1699 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1700 * @param {HtmlElement} t The target of the event.
1701 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1705 * Fires when a mouseout is detected with the element.
1706 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1707 * @param {HtmlElement} t The target of the event.
1708 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1712 * Fires when the mouse enters the element.
1713 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1714 * @param {HtmlElement} t The target of the event.
1715 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1719 * Fires when the mouse leaves the element.
1720 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1721 * @param {HtmlElement} t The target of the event.
1722 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1728 * Fires when a keypress is detected within the element.
1729 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1730 * @param {HtmlElement} t The target of the event.
1731 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1735 * Fires when a keydown is detected within the element.
1736 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1737 * @param {HtmlElement} t The target of the event.
1738 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1742 * Fires when a keyup is detected within the element.
1743 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1744 * @param {HtmlElement} t The target of the event.
1745 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1749 // HTML frame/object events
1752 * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
1753 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1754 * @param {HtmlElement} t The target of the event.
1755 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1759 * 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.
1760 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1761 * @param {HtmlElement} t The target of the event.
1762 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1766 * Fires when an object/image is stopped from loading before completely loaded.
1767 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1768 * @param {HtmlElement} t The target of the event.
1769 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1773 * Fires when an object/image/frame cannot be loaded properly.
1774 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1775 * @param {HtmlElement} t The target of the event.
1776 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1780 * Fires when a document view is resized.
1781 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1782 * @param {HtmlElement} t The target of the event.
1783 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1787 * Fires when a document view is scrolled.
1788 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1789 * @param {HtmlElement} t The target of the event.
1790 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1796 * Fires when a user selects some text in a text field, including input and textarea.
1797 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1798 * @param {HtmlElement} t The target of the event.
1799 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1803 * Fires when a control loses the input focus and its value has been modified since gaining focus.
1804 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1805 * @param {HtmlElement} t The target of the event.
1806 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1810 * Fires when a form is submitted.
1811 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1812 * @param {HtmlElement} t The target of the event.
1813 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1817 * Fires when a form is reset.
1818 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1819 * @param {HtmlElement} t The target of the event.
1820 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1824 * Fires when an element receives focus either via the pointing device or by tab navigation.
1825 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1826 * @param {HtmlElement} t The target of the event.
1827 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1831 * Fires when an element loses focus either via the pointing device or by tabbing navigation.
1832 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1833 * @param {HtmlElement} t The target of the event.
1834 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1837 // User Interface events
1840 * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
1841 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1842 * @param {HtmlElement} t The target of the event.
1843 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1846 * @event DOMFocusOut
1847 * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
1848 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1849 * @param {HtmlElement} t The target of the event.
1850 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1853 * @event DOMActivate
1854 * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
1855 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1856 * @param {HtmlElement} t The target of the event.
1857 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1860 // DOM Mutation events
1862 * @event DOMSubtreeModified
1863 * Where supported. Fires when the subtree is modified.
1864 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1865 * @param {HtmlElement} t The target of the event.
1866 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1869 * @event DOMNodeInserted
1870 * Where supported. Fires when a node has been added as a child of another node.
1871 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1872 * @param {HtmlElement} t The target of the event.
1873 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1876 * @event DOMNodeRemoved
1877 * Where supported. Fires when a descendant node of the element is removed.
1878 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1879 * @param {HtmlElement} t The target of the event.
1880 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1883 * @event DOMNodeRemovedFromDocument
1884 * Where supported. Fires when a node is being removed from a document.
1885 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1886 * @param {HtmlElement} t The target of the event.
1887 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1890 * @event DOMNodeInsertedIntoDocument
1891 * Where supported. Fires when a node is being inserted into a document.
1892 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1893 * @param {HtmlElement} t The target of the event.
1894 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1897 * @event DOMAttrModified
1898 * Where supported. Fires when an attribute has been modified.
1899 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1900 * @param {HtmlElement} t The target of the event.
1901 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1904 * @event DOMCharacterDataModified
1905 * Where supported. Fires when the character data has been modified.
1906 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
1907 * @param {HtmlElement} t The target of the event.
1908 * @param {Object} o The options configuration passed to the {@link #addListener} call.
1912 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
1918 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
1919 * @param {String} selector The simple selector to test
1920 * @return {Boolean} True if this element matches the selector, else false
1922 is: function(simpleSelector) {
1923 return Ext.DomQuery.is(this.dom, simpleSelector);
1927 * Tries to focus the element. Any exceptions are caught and ignored.
1928 * @param {Number} defer (optional) Milliseconds to defer the focus
1929 * @return {Ext.core.Element} this
1931 focus: function(defer,
1935 dom = dom || me.dom;
1937 if (Number(defer)) {
1938 Ext.defer(me.focus, defer, null, [null, dom]);
1947 * Tries to blur the element. Any exceptions are caught and ignored.
1948 * @return {Ext.core.Element} this
1958 * Returns the value of the "value" attribute
1959 * @param {Boolean} asNumber true to parse the value as a number
1960 * @return {String/Number}
1962 getValue: function(asNumber) {
1963 var val = this.dom.value;
1964 return asNumber ? parseInt(val, 10) : val;
1968 * Appends an event handler to this element. The shorthand version {@link #on} is equivalent.
1969 * @param {String} eventName The name of event to handle.
1970 * @param {Function} fn The handler function the event invokes. This function is passed
1971 * the following parameters:<ul>
1972 * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
1973 * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
1974 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
1975 * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
1977 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1978 * <b>If omitted, defaults to this Element.</b>.
1979 * @param {Object} options (optional) An object containing handler configuration properties.
1980 * This may contain any of the following properties:<ul>
1981 * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1982 * <b>If omitted, defaults to this Element.</b></div></li>
1983 * <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>
1984 * <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>
1985 * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
1986 * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
1987 * <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>
1988 * <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>
1989 * <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>
1990 * <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>
1991 * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
1992 * by the specified number of milliseconds. If the event fires again within that time, the original
1993 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
1996 * <b>Combining Options</b><br>
1997 * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
1998 * addListener. The two are equivalent. Using the options argument, it is possible to combine different
1999 * types of listeners:<br>
2001 * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
2002 * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
2004 el.on('click', this.onClick, this, {
2009 });</code></pre></p>
2011 * <b>Attaching multiple handlers in 1 call</b><br>
2012 * The method also allows for a single argument to be passed which is a config object containing properties
2013 * which specify multiple handlers.</p>
2023 fn: this.onMouseOver,
2027 fn: this.onMouseOut,
2032 * Or a shorthand syntax:<br>
2033 * Code:<pre><code></p>
2035 'click' : this.onClick,
2036 'mouseover' : this.onMouseOver,
2037 'mouseout' : this.onMouseOut,
2041 * <p><b>delegate</b></p>
2042 * <p>This is a configuration option that you can pass along when registering a handler for
2043 * an event to assist with event delegation. Event delegation is a technique that is used to
2044 * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
2045 * for a container element as opposed to each element within a container. By setting this
2046 * configuration option to a simple selector, the target element will be filtered to look for
2047 * a descendant of the target.
2048 * For example:<pre><code>
2049 // using this markup:
2051 <p id='p1'>paragraph one</p>
2052 <p id='p2' class='clickable'>paragraph two</p>
2053 <p id='p3'>paragraph three</p>
2055 // utilize event delegation to registering just one handler on the container element:
2056 el = Ext.get('elId');
2061 console.info(t.id); // 'p2'
2065 // filter the target element to be a descendant with the class 'clickable'
2066 delegate: '.clickable'
2070 * @return {Ext.core.Element} this
2072 addListener: function(eventName, fn, scope, options) {
2073 Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
2078 * Removes an event handler from this element. The shorthand version {@link #un} is equivalent.
2079 * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
2080 * listener, the same scope must be specified here.
2083 el.removeListener('click', this.handlerFn);
2085 el.un('click', this.handlerFn);
2087 * @param {String} eventName The name of the event from which to remove the handler.
2088 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2089 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2090 * then this must refer to the same object.
2091 * @return {Ext.core.Element} this
2093 removeListener: function(eventName, fn, scope) {
2094 Ext.EventManager.un(this.dom, eventName, fn, scope || this);
2099 * Removes all previous added listeners from this element
2100 * @return {Ext.core.Element} this
2102 removeAllListeners: function() {
2103 Ext.EventManager.removeAll(this.dom);
2108 * Recursively removes all previous added listeners from this element and its children
2109 * @return {Ext.core.Element} this
2111 purgeAllListeners: function() {
2112 Ext.EventManager.purgeElement(this);
2117 * @private Test if size has a unit, otherwise appends the passed unit string, or the default for this Element.
2118 * @param size {Mixed} The size to set
2119 * @param units {String} The units to append to a numeric size value
2121 addUnits: function(size, units) {
2123 // Most common case first: Size is set to a number
2124 if (Ext.isNumber(size)) {
2125 return size + (units || this.defaultUnit || 'px');
2128 // Size set to a value which means "auto"
2129 if (size === "" || size == "auto" || size === undefined || size === null) {
2133 // Otherwise, warn if it's not a valid CSS measurement
2134 if (!unitPattern.test(size)) {
2136 if (Ext.isDefined(Ext.global.console)) {
2137 Ext.global.console.warn("Warning, size detected as NaN on Element.addUnits.");
2146 * Tests various css rules/browsers to determine if this element uses a border box
2149 isBorderBox: function() {
2150 return Ext.isBorderBox || noBoxAdjust[(this.dom.tagName || "").toLowerCase()];
2154 * <p>Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}</p>
2156 remove: function() {
2162 Ext.removeNode(dom);
2167 * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
2168 * @param {Function} overFn The function to call when the mouse enters the Element.
2169 * @param {Function} outFn The function to call when the mouse leaves the Element.
2170 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
2171 * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
2172 * @return {Ext.core.Element} this
2174 hover: function(overFn, outFn, scope, options) {
2176 me.on('mouseenter', overFn, scope || me.dom, options);
2177 me.on('mouseleave', outFn, scope || me.dom, options);
2182 * Returns true if this element is an ancestor of the passed element
2183 * @param {HTMLElement/String} el The element to check
2184 * @return {Boolean} True if this element is an ancestor of el, else false
2186 contains: function(el) {
2187 return ! el ? false: Ext.core.Element.isAncestor(this.dom, el.dom ? el.dom: el);
2191 * Returns the value of a namespaced attribute from the element's underlying DOM node.
2192 * @param {String} namespace The namespace in which to look for the attribute
2193 * @param {String} name The attribute name
2194 * @return {String} The attribute value
2197 getAttributeNS: function(ns, name) {
2198 return this.getAttribute(name, ns);
2202 * Returns the value of an attribute from the element's underlying DOM node.
2203 * @param {String} name The attribute name
2204 * @param {String} namespace (optional) The namespace in which to look for the attribute
2205 * @return {String} The attribute value
2208 getAttribute: (Ext.isIE && !(Ext.isIE9 && document.documentMode === 9)) ?
2209 function(name, ns) {
2213 type = typeof d[ns + ":" + name];
2214 if (type != 'undefined' && type != 'unknown') {
2215 return d[ns + ":" + name] || null;
2219 if (name === "for") {
2222 return d[name] || null;
2223 }: function(name, ns) {
2226 return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name);
2228 return d.getAttribute(name) || d[name] || null;
2232 * Update the innerHTML of this element
2233 * @param {String} html The new HTML
2234 * @return {Ext.core.Element} this
2236 update: function(html) {
2238 this.dom.innerHTML = html;
2244 var ep = El.prototype;
2246 El.addMethods = function(o) {
2251 * Appends an event handler (shorthand for {@link #addListener}).
2252 * @param {String} eventName The name of event to handle.
2253 * @param {Function} fn The handler function the event invokes.
2254 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
2255 * @param {Object} options (optional) An object containing standard {@link #addListener} options
2256 * @member Ext.core.Element
2259 ep.on = ep.addListener;
2262 * Removes an event handler from this element (see {@link #removeListener} for additional notes).
2263 * @param {String} eventName The name of the event from which to remove the handler.
2264 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2265 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2266 * then this must refer to the same object.
2267 * @return {Ext.core.Element} this
2268 * @member Ext.core.Element
2271 ep.un = ep.removeListener;
2274 * Removes all previous added listeners from this element
2275 * @return {Ext.core.Element} this
2276 * @member Ext.core.Element
2277 * @method clearListeners
2279 ep.clearListeners = ep.removeAllListeners;
2282 * Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode Ext.removeNode}.
2283 * Alias to {@link #remove}.
2284 * @member Ext.core.Element
2287 ep.destroy = ep.remove;
2290 * true to automatically adjust width and height settings for box-model issues (default to true)
2292 ep.autoBoxAdjust = true;
2295 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
2299 * Retrieves Ext.core.Element objects.
2300 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
2301 * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
2302 * its ID, use {@link Ext.ComponentManager#get}.</p>
2303 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
2304 * object was recreated with the same id via AJAX or DOM.</p>
2305 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
2306 * @return {Element} The Element object (or null if no matching element was found)
2308 * @member Ext.core.Element
2311 El.get = function(el) {
2318 if (typeof el == "string") {
2320 if (! (elm = DOC.getElementById(el))) {
2323 if (EC[el] && EC[el].el) {
2327 ex = El.addToCache(new El(elm));
2330 } else if (el.tagName) {
2332 if (! (id = el.id)) {
2335 if (EC[id] && EC[id].el) {
2339 ex = El.addToCache(new El(el));
2342 } else if (el instanceof El) {
2344 // refresh dom element in case no longer valid,
2345 // catch case where it hasn't been appended
2346 // If an el instance is passed, don't pass to getElementById without some kind of id
2347 if (Ext.isIE && (el.id == undefined || el.id == '')) {
2350 el.dom = DOC.getElementById(el.id) || el.dom;
2354 } else if (el.isComposite) {
2356 } else if (Ext.isArray(el)) {
2357 return El.select(el);
2358 } else if (el == DOC) {
2359 // create a bogus element object representing the document object
2361 var f = function() {};
2362 f.prototype = El.prototype;
2371 El.addToCache = function(el, id) {
2383 // private method for getting and setting element data
2384 El.data = function(el, key, value) {
2389 var c = EC[el.id].data;
2390 if (arguments.length == 2) {
2393 return (c[key] = value);
2398 // Garbage collection - uncache elements/purge listeners on orphaned elements
2399 // so we don't hold a reference and cause the browser to retain them
2400 function garbageCollect() {
2401 if (!Ext.enableGarbageCollector) {
2402 clearInterval(El.collectorThreadId);
2410 if (!EC.hasOwnProperty(eid)) {
2414 if (o.skipGarbageCollection) {
2419 // -------------------------------------------------------
2420 // Determining what is garbage:
2421 // -------------------------------------------------------
2423 // dom node is null, definitely garbage
2424 // -------------------------------------------------------
2426 // no parentNode == direct orphan, definitely garbage
2427 // -------------------------------------------------------
2428 // !d.offsetParent && !document.getElementById(eid)
2429 // display none elements have no offsetParent so we will
2430 // also try to look it up by it's id. However, check
2431 // offsetParent first so we don't do unneeded lookups.
2432 // This enables collection of elements that are not orphans
2433 // directly, but somewhere up the line they have an orphan
2435 // -------------------------------------------------------
2436 if (!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))) {
2437 if (d && Ext.enableListenerCollection) {
2438 Ext.EventManager.removeAll(d);
2443 // Cleanup IE Object leaks
2447 if (!EC.hasOwnProperty(eid)) {
2456 El.collectorThreadId = setInterval(garbageCollect, 30000);
2458 var flyFn = function() {};
2459 flyFn.prototype = El.prototype;
2462 El.Flyweight = function(dom) {
2466 El.Flyweight.prototype = new flyFn();
2467 El.Flyweight.prototype.isFlyweight = true;
2468 El._flyweights = {};
2471 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
2472 * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
2473 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
2474 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
2475 * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
2476 * @param {String/HTMLElement} el The dom node or id
2477 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
2478 * (e.g. internally Ext uses "_global")
2479 * @return {Element} The shared Element object (or null if no matching element was found)
2480 * @member Ext.core.Element
2483 El.fly = function(el, named) {
2485 named = named || '_global';
2486 el = Ext.getDom(el);
2488 (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
2489 ret = El._flyweights[named];
2495 * Retrieves Ext.core.Element objects.
2496 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
2497 * retrieves Ext.core.Element objects which encapsulate DOM elements. To retrieve a Component by
2498 * its ID, use {@link Ext.ComponentManager#get}.</p>
2499 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
2500 * object was recreated with the same id via AJAX or DOM.</p>
2501 * Shorthand of {@link Ext.core.Element#get}
2502 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
2503 * @return {Element} The Element object (or null if no matching element was found)
2510 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
2511 * the dom node can be overwritten by other code. Shorthand of {@link Ext.core.Element#fly}</p>
2512 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
2513 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get Ext.get}
2514 * will be more appropriate to take advantage of the caching provided by the Ext.core.Element class.</p>
2515 * @param {String/HTMLElement} el The dom node or id
2516 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
2517 * (e.g. internally Ext uses "_global")
2518 * @return {Element} The shared Element object (or null if no matching element was found)
2524 // speedy lookup for elements never to box adjust
2525 var noBoxAdjust = Ext.isStrict ? {
2532 if (Ext.isIE || Ext.isGecko) {
2533 noBoxAdjust['button'] = 1;
2538 * @class Ext.core.Element
2540 Ext.core.Element.addMethods({
2542 * 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)
2543 * @param {String} selector The simple selector to test
2544 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
2545 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
2546 * @return {HTMLElement} The matching DOM node (or null if no match was found)
2548 findParent : function(simpleSelector, maxDepth, returnEl) {
2554 maxDepth = maxDepth || 50;
2555 if (isNaN(maxDepth)) {
2556 stopEl = Ext.getDom(maxDepth);
2557 maxDepth = Number.MAX_VALUE;
2559 while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
2560 if (Ext.DomQuery.is(p, simpleSelector)) {
2561 return returnEl ? Ext.get(p) : p;
2570 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
2571 * @param {String} selector The simple selector to test
2572 * @param {Number/Mixed} maxDepth (optional) The max depth to
2573 search as a number or element (defaults to 10 || document.body)
2574 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
2575 * @return {HTMLElement} The matching DOM node (or null if no match was found)
2577 findParentNode : function(simpleSelector, maxDepth, returnEl) {
2578 var p = Ext.fly(this.dom.parentNode, '_internal');
2579 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
2583 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
2584 * This is a shortcut for findParentNode() that always returns an Ext.core.Element.
2585 * @param {String} selector The simple selector to test
2586 * @param {Number/Mixed} maxDepth (optional) The max depth to
2587 search as a number or element (defaults to 10 || document.body)
2588 * @return {Ext.core.Element} The matching DOM node (or null if no match was found)
2590 up : function(simpleSelector, maxDepth) {
2591 return this.findParentNode(simpleSelector, maxDepth, true);
2595 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
2596 * @param {String} selector The CSS selector
2597 * @return {CompositeElement/CompositeElement} The composite element
2599 select : function(selector) {
2600 return Ext.core.Element.select(selector, false, this.dom);
2604 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
2605 * @param {String} selector The CSS selector
2606 * @return {Array} An array of the matched nodes
2608 query : function(selector) {
2609 return Ext.DomQuery.select(selector, this.dom);
2613 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
2614 * @param {String} selector The CSS selector
2615 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
2616 * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
2618 down : function(selector, returnDom) {
2619 var n = Ext.DomQuery.selectNode(selector, this.dom);
2620 return returnDom ? n : Ext.get(n);
2624 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
2625 * @param {String} selector The CSS selector
2626 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.core.Element (defaults to false)
2627 * @return {HTMLElement/Ext.core.Element} The child Ext.core.Element (or DOM node if returnDom = true)
2629 child : function(selector, returnDom) {
2633 id = Ext.get(me).id;
2635 id = id.replace(/[\.:]/g, "\\$0");
2636 node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
2637 return returnDom ? node : Ext.get(node);
2641 * Gets the parent node for this element, optionally chaining up trying to match a selector
2642 * @param {String} selector (optional) Find a parent node that matches the passed simple selector
2643 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2644 * @return {Ext.core.Element/HTMLElement} The parent node or null
2646 parent : function(selector, returnDom) {
2647 return this.matchNode('parentNode', 'parentNode', selector, returnDom);
2651 * Gets the next sibling, skipping text nodes
2652 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
2653 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2654 * @return {Ext.core.Element/HTMLElement} The next sibling or null
2656 next : function(selector, returnDom) {
2657 return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
2661 * Gets the previous sibling, skipping text nodes
2662 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
2663 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2664 * @return {Ext.core.Element/HTMLElement} The previous sibling or null
2666 prev : function(selector, returnDom) {
2667 return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
2672 * Gets the first child, skipping text nodes
2673 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
2674 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2675 * @return {Ext.core.Element/HTMLElement} The first child or null
2677 first : function(selector, returnDom) {
2678 return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
2682 * Gets the last child, skipping text nodes
2683 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
2684 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.core.Element
2685 * @return {Ext.core.Element/HTMLElement} The last child or null
2687 last : function(selector, returnDom) {
2688 return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
2691 matchNode : function(dir, start, selector, returnDom) {
2696 var n = this.dom[start];
2698 if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
2699 return !returnDom ? Ext.get(n) : n;
2708 * @class Ext.core.Element
2710 Ext.core.Element.addMethods({
2712 * Appends the passed element(s) to this element
2713 * @param {String/HTMLElement/Array/Element/CompositeElement} el
2714 * @return {Ext.core.Element} this
2716 appendChild : function(el) {
2717 return Ext.get(el).appendTo(this);
2721 * Appends this element to the passed element
2722 * @param {Mixed} el The new parent element
2723 * @return {Ext.core.Element} this
2725 appendTo : function(el) {
2726 Ext.getDom(el).appendChild(this.dom);
2731 * Inserts this element before the passed element in the DOM
2732 * @param {Mixed} el The element before which this element will be inserted
2733 * @return {Ext.core.Element} this
2735 insertBefore : function(el) {
2736 el = Ext.getDom(el);
2737 el.parentNode.insertBefore(this.dom, el);
2742 * Inserts this element after the passed element in the DOM
2743 * @param {Mixed} el The element to insert after
2744 * @return {Ext.core.Element} this
2746 insertAfter : function(el) {
2747 el = Ext.getDom(el);
2748 el.parentNode.insertBefore(this.dom, el.nextSibling);
2753 * Inserts (or creates) an element (or DomHelper config) as the first child of this element
2754 * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
2755 * @return {Ext.core.Element} The new child
2757 insertFirst : function(el, returnDom) {
2759 if (el.nodeType || el.dom || typeof el == 'string') { // element
2760 el = Ext.getDom(el);
2761 this.dom.insertBefore(el, this.dom.firstChild);
2762 return !returnDom ? Ext.get(el) : el;
2765 return this.createChild(el, this.dom.firstChild, returnDom);
2770 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
2771 * @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.
2772 * @param {String} where (optional) 'before' or 'after' defaults to before
2773 * @param {Boolean} returnDom (optional) True to return the .;ll;l,raw DOM element instead of Ext.core.Element
2774 * @return {Ext.core.Element} The inserted Element. If an array is passed, the last inserted element is returned.
2776 insertSibling: function(el, where, returnDom){
2778 isAfter = (where || 'before').toLowerCase() == 'after',
2781 if(Ext.isArray(el)){
2783 Ext.each(el, function(e) {
2784 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
2794 if(el.nodeType || el.dom){
2795 rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
2800 if (isAfter && !me.dom.nextSibling) {
2801 rt = Ext.core.DomHelper.append(me.dom.parentNode, el, !returnDom);
2803 rt = Ext.core.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
2810 * Replaces the passed element with this element
2811 * @param {Mixed} el The element to replace
2812 * @return {Ext.core.Element} this
2814 replace : function(el) {
2816 this.insertBefore(el);
2822 * Replaces this element with the passed element
2823 * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
2824 * @return {Ext.core.Element} this
2826 replaceWith: function(el){
2829 if(el.nodeType || el.dom || typeof el == 'string'){
2831 me.dom.parentNode.insertBefore(el, me.dom);
2833 el = Ext.core.DomHelper.insertBefore(me.dom, el);
2836 delete Ext.cache[me.id];
2837 Ext.removeNode(me.dom);
2838 me.id = Ext.id(me.dom = el);
2839 Ext.core.Element.addToCache(me.isFlyweight ? new Ext.core.Element(me.dom) : me);
2844 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
2845 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
2846 * automatically generated with the specified attributes.
2847 * @param {HTMLElement} insertBefore (optional) a child element of this element
2848 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
2849 * @return {Ext.core.Element} The new child element
2851 createChild : function(config, insertBefore, returnDom) {
2852 config = config || {tag:'div'};
2854 return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
2857 return Ext.core.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config, returnDom !== true);
2862 * Creates and wraps this element with another element
2863 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
2864 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.core.Element
2865 * @return {HTMLElement/Element} The newly created wrapper element
2867 wrap : function(config, returnDom) {
2868 var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom),
2869 d = newEl.dom || newEl;
2871 d.appendChild(this.dom);
2876 * Inserts an html fragment into this element
2877 * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
2878 * @param {String} html The HTML fragment
2879 * @param {Boolean} returnEl (optional) True to return an Ext.core.Element (defaults to false)
2880 * @return {HTMLElement/Ext.core.Element} The inserted node (or nearest related if more than 1 inserted)
2882 insertHtml : function(where, html, returnEl) {
2883 var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
2884 return returnEl ? Ext.get(el) : el;
2889 * @class Ext.core.Element
2892 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>';
2893 // local style camelizing for speed
2894 var supports = Ext.supports,
2895 view = document.defaultView,
2896 opacityRe = /alpha\(opacity=(.*)\)/i,
2897 trimRe = /^\s+|\s+$/g,
2900 adjustDirect2DTableRe = /table-row|table-.*-group/,
2901 INTERNAL = '_internal',
2902 PADDING = 'padding',
2912 ISCLIPPED = 'isClipped',
2913 OVERFLOW = 'overflow',
2914 OVERFLOWX = 'overflow-x',
2915 OVERFLOWY = 'overflow-y',
2916 ORIGINALCLIP = 'originalClip',
2917 // special markup used throughout Ext when box wrapping elements
2918 borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
2919 paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
2920 margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
2921 data = Ext.core.Element.data;
2923 Ext.override(Ext.core.Element, {
2926 * TODO: Look at this
2928 // private ==> used by Fx
2929 adjustWidth : function(width) {
2931 isNum = (typeof width == 'number');
2933 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
2934 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
2936 return (isNum && width < 0) ? 0 : width;
2939 // private ==> used by Fx
2940 adjustHeight : function(height) {
2942 isNum = (typeof height == "number");
2944 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
2945 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
2947 return (isNum && height < 0) ? 0 : height;
2952 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
2953 * @param {String/Array} className The CSS classes to add separated by space, or an array of classes
2954 * @return {Ext.core.Element} this
2956 addCls : function(className){
2959 space = ((me.dom.className.replace(trimRe, '') == '') ? "" : " "),
2961 if (!Ext.isDefined(className)) {
2964 // Separate case is for speed
2965 if (!Ext.isArray(className)) {
2966 if (typeof className === 'string') {
2967 className = className.replace(trimRe, '').split(spacesRe);
2968 if (className.length === 1) {
2969 className = className[0];
2970 if (!me.hasCls(className)) {
2971 me.dom.className += space + className;
2974 this.addCls(className);
2978 for (i = 0, len = className.length; i < len; i++) {
2980 if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
2985 me.dom.className += space + cls.join(" ");
2992 * Removes one or more CSS classes from the element.
2993 * @param {String/Array} className The CSS classes to remove separated by space, or an array of classes
2994 * @return {Ext.core.Element} this
2996 removeCls : function(className){
2998 i, idx, len, cls, elClasses;
2999 if (!Ext.isDefined(className)) {
3002 if (!Ext.isArray(className)){
3003 className = className.replace(trimRe, '').split(spacesRe);
3005 if (me.dom && me.dom.className) {
3006 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
3007 for (i = 0, len = className.length; i < len; i++) {
3009 if (typeof cls == 'string') {
3010 cls = cls.replace(trimRe, '');
3011 idx = Ext.Array.indexOf(elClasses, cls);
3013 elClasses.splice(idx, 1);
3017 me.dom.className = elClasses.join(" ");
3023 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
3024 * @param {String/Array} className The CSS class to add, or an array of classes
3025 * @return {Ext.core.Element} this
3027 radioCls : function(className){
3028 var cn = this.dom.parentNode.childNodes,
3030 className = Ext.isArray(className) ? className : [className];
3031 for (i = 0, len = cn.length; i < len; i++) {
3033 if (v && v.nodeType == 1) {
3034 Ext.fly(v, '_internal').removeCls(className);
3037 return this.addCls(className);
3041 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
3042 * @param {String} className The CSS class to toggle
3043 * @return {Ext.core.Element} this
3046 toggleCls : Ext.supports.ClassList ?
3047 function(className) {
3048 this.dom.classList.toggle(Ext.String.trim(className));
3051 function(className) {
3052 return this.hasCls(className) ? this.removeCls(className) : this.addCls(className);
3056 * Checks if the specified CSS class exists on this element's DOM node.
3057 * @param {String} className The CSS class to check for
3058 * @return {Boolean} True if the class exists, else false
3061 hasCls : Ext.supports.ClassList ?
3062 function(className) {
3066 className = className.split(spacesRe);
3067 var ln = className.length,
3069 for (; i < ln; i++) {
3070 if (className[i] && this.dom.classList.contains(className[i])) {
3076 function(className){
3077 return className && (' ' + this.dom.className + ' ').indexOf(' ' + className + ' ') != -1;
3081 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
3082 * @param {String} oldClassName The CSS class to replace
3083 * @param {String} newClassName The replacement CSS class
3084 * @return {Ext.core.Element} this
3086 replaceCls : function(oldClassName, newClassName){
3087 return this.removeCls(oldClassName).addCls(newClassName);
3090 isStyle : function(style, val) {
3091 return this.getStyle(style) == val;
3095 * Normalizes currentStyle and computedStyle.
3096 * @param {String} property The style property whose value is returned.
3097 * @return {String} The current value of the style property for this element.
3100 getStyle : function(){
3101 return view && view.getComputedStyle ?
3104 v, cs, out, display, cleaner;
3109 prop = Ext.core.Element.normalize(prop);
3110 out = (v = el.style[prop]) ? v :
3111 (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
3113 // Ignore cases when the margin is correctly reported as 0, the bug only shows
3115 if(prop == 'marginRight' && out != '0px' && !supports.RightMargin){
3116 cleaner = Ext.core.Element.getRightMarginFixCleaner(el);
3117 display = this.getStyle('display');
3118 el.style.display = 'inline-block';
3119 out = view.getComputedStyle(el, '').marginRight;
3120 el.style.display = display;
3124 if(prop == 'backgroundColor' && out == 'rgba(0, 0, 0, 0)' && !supports.TransparentColor){
3125 out = 'transparent';
3133 if (el == document) {
3137 if (prop == 'opacity') {
3138 if (el.style.filter.match) {
3139 m = el.style.filter.match(opacityRe);
3141 var fv = parseFloat(m[1]);
3143 return fv ? fv / 100 : 0;
3149 prop = Ext.core.Element.normalize(prop);
3150 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
3155 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
3156 * are convert to standard 6 digit hex color.
3157 * @param {String} attr The css attribute
3158 * @param {String} defaultValue The default value to use when a valid color isn't found
3159 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
3162 getColor : function(attr, defaultValue, prefix){
3163 var v = this.getStyle(attr),
3164 color = prefix || prefix === '' ? prefix : '#',
3167 if(!v || (/transparent|inherit/.test(v))) {
3168 return defaultValue;
3171 Ext.each(v.slice(4, v.length -1).split(','), function(s){
3172 h = parseInt(s, 10);
3173 color += (h < 16 ? '0' : '') + h.toString(16);
3176 v = v.replace('#', '');
3177 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
3179 return(color.length > 5 ? color.toLowerCase() : defaultValue);
3183 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
3184 * @param {String/Object} property The style property to be set, or an object of multiple styles.
3185 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
3186 * @return {Ext.core.Element} this
3188 setStyle : function(prop, value){
3196 if (!Ext.isObject(prop)) {
3201 for (style in prop) {
3202 if (prop.hasOwnProperty(style)) {
3203 value = Ext.value(prop[style], '');
3204 if (style == 'opacity') {
3205 me.setOpacity(value);
3208 me.dom.style[Ext.core.Element.normalize(style)] = value;
3216 * Set the opacity of the element
3217 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
3218 * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
3219 * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
3220 * @return {Ext.core.Element} this
3222 setOpacity: function(opacity, animate) {
3232 style = me.dom.style;
3234 if (!animate || !me.anim) {
3235 if (!Ext.supports.Opacity) {
3236 opacity = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')': '';
3237 val = style.filter.replace(opacityRe, '').replace(trimRe, '');
3240 style.filter = val + (val.length > 0 ? ' ': '') + opacity;
3243 style.opacity = opacity;
3247 if (!Ext.isObject(animate)) {
3253 me.animate(Ext.applyIf({
3265 * Clears any opacity settings from this element. Required in some cases for IE.
3266 * @return {Ext.core.Element} this
3268 clearOpacity : function(){
3269 var style = this.dom.style;
3270 if(!Ext.supports.Opacity){
3271 if(!Ext.isEmpty(style.filter)){
3272 style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
3275 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
3282 * Returns 1 if the browser returns the subpixel dimension rounded to the lowest pixel.
3283 * @return {Number} 0 or 1
3285 adjustDirect2DDimension: function(dimension) {
3288 display = me.getStyle('display'),
3289 inlineDisplay = dom.style['display'],
3290 inlinePosition = dom.style['position'],
3291 originIndex = dimension === 'width' ? 0 : 1,
3294 if (display === 'inline') {
3295 dom.style['display'] = 'inline-block';
3298 dom.style['position'] = display.match(adjustDirect2DTableRe) ? 'absolute' : 'static';
3300 // floating will contain digits that appears after the decimal point
3301 // if height or width are set to auto we fallback to msTransformOrigin calculation
3302 floating = (parseFloat(me.getStyle(dimension)) || parseFloat(dom.currentStyle.msTransformOrigin.split(' ')[originIndex]) * 2) % 1;
3304 dom.style['position'] = inlinePosition;
3306 if (display === 'inline') {
3307 dom.style['display'] = inlineDisplay;
3314 * Returns the offset height of the element
3315 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
3316 * @return {Number} The element's height
3318 getHeight: function(contentHeight, preciseHeight) {
3321 hidden = Ext.isIE && me.isStyle('display', 'none'),
3322 height, overflow, style, floating;
3324 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3325 // We will put the overflow back to it's original value when we are done measuring.
3326 if (Ext.isIEQuirks) {
3328 overflow = style.overflow;
3329 me.setStyle({ overflow: 'hidden'});
3332 height = dom.offsetHeight;
3334 height = MATH.max(height, hidden ? 0 : dom.clientHeight) || 0;
3336 // IE9 Direct2D dimension rounding bug
3337 if (!hidden && Ext.supports.Direct2DBug) {
3338 floating = me.adjustDirect2DDimension('height');
3339 if (preciseHeight) {
3342 else if (floating > 0 && floating < 0.5) {
3347 if (contentHeight) {
3348 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
3351 if (Ext.isIEQuirks) {
3352 me.setStyle({ overflow: overflow});
3362 * Returns the offset width of the element
3363 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
3364 * @return {Number} The element's width
3366 getWidth: function(contentWidth, preciseWidth) {
3369 hidden = Ext.isIE && me.isStyle('display', 'none'),
3370 rect, width, overflow, style, floating, parentPosition;
3372 // IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3373 // We will put the overflow back to it's original value when we are done measuring.
3374 if (Ext.isIEQuirks) {
3376 overflow = style.overflow;
3377 me.setStyle({overflow: 'hidden'});
3380 // Fix Opera 10.5x width calculation issues
3381 if (Ext.isOpera10_5) {
3382 if (dom.parentNode.currentStyle.position === 'relative') {
3383 parentPosition = dom.parentNode.style.position;
3384 dom.parentNode.style.position = 'static';
3385 width = dom.offsetWidth;
3386 dom.parentNode.style.position = parentPosition;
3388 width = Math.max(width || 0, dom.offsetWidth);
3390 // Gecko will in some cases report an offsetWidth that is actually less than the width of the
3391 // text contents, because it measures fonts with sub-pixel precision but rounds the calculated
3392 // value down. Using getBoundingClientRect instead of offsetWidth allows us to get the precise
3393 // subpixel measurements so we can force them to always be rounded up. See
3394 // https://bugzilla.mozilla.org/show_bug.cgi?id=458617
3395 } else if (Ext.supports.BoundingClientRect) {
3396 rect = dom.getBoundingClientRect();
3397 width = rect.right - rect.left;
3398 width = preciseWidth ? width : Math.ceil(width);
3400 width = dom.offsetWidth;
3403 width = MATH.max(width, hidden ? 0 : dom.clientWidth) || 0;
3405 // IE9 Direct2D dimension rounding bug
3406 if (!hidden && Ext.supports.Direct2DBug) {
3407 floating = me.adjustDirect2DDimension('width');
3411 else if (floating > 0 && floating < 0.5) {
3417 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
3420 if (Ext.isIEQuirks) {
3421 me.setStyle({ overflow: overflow});
3431 * Set the width of this Element.
3432 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
3433 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3434 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
3436 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3437 * @return {Ext.core.Element} this
3439 setWidth : function(width, animate){
3441 width = me.adjustWidth(width);
3442 if (!animate || !me.anim) {
3443 me.dom.style.width = me.addUnits(width);
3446 if (!Ext.isObject(animate)) {
3449 me.animate(Ext.applyIf({
3459 * Set the height of this Element.
3461 // change the height to 200px and animate with default configuration
3462 Ext.fly('elementId').setHeight(200, true);
3464 // change the height to 150px and animate with a custom configuration
3465 Ext.fly('elId').setHeight(150, {
3466 duration : .5, // animation will have a duration of .5 seconds
3467 // will change the content to "finished"
3468 callback: function(){ this.{@link #update}("finished"); }
3471 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
3472 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
3473 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
3475 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3476 * @return {Ext.core.Element} this
3478 setHeight : function(height, animate){
3480 height = me.adjustHeight(height);
3481 if (!animate || !me.anim) {
3482 me.dom.style.height = me.addUnits(height);
3485 if (!Ext.isObject(animate)) {
3488 me.animate(Ext.applyIf({
3498 * Gets the width of the border(s) for the specified side(s)
3499 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
3500 * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
3501 * @return {Number} The width of the sides passed added together
3503 getBorderWidth : function(side){
3504 return this.addStyles(side, borders);
3508 * Gets the width of the padding(s) for the specified side(s)
3509 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
3510 * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
3511 * @return {Number} The padding of the sides passed added together
3513 getPadding : function(side){
3514 return this.addStyles(side, paddings);
3518 * Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
3519 * @return {Ext.core.Element} this
3525 if(!data(dom, ISCLIPPED)){
3526 data(dom, ISCLIPPED, true);
3527 data(dom, ORIGINALCLIP, {
3528 o: me.getStyle(OVERFLOW),
3529 x: me.getStyle(OVERFLOWX),
3530 y: me.getStyle(OVERFLOWY)
3532 me.setStyle(OVERFLOW, HIDDEN);
3533 me.setStyle(OVERFLOWX, HIDDEN);
3534 me.setStyle(OVERFLOWY, HIDDEN);
3540 * Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
3541 * @return {Ext.core.Element} this
3543 unclip : function(){
3548 if(data(dom, ISCLIPPED)){
3549 data(dom, ISCLIPPED, false);
3550 clip = data(dom, ORIGINALCLIP);
3552 me.setStyle(OVERFLOW, o.o);
3555 me.setStyle(OVERFLOWX, o.x);
3558 me.setStyle(OVERFLOWY, o.y);
3565 addStyles : function(sides, styles){
3567 sidesArr = sides.match(wordsRe),
3569 len = sidesArr.length,
3571 for (; i < len; i++) {
3573 size = side && parseInt(this.getStyle(styles[side]), 10);
3575 totalSize += MATH.abs(size);
3584 * More flexible version of {@link #setStyle} for setting style properties.
3585 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
3586 * a function which returns such a specification.
3587 * @return {Ext.core.Element} this
3589 applyStyles : function(style){
3590 Ext.core.DomHelper.applyStyles(this.dom, style);
3595 * Returns an object with properties matching the styles requested.
3596 * For example, el.getStyles('color', 'font-size', 'width') might return
3597 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
3598 * @param {String} style1 A style name
3599 * @param {String} style2 A style name
3600 * @param {String} etc.
3601 * @return {Object} The style object
3603 getStyles : function(){
3605 len = arguments.length,
3608 for(; i < len; ++i) {
3609 style = arguments[i];
3610 styles[style] = this.getStyle(style);
3616 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
3617 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
3618 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.button.Button},
3619 * {@link Ext.panel.Panel} when <tt>{@link Ext.panel.Panel#frame frame=true}</tt>, {@link Ext.window.Window}). The markup
3620 * is of this form:</p>
3622 Ext.core.Element.boxMarkup =
3623 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
3624 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
3625 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
3627 * <p>Example usage:</p>
3630 Ext.get("foo").boxWrap();
3632 // You can also add a custom class and use CSS inheritance rules to customize the box look.
3633 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
3634 // for how to create a custom box wrap style.
3635 Ext.get("foo").boxWrap().addCls("x-box-blue");
3637 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
3638 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
3639 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
3640 * also supply all of the necessary rules.
3641 * @return {Ext.core.Element} The outermost wrapping element of the created box structure.
3643 boxWrap : function(cls){
3644 cls = cls || Ext.baseCSSPrefix + 'box';
3645 var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + Ext.String.format(Ext.core.Element.boxMarkup, cls) + "</div>"));
3646 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
3651 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
3652 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
3653 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3654 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
3655 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
3657 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
3658 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
3659 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
3661 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3662 * @return {Ext.core.Element} this
3664 setSize : function(width, height, animate){
3666 if (Ext.isObject(width)){ // in case of object from getSize()
3667 height = width.height;
3668 width = width.width;
3670 width = me.adjustWidth(width);
3671 height = me.adjustHeight(height);
3672 if(!animate || !me.anim){
3673 me.dom.style.width = me.addUnits(width);
3674 me.dom.style.height = me.addUnits(height);
3677 if (!Ext.isObject(animate)) {
3680 me.animate(Ext.applyIf({
3691 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
3692 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
3693 * if a height has not been set using CSS.
3696 getComputedHeight : function(){
3698 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
3700 h = parseFloat(me.getStyle('height')) || 0;
3701 if(!me.isBorderBox()){
3702 h += me.getFrameWidth('tb');
3709 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
3710 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
3711 * if a width has not been set using CSS.
3714 getComputedWidth : function(){
3716 w = Math.max(me.dom.offsetWidth, me.dom.clientWidth);
3719 w = parseFloat(me.getStyle('width')) || 0;
3720 if(!me.isBorderBox()){
3721 w += me.getFrameWidth('lr');
3728 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
3729 for more information about the sides.
3730 * @param {String} sides
3733 getFrameWidth : function(sides, onlyContentBox){
3734 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
3738 * Sets up event handlers to add and remove a css class when the mouse is over this element
3739 * @param {String} className
3740 * @return {Ext.core.Element} this
3742 addClsOnOver : function(className){
3746 Ext.fly(dom, INTERNAL).addCls(className);
3749 Ext.fly(dom, INTERNAL).removeCls(className);
3756 * Sets up event handlers to add and remove a css class when this element has the focus
3757 * @param {String} className
3758 * @return {Ext.core.Element} this
3760 addClsOnFocus : function(className){
3763 me.on("focus", function(){
3764 Ext.fly(dom, INTERNAL).addCls(className);
3766 me.on("blur", function(){
3767 Ext.fly(dom, INTERNAL).removeCls(className);
3773 * 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)
3774 * @param {String} className
3775 * @return {Ext.core.Element} this
3777 addClsOnClick : function(className){
3779 this.on("mousedown", function(){
3780 Ext.fly(dom, INTERNAL).addCls(className);
3781 var d = Ext.getDoc(),
3783 Ext.fly(dom, INTERNAL).removeCls(className);
3784 d.removeListener("mouseup", fn);
3786 d.on("mouseup", fn);
3792 * <p>Returns the dimensions of the element available to lay content out in.<p>
3793 * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
3794 * example:<pre><code>
3795 var vpSize = Ext.getBody().getViewSize();
3797 // all Windows created afterwards will have a default value of 90% height and 95% width
3798 Ext.Window.override({
3799 width: vpSize.width * 0.9,
3800 height: vpSize.height * 0.95
3802 // To handle window resizing you would have to hook onto onWindowResize.
3805 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
3806 * To obtain the size including scrollbars, use getStyleSize
3808 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
3811 getViewSize : function(){
3814 isDoc = (dom == Ext.getDoc().dom || dom == Ext.getBody().dom),
3815 style, overflow, ret;
3817 // If the body, use static methods
3820 width : Ext.core.Element.getViewWidth(),
3821 height : Ext.core.Element.getViewHeight()
3824 // Else use clientHeight/clientWidth
3827 // IE 6 & IE Quirks mode acts more like a max-size measurement unless overflow is hidden during measurement.
3828 // We will put the overflow back to it's original value when we are done measuring.
3829 if (Ext.isIE6 || Ext.isIEQuirks) {
3831 overflow = style.overflow;
3832 me.setStyle({ overflow: 'hidden'});
3835 width : dom.clientWidth,
3836 height : dom.clientHeight
3838 if (Ext.isIE6 || Ext.isIEQuirks) {
3839 me.setStyle({ overflow: overflow });
3846 * <p>Returns the dimensions of the element available to lay content out in.<p>
3848 * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
3849 * To obtain the size excluding scrollbars, use getViewSize
3851 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
3854 getStyleSize : function(){
3858 isDoc = (d == doc || d == doc.body),
3862 // If the body, use static methods
3865 width : Ext.core.Element.getViewWidth(),
3866 height : Ext.core.Element.getViewHeight()
3869 // Use Styles if they are set
3870 if(s.width && s.width != 'auto'){
3871 w = parseFloat(s.width);
3872 if(me.isBorderBox()){
3873 w -= me.getFrameWidth('lr');
3876 // Use Styles if they are set
3877 if(s.height && s.height != 'auto'){
3878 h = parseFloat(s.height);
3879 if(me.isBorderBox()){
3880 h -= me.getFrameWidth('tb');
3883 // Use getWidth/getHeight if style not set.
3884 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
3888 * Returns the size of the element.
3889 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
3890 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
3892 getSize : function(contentSize){
3893 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
3897 * Forces the browser to repaint this element
3898 * @return {Ext.core.Element} this
3900 repaint : function(){
3902 this.addCls(Ext.baseCSSPrefix + 'repaint');
3903 setTimeout(function(){
3904 Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
3910 * Disables text selection for this element (normalized across browsers)
3911 * @return {Ext.core.Element} this
3913 unselectable : function(){
3915 me.dom.unselectable = "on";
3917 me.swallowEvent("selectstart", true);
3918 me.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
3919 me.addCls(Ext.baseCSSPrefix + 'unselectable');
3925 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
3926 * then it returns the calculated width of the sides (see getPadding)
3927 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
3928 * @return {Object/Number}
3930 getMargin : function(side){
3932 hash = {t:"top", l:"left", r:"right", b: "bottom"},
3937 for (key in me.margins){
3938 o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
3942 return me.addStyles.call(me, side, me.margins);
3948 * @class Ext.core.Element
3951 * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
3955 Ext.core.Element.VISIBILITY = 1;
3957 * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
3961 Ext.core.Element.DISPLAY = 2;
3964 * Visibility mode constant for use with {@link #setVisibilityMode}. Use offsets (x and y positioning offscreen)
3969 Ext.core.Element.OFFSETS = 3;
3972 Ext.core.Element.ASCLASS = 4;
3975 * Defaults to 'x-hide-nosize'
3979 Ext.core.Element.visibilityCls = Ext.baseCSSPrefix + 'hide-nosize';
3981 Ext.core.Element.addMethods(function(){
3982 var El = Ext.core.Element,
3983 OPACITY = "opacity",
3984 VISIBILITY = "visibility",
3985 DISPLAY = "display",
3987 OFFSETS = "offsets",
3988 ASCLASS = "asclass",
3991 ORIGINALDISPLAY = 'originalDisplay',
3992 VISMODE = 'visibilityMode',
3993 ISVISIBLE = 'isVisible',
3995 getDisplay = function(dom){
3996 var d = data(dom, ORIGINALDISPLAY);
3997 if(d === undefined){
3998 data(dom, ORIGINALDISPLAY, d = '');
4002 getVisMode = function(dom){
4003 var m = data(dom, VISMODE);
4004 if(m === undefined){
4005 data(dom, VISMODE, m = 1);
4012 * The element's default display mode (defaults to "")
4015 originalDisplay : "",
4019 * Sets the element's visibility mode. When setVisible() is called it
4020 * will use this to determine whether to set the visibility or the display property.
4021 * @param {Number} visMode Ext.core.Element.VISIBILITY or Ext.core.Element.DISPLAY
4022 * @return {Ext.core.Element} this
4024 setVisibilityMode : function(visMode){
4025 data(this.dom, VISMODE, visMode);
4030 * Checks whether the element is currently visible using both visibility and display properties.
4031 * @return {Boolean} True if the element is currently visible, else false
4033 isVisible : function() {
4036 visible = data(dom, ISVISIBLE);
4038 if(typeof visible == 'boolean'){ //return the cached value if registered
4041 //Determine the current state based on display states
4042 visible = !me.isStyle(VISIBILITY, HIDDEN) &&
4043 !me.isStyle(DISPLAY, NONE) &&
4044 !((getVisMode(dom) == El.ASCLASS) && me.hasCls(me.visibilityCls || El.visibilityCls));
4046 data(dom, ISVISIBLE, visible);
4051 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
4052 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
4053 * @param {Boolean} visible Whether the element is visible
4054 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4055 * @return {Ext.core.Element} this
4057 setVisible : function(visible, animate){
4058 var me = this, isDisplay, isVisibility, isOffsets, isNosize,
4060 visMode = getVisMode(dom);
4063 // hideMode string override
4064 if (typeof animate == 'string'){
4067 visMode = El.DISPLAY;
4070 visMode = El.VISIBILITY;
4073 visMode = El.OFFSETS;
4077 visMode = El.ASCLASS;
4080 me.setVisibilityMode(visMode);
4084 if (!animate || !me.anim) {
4085 if(visMode == El.ASCLASS ){
4087 me[visible?'removeCls':'addCls'](me.visibilityCls || El.visibilityCls);
4089 } else if (visMode == El.DISPLAY){
4091 return me.setDisplayed(visible);
4093 } else if (visMode == El.OFFSETS){
4096 // Remember position for restoring, if we are not already hidden by offsets.
4097 if (!me.hideModeStyles) {
4098 me.hideModeStyles = {
4099 position: me.getStyle('position'),
4100 top: me.getStyle('top'),
4101 left: me.getStyle('left')
4104 me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
4107 // Only "restore" as position if we have actually been hidden using offsets.
4108 // Calling setVisible(true) on a positioned element should not reposition it.
4109 else if (me.hideModeStyles) {
4110 me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
4111 delete me.hideModeStyles;
4116 // Show by clearing visibility style. Explicitly setting to "visible" overrides parent visibility setting.
4117 dom.style.visibility = visible ? '' : HIDDEN;
4120 // closure for composites
4122 me.setOpacity(0.01);
4123 me.setVisible(true);
4125 if (!Ext.isObject(animate)) {
4131 me.animate(Ext.applyIf({
4132 callback: function() {
4133 visible || me.setVisible(false).setOpacity(1);
4136 opacity: (visible) ? 1 : 0
4140 data(dom, ISVISIBLE, visible); //set logical visibility state
4147 * Determine if the Element has a relevant height and width available based
4148 * upon current logical visibility state
4150 hasMetrics : function(){
4152 return this.isVisible() || (getVisMode(dom) == El.OFFSETS) || (getVisMode(dom) == El.VISIBILITY);
4156 * Toggles the element's visibility or display, depending on visibility mode.
4157 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
4158 * @return {Ext.core.Element} this
4160 toggle : function(animate){
4162 me.setVisible(!me.isVisible(), me.anim(animate));
4167 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
4168 * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
4169 * @return {Ext.core.Element} this
4171 setDisplayed : function(value) {
4172 if(typeof value == "boolean"){
4173 value = value ? getDisplay(this.dom) : NONE;
4175 this.setStyle(DISPLAY, value);
4180 fixDisplay : function(){
4182 if (me.isStyle(DISPLAY, NONE)) {
4183 me.setStyle(VISIBILITY, HIDDEN);
4184 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
4185 if (me.isStyle(DISPLAY, NONE)) { // if that fails, default to block
4186 me.setStyle(DISPLAY, "block");
4192 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
4193 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
4194 * @return {Ext.core.Element} this
4196 hide : function(animate){
4197 // hideMode override
4198 if (typeof animate == 'string'){
4199 this.setVisible(false, animate);
4202 this.setVisible(false, this.anim(animate));
4207 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
4208 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
4209 * @return {Ext.core.Element} this
4211 show : function(animate){
4212 // hideMode override
4213 if (typeof animate == 'string'){
4214 this.setVisible(true, animate);
4217 this.setVisible(true, this.anim(animate));
4223 * @class Ext.core.Element
4225 Ext.applyIf(Ext.core.Element.prototype, {
4226 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
4227 animate: function(config) {
4230 me = Ext.get(me.dom);
4232 if (Ext.fx.Manager.hasFxBlock(me.id)) {
4235 Ext.fx.Manager.queueFx(Ext.create('Ext.fx.Anim', me.anim(config)));
4239 // @private override base Ext.util.Animate mixin for animate for backwards compatibility
4240 anim: function(config) {
4241 if (!Ext.isObject(config)) {
4242 return (config) ? {} : false;
4246 duration = config.duration || Ext.fx.Anim.prototype.duration,
4247 easing = config.easing || 'ease',
4250 if (config.stopAnimation) {
4254 Ext.applyIf(config, Ext.fx.Manager.getFxDefaults(me.id));
4256 // Clear any 'paused' defaults.
4257 Ext.fx.Manager.setFxDefaults(me.id, {
4263 remove: config.remove,
4264 alternate: config.alternate || false,
4267 callback: config.callback,
4268 listeners: config.listeners,
4269 iterations: config.iterations || 1,
4270 scope: config.scope,
4271 block: config.block,
4272 concurrent: config.concurrent,
4273 delay: config.delay || 0,
4275 keyframes: config.keyframes,
4276 from: config.from || {},
4277 to: Ext.apply({}, config)
4279 Ext.apply(animConfig.to, config.to);
4281 // Anim API properties - backward compat
4282 delete animConfig.to.to;
4283 delete animConfig.to.from;
4284 delete animConfig.to.remove;
4285 delete animConfig.to.alternate;
4286 delete animConfig.to.keyframes;
4287 delete animConfig.to.iterations;
4288 delete animConfig.to.listeners;
4289 delete animConfig.to.target;
4290 delete animConfig.to.paused;
4291 delete animConfig.to.callback;
4292 delete animConfig.to.scope;
4293 delete animConfig.to.duration;
4294 delete animConfig.to.easing;
4295 delete animConfig.to.concurrent;
4296 delete animConfig.to.block;
4297 delete animConfig.to.stopAnimation;
4298 delete animConfig.to.delay;
4303 * Slides the element into view. An anchor point can be optionally passed to set the point of
4304 * origin for the slide effect. This function automatically handles wrapping the element with
4305 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
4308 // default: slide the element in from the top
4311 // custom: slide the element in from the right with a 2-second duration
4312 el.slideIn('r', { duration: 2 });
4314 // common config options shown with default values
4320 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
4321 * @param {Object} options (optional) Object literal with any of the Fx config options
4322 * @return {Ext.core.Element} The Element
4324 slideIn: function(anchor, obj, slideOut) {
4326 elStyle = me.dom.style,
4327 beforeAnim, wrapAnim;
4329 anchor = anchor || "t";
4332 beforeAnim = function() {
4333 var animScope = this,
4334 listeners = obj.listeners,
4335 box, position, restoreSize, wrap, anim;
4342 if ((anchor == 't' || anchor == 'b') && box.height == 0) {
4343 box.height = me.dom.scrollHeight;
4345 else if ((anchor == 'l' || anchor == 'r') && box.width == 0) {
4346 box.width = me.dom.scrollWidth;
4349 position = me.getPositioning();
4350 me.setSize(box.width, box.height);
4354 visibility: slideOut ? 'visible' : 'hidden'
4357 wrap.setPositioning(position);
4358 if (wrap.isStyle('position', 'static')) {
4359 wrap.position('relative');
4361 me.clearPositioning('auto');
4364 // This element is temporarily positioned absolute within its wrapper.
4365 // Restore to its default, CSS-inherited visibility setting.
4366 // We cannot explicitly poke visibility:visible into its style because that overrides the visibility of the wrap.
4369 position: 'absolute'
4372 wrap.setSize(box.width, box.height);
4379 width: box.width + 'px',
4383 width: box.width + 'px',
4384 height: box.height + 'px'
4387 elStyle.bottom = '0px';
4393 height: box.height + 'px'
4396 width: box.width + 'px',
4397 height: box.height + 'px'
4400 elStyle.right = '0px';
4405 x: box.x + box.width,
4407 height: box.height + 'px'
4411 width: box.width + 'px',
4412 height: box.height + 'px'
4419 y: box.y + box.height,
4420 width: box.width + 'px',
4425 width: box.width + 'px',
4426 height: box.height + 'px'
4439 width: box.width + 'px',
4440 height: box.height + 'px'
4443 elStyle.bottom = '0px';
4444 elStyle.right = '0px';
4449 x: box.x + box.width,
4455 width: box.width + 'px',
4456 height: box.height + 'px'
4459 elStyle.right = '0px';
4464 x: box.x + box.width,
4465 y: box.y + box.height,
4472 width: box.width + 'px',
4473 height: box.height + 'px'
4480 y: box.y + box.height,
4486 width: box.width + 'px',
4487 height: box.height + 'px'
4490 elStyle.bottom = '0px';
4495 wrapAnim = Ext.apply({}, obj);
4496 delete wrapAnim.listeners;
4497 wrapAnim = Ext.create('Ext.fx.Anim', Ext.applyIf(wrapAnim, {
4501 from: slideOut ? anim.to : anim.from,
4502 to: slideOut ? anim.from : anim.to
4505 // In the absence of a callback, this listener MUST be added first
4506 wrapAnim.on('afteranimate', function() {
4508 me.setPositioning(position);
4509 if (obj.useDisplay) {
4510 me.setDisplayed(false);
4516 me.clearPositioning();
4517 me.setPositioning(position);
4520 wrap.dom.parentNode.insertBefore(me.dom, wrap.dom);
4523 me.setSize(box.width, box.height);
4526 // Add configured listeners after
4528 wrapAnim.on(listeners);
4533 duration: obj.duration ? obj.duration * 2 : 1000,
4540 if (wrapAnim && wrapAnim.running) {
4552 * Slides the element out of view. An anchor point can be optionally passed to set the end point
4553 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
4554 * 'hidden') but block elements will still take up space in the document. The element must be removed
4555 * from the DOM using the 'remove' config option if desired. This function automatically handles
4556 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
4559 // default: slide the element out to the top
4562 // custom: slide the element out to the right with a 2-second duration
4563 el.slideOut('r', { duration: 2 });
4565 // common config options shown with default values
4573 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
4574 * @param {Object} options (optional) Object literal with any of the Fx config options
4575 * @return {Ext.core.Element} The Element
4577 slideOut: function(anchor, o) {
4578 return this.slideIn(anchor, o, true);
4582 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
4583 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
4589 // common config options shown with default values
4596 * @param {Object} options (optional) Object literal with any of the Fx config options
4597 * @return {Ext.core.Element} The Element
4600 puff: function(obj) {
4603 obj = Ext.applyIf(obj || {}, {
4609 beforeAnim = function() {
4613 var box = me.getBox(),
4614 fontSize = me.getStyle('fontSize'),
4615 position = me.getPositioning();
4617 width: box.width * 2,
4618 height: box.height * 2,
4619 x: box.x - (box.width / 2),
4620 y: box.y - (box.height /2),
4624 this.on('afteranimate',function() {
4626 if (obj.useDisplay) {
4627 me.setDisplayed(false);
4632 me.setPositioning(position);
4633 me.setStyle({fontSize: fontSize});
4639 duration: obj.duration,
4651 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
4652 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
4653 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
4659 // all config options shown with default values
4667 * @param {Object} options (optional) Object literal with any of the Fx config options
4668 * @return {Ext.core.Element} The Element
4670 switchOff: function(obj) {
4674 obj = Ext.applyIf(obj || {}, {
4681 beforeAnim = function() {
4682 var animScope = this,
4683 size = me.getSize(),
4688 position = me.getPositioning();
4690 keyframe = Ext.create('Ext.fx.Animator', {
4692 duration: obj.duration,
4700 y: xy[1] + size.height / 2
4704 x: xy[0] + size.width / 2
4708 keyframe.on('afteranimate', function() {
4709 if (obj.useDisplay) {
4710 me.setDisplayed(false);
4715 me.setPositioning(position);
4721 duration: (obj.duration * 2),
4732 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
4735 // default: a single light blue ripple
4738 // custom: 3 red ripples lasting 3 seconds total
4739 el.frame("#ff0000", 3, { duration: 3 });
4741 // common config options shown with default values
4742 el.frame("#C3DAF9", 1, {
4743 duration: 1 //duration of each individual ripple.
4744 // Note: Easing is not configurable and will be ignored if included
4747 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
4748 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
4749 * @param {Object} options (optional) Object literal with any of the Fx config options
4750 * @return {Ext.core.Element} The Element
4752 frame : function(color, count, obj){
4756 color = color || '#C3DAF9';
4760 beforeAnim = function() {
4762 var animScope = this,
4764 proxy = Ext.getBody().createChild({
4766 position : 'absolute',
4767 'pointer-events': 'none',
4769 border : '0px solid ' + color
4773 proxyAnim = Ext.create('Ext.fx.Anim', {
4775 duration: obj.duration || 1000,
4790 height: box.height + 40,
4791 width: box.width + 40
4794 proxyAnim.on('afteranimate', function() {
4801 duration: (obj.duration * 2) || 2000,
4812 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
4813 * ending point of the effect.
4816 // default: slide the element downward while fading out
4819 // custom: slide the element out to the right with a 2-second duration
4820 el.ghost('r', { duration: 2 });
4822 // common config options shown with default values
4828 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
4829 * @param {Object} options (optional) Object literal with any of the Fx config options
4830 * @return {Ext.core.Element} The Element
4832 ghost: function(anchor, obj) {
4836 anchor = anchor || "b";
4837 beforeAnim = function() {
4838 var width = me.getWidth(),
4839 height = me.getHeight(),
4841 position = me.getPositioning(),
4847 to.y = xy[1] - height;
4850 to.x = xy[0] - width;
4853 to.x = xy[0] + width;
4856 to.y = xy[1] + height;
4859 to.x = xy[0] - width;
4860 to.y = xy[1] - height;
4863 to.x = xy[0] - width;
4864 to.y = xy[1] + height;
4867 to.x = xy[0] + width;
4868 to.y = xy[1] + height;
4871 to.x = xy[0] + width;
4872 to.y = xy[1] - height;
4876 this.on('afteranimate', function () {
4880 me.setPositioning(position);
4885 me.animate(Ext.applyIf(obj || {}, {
4898 * Highlights the Element by setting a color (applies to the background-color by default, but can be
4899 * changed using the "attr" config option) and then fading back to the original color. If no original
4900 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
4903 // default: highlight background to yellow
4906 // custom: highlight foreground text to blue for 2 seconds
4907 el.highlight("0000ff", { attr: 'color', duration: 2 });
4909 // common config options shown with default values
4910 el.highlight("ffff9c", {
4911 attr: "backgroundColor", //can be any valid CSS property (attribute) that supports a color value
4912 endColor: (current color) or "ffffff",
4917 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
4918 * @param {Object} options (optional) Object literal with any of the Fx config options
4919 * @return {Ext.core.Element} The Element
4921 highlight: function(color, o) {
4925 restore, to, attr, lns, event, fn;
4928 lns = o.listeners || {};
4929 attr = o.attr || 'backgroundColor';
4930 from[attr] = color || 'ffff9c';
4934 to[attr] = o.endColor || me.getColor(attr, 'ffffff', '');
4940 // Don't apply directly on lns, since we reference it in our own callbacks below
4941 o.listeners = Ext.apply(Ext.apply({}, lns), {
4942 beforeanimate: function() {
4943 restore = dom.style[attr];
4947 event = lns.beforeanimate;
4949 fn = event.fn || event;
4950 return fn.apply(event.scope || lns.scope || window, arguments);
4953 afteranimate: function() {
4955 dom.style[attr] = restore;
4958 event = lns.afteranimate;
4960 fn = event.fn || event;
4961 fn.apply(event.scope || lns.scope || window, arguments);
4966 me.animate(Ext.apply({}, o, {
4977 * Creates a pause before any subsequent queued effects begin. If there are
4978 * no effects queued after the pause it will have no effect.
4983 * @param {Number} seconds The length of time to pause (in seconds)
4984 * @return {Ext.Element} The Element
4986 pause: function(ms) {
4988 Ext.fx.Manager.setFxDefaults(me.id, {
4995 * Fade an element in (from transparent to opaque). The ending opacity can be specified
4996 * using the <tt>{@link #endOpacity}</tt> config option.
4999 // default: fade in from opacity 0 to 100%
5002 // custom: fade in from opacity 0 to 75% over 2 seconds
5003 el.fadeIn({ endOpacity: .75, duration: 2});
5005 // common config options shown with default values
5007 endOpacity: 1, //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 fadeIn: function(o) {
5016 this.animate(Ext.apply({}, o, {
5023 * Fade an element out (from opaque to transparent). The ending opacity can be specified
5024 * using the <tt>{@link #endOpacity}</tt> config option. Note that IE may require
5025 * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
5028 // default: fade out from the element's current opacity to 0
5031 // custom: fade out from the element's current opacity to 25% over 2 seconds
5032 el.fadeOut({ endOpacity: .25, duration: 2});
5034 // common config options shown with default values
5036 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
5043 * @param {Object} options (optional) Object literal with any of the Fx config options
5044 * @return {Ext.Element} The Element
5046 fadeOut: function(o) {
5047 this.animate(Ext.apply({}, o, {
5055 * Animates the transition of an element's dimensions from a starting height/width
5056 * to an ending height/width. This method is a convenience implementation of {@link shift}.
5059 // change height and width to 100x100 pixels
5062 // common config options shown with default values. The height and width will default to
5063 // the element's existing values if passed as null.
5065 [element's width],
5066 [element's height], {
5072 * @param {Number} width The new width (pass undefined to keep the original width)
5073 * @param {Number} height The new height (pass undefined to keep the original height)
5074 * @param {Object} options (optional) Object literal with any of the Fx config options
5075 * @return {Ext.Element} The Element
5077 scale: function(w, h, o) {
5078 this.animate(Ext.apply({}, o, {
5087 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
5088 * Any of these properties not specified in the config object will not be changed. This effect
5089 * requires that at least one new dimension, position or opacity setting must be passed in on
5090 * the config object in order for the function to have any effect.
5093 // slide the element horizontally to x position 200 while changing the height and opacity
5094 el.shift({ x: 200, height: 50, opacity: .8 });
5096 // common config options shown with default values.
5098 width: [element's width],
5099 height: [element's height],
5100 x: [element's x position],
5101 y: [element's y position],
5102 opacity: [element's opacity],
5107 * @param {Object} options Object literal with any of the Fx config options
5108 * @return {Ext.Element} The Element
5110 shift: function(config) {
5111 this.animate(config);
5117 * @class Ext.core.Element
5119 Ext.applyIf(Ext.core.Element, {
5120 unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
5121 camelRe: /(-[a-z])/gi,
5122 opacityRe: /alpha\(opacity=(.*)\)/i,
5123 cssRe: /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
5126 borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
5127 paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
5128 margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},
5130 // Reference the prototype's version of the method. Signatures are identical.
5131 addUnits : Ext.core.Element.prototype.addUnits,
5134 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
5135 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
5137 * @param {Number|String} box The encoded margins
5138 * @return {Object} An object with margin sizes for top, right, bottom and left
5140 parseBox : function(box) {
5141 if (Ext.isObject(box)) {
5144 right: box.right || 0,
5145 bottom: box.bottom || 0,
5149 if (typeof box != 'string') {
5150 box = box.toString();
5152 var parts = box.split(' '),
5156 parts[1] = parts[2] = parts[3] = parts[0];
5159 parts[2] = parts[0];
5160 parts[3] = parts[1];
5163 parts[3] = parts[1];
5167 top :parseFloat(parts[0]) || 0,
5168 right :parseFloat(parts[1]) || 0,
5169 bottom:parseFloat(parts[2]) || 0,
5170 left :parseFloat(parts[3]) || 0
5177 * Parses a number or string representing margin sizes into an object. Supports CSS-style margin declarations
5178 * (e.g. 10, "10", "10 10", "10 10 10" and "10 10 10 10" are all valid options and would return the same result)
5180 * @param {Number|String} box The encoded margins
5181 * @param {String} units The type of units to add
5182 * @return {String} An string with unitized (px if units is not specified) metrics for top, right, bottom and left
5184 unitizeBox : function(box, units) {
5185 var A = this.addUnits,
5186 B = this.parseBox(box);
5188 return A(B.top, units) + ' ' +
5189 A(B.right, units) + ' ' +
5190 A(B.bottom, units) + ' ' +
5196 camelReplaceFn : function(m, a) {
5197 return a.charAt(1).toUpperCase();
5201 * Normalizes CSS property keys from dash delimited to camel case JavaScript Syntax.
5204 * <li>border-width -> borderWidth</li>
5205 * <li>padding-top -> paddingTop</li>
5208 * @param {String} prop The property to normalize
5209 * @return {String} The normalized string
5211 normalize : function(prop) {
5212 if (prop == 'float') {
5213 prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
5215 return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
5219 * Retrieves the document height
5221 * @return {Number} documentHeight
5223 getDocumentHeight: function() {
5224 return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
5228 * Retrieves the document width
5230 * @return {Number} documentWidth
5232 getDocumentWidth: function() {
5233 return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
5237 * Retrieves the viewport height of the window.
5239 * @return {Number} viewportHeight
5241 getViewportHeight: function(){
5242 return window.innerHeight;
5246 * Retrieves the viewport width of the window.
5248 * @return {Number} viewportWidth
5250 getViewportWidth : function() {
5251 return window.innerWidth;
5255 * Retrieves the viewport size of the window.
5257 * @return {Object} object containing width and height properties
5259 getViewSize : function() {
5261 width: window.innerWidth,
5262 height: window.innerHeight
5267 * Retrieves the current orientation of the window. This is calculated by
5268 * determing if the height is greater than the width.
5270 * @return {String} Orientation of window: 'portrait' or 'landscape'
5272 getOrientation : function() {
5273 if (Ext.supports.OrientationChange) {
5274 return (window.orientation == 0) ? 'portrait' : 'landscape';
5277 return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
5281 * Returns the top Element that is located at the passed coordinates
5283 * @param {Number} x The x coordinate
5284 * @param {Number} x The y coordinate
5285 * @return {String} The found Element
5287 fromPoint: function(x, y) {
5288 return Ext.get(document.elementFromPoint(x, y));
5292 * Converts a CSS string into an object with a property for each style.
5294 * The sample code below would return an object with 2 properties, one
5295 * for background-color and one for color.</p>
5297 var css = 'background-color: red;color: blue; ';
5298 console.log(Ext.core.Element.parseStyles(css));
5301 * @param {String} styles A CSS string
5302 * @return {Object} styles
5304 parseStyles: function(styles){
5310 // Since we're using the g flag on the regex, we need to set the lastIndex.
5311 // This automatically happens on some implementations, but not others, see:
5312 // http://stackoverflow.com/questions/2645273/javascript-regular-expression-literal-persists-between-function-calls
5313 // http://blog.stevenlevithan.com/archives/fixing-javascript-regexp
5314 cssRe.lastIndex = 0;
5315 while ((matches = cssRe.exec(styles))) {
5316 out[matches[1]] = matches[2];
5324 * @class Ext.CompositeElementLite
5325 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
5326 * members, or to perform collective actions upon the whole set.</p>
5327 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
5328 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
5329 * Example:<pre><code>
5330 var els = Ext.select("#some-el div.some-class");
5331 // or select directly from an existing element
5332 var el = Ext.get('some-el');
5333 el.select('div.some-class');
5335 els.setWidth(100); // all elements become 100 width
5336 els.hide(true); // all elements fade out and hide
5338 els.setWidth(100).hide(true);
5341 Ext.CompositeElementLite = function(els, root){
5343 * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
5344 * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
5345 * to augment the capabilities of the CompositeElementLite class may use it when adding
5346 * methods to the class.</p>
5347 * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
5348 * following siblings of selected elements, the code would be</p><code><pre>
5349 Ext.override(Ext.CompositeElementLite, {
5350 nextAll: function() {
5351 var els = this.elements, i, l = els.length, n, r = [], ri = -1;
5353 // Loop through all elements in this Composite, accumulating
5354 // an Array of all siblings.
5355 for (i = 0; i < l; i++) {
5356 for (n = els[i].nextSibling; n; n = n.nextSibling) {
5361 // Add all found siblings to this Composite
5366 * @property elements
5369 this.add(els, root);
5370 this.el = new Ext.core.Element.Flyweight();
5373 Ext.CompositeElementLite.prototype = {
5377 getElement : function(el){
5378 // Set the shared flyweight dom property to the current element
5386 transformElement : function(el){
5387 return Ext.getDom(el);
5391 * Returns the number of elements in this Composite.
5394 getCount : function(){
5395 return this.elements.length;
5398 * Adds elements to this Composite object.
5399 * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
5400 * @return {CompositeElement} This Composite object.
5402 add : function(els, root){
5404 elements = me.elements;
5408 if(typeof els == "string"){
5409 els = Ext.core.Element.selectorFunction(els, root);
5410 }else if(els.isComposite){
5412 }else if(!Ext.isIterable(els)){
5416 for(var i = 0, len = els.length; i < len; ++i){
5417 elements.push(me.transformElement(els[i]));
5422 invoke : function(fn, args){
5429 for(i = 0; i < len; i++) {
5432 Ext.core.Element.prototype[fn].apply(me.getElement(e), args);
5438 * Returns a flyweight Element of the dom element object at the specified index
5439 * @param {Number} index
5440 * @return {Ext.core.Element}
5442 item : function(index){
5444 el = me.elements[index],
5448 out = me.getElement(el);
5453 // fixes scope with flyweight
5454 addListener : function(eventName, handler, scope, opt){
5455 var els = this.elements,
5459 for(i = 0; i<len; i++) {
5462 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
5468 * <p>Calls the passed function for each element in this composite.</p>
5469 * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
5470 * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
5471 * <b>This is the flyweight (shared) Ext.core.Element instance, so if you require a
5472 * a reference to the dom node, use el.dom.</b></div></li>
5473 * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
5474 * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
5476 * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
5477 * @return {CompositeElement} this
5479 each : function(fn, scope){
5485 for(i = 0; i<len; i++) {
5488 e = this.getElement(e);
5489 if(fn.call(scope || e, e, me, i) === false){
5498 * Clears this Composite and adds the elements passed.
5499 * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
5500 * @return {CompositeElement} this
5502 fill : function(els){
5510 * Filters this composite to only elements that match the passed selector.
5511 * @param {String/Function} selector A string CSS selector or a comparison function.
5512 * The comparison function will be called with the following arguments:<ul>
5513 * <li><code>el</code> : Ext.core.Element<div class="sub-desc">The current DOM element.</div></li>
5514 * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
5516 * @return {CompositeElement} this
5518 filter : function(selector){
5521 fn = Ext.isFunction(selector) ? selector
5523 return el.is(selector);
5526 me.each(function(el, self, i) {
5527 if (fn(el, i) !== false) {
5528 els[els.length] = me.transformElement(el);
5537 * Find the index of the passed element within the composite collection.
5538 * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
5539 * @return Number The index of the passed Ext.core.Element in the composite collection, or -1 if not found.
5541 indexOf : function(el){
5542 return Ext.Array.indexOf(this.elements, this.transformElement(el));
5546 * Replaces the specified element with the passed element.
5547 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
5549 * @param {Mixed} replacement The id of an element or the Element itself.
5550 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
5551 * @return {CompositeElement} this
5553 replaceElement : function(el, replacement, domReplace){
5554 var index = !isNaN(el) ? el : this.indexOf(el),
5557 replacement = Ext.getDom(replacement);
5559 d = this.elements[index];
5560 d.parentNode.insertBefore(replacement, d);
5563 this.elements.splice(index, 1, replacement);
5569 * Removes all elements.
5576 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
5580 * Copies all of the functions from Ext.core.Element's prototype onto CompositeElementLite's prototype.
5581 * This is called twice - once immediately below, and once again after additional Ext.core.Element
5582 * are added in Ext JS
5584 Ext.CompositeElementLite.importElementMethods = function() {
5586 ElProto = Ext.core.Element.prototype,
5587 CelProto = Ext.CompositeElementLite.prototype;
5589 for (fnName in ElProto) {
5590 if (typeof ElProto[fnName] == 'function'){
5592 CelProto[fnName] = CelProto[fnName] || function() {
5593 return this.invoke(fnName, arguments);
5595 }).call(CelProto, fnName);
5601 Ext.CompositeElementLite.importElementMethods();
5604 Ext.core.Element.selectorFunction = Ext.DomQuery.select;
5608 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
5609 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
5610 * {@link Ext.CompositeElementLite CompositeElementLite} object.
5611 * @param {String/Array} selector The CSS selector or an array of elements
5612 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
5613 * @return {CompositeElementLite/CompositeElement}
5614 * @member Ext.core.Element
5617 Ext.core.Element.select = function(selector, root){
5619 if(typeof selector == "string"){
5620 els = Ext.core.Element.selectorFunction(selector, root);
5621 }else if(selector.length !== undefined){
5626 sourceClass: "Ext.core.Element",
5627 sourceMethod: "select",
5630 msg: "Invalid selector specified: " + selector
5634 return new Ext.CompositeElementLite(els);
5637 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
5638 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
5639 * {@link Ext.CompositeElementLite CompositeElementLite} object.
5640 * @param {String/Array} selector The CSS selector or an array of elements
5641 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
5642 * @return {CompositeElementLite/CompositeElement}
5646 Ext.select = Ext.core.Element.select;
5649 * @class Ext.util.DelayedTask
5651 * The DelayedTask class provides a convenient way to "buffer" the execution of a method,
5652 * performing setTimeout where a new timeout cancels the old timeout. When called, the
5653 * task will wait the specified time period before executing. If durng that time period,
5654 * the task is called again, the original call will be cancelled. This continues so that
5655 * the function is only called a single time for each iteration.
5657 * This method is especially useful for things like detecting whether a user has finished
5658 * typing in a text field. An example would be performing validation on a keypress. You can
5659 * use this class to buffer the keypress events for a certain number of milliseconds, and
5660 * perform only if they stop for that amount of time.
5664 * var task = new Ext.util.DelayedTask(function(){
5665 * alert(Ext.getDom('myInputField').value.length);
5668 * // Wait 500ms before calling our function. If the user presses another key
5669 * // during that 500ms, it will be cancelled and we'll wait another 500ms.
5670 * Ext.get('myInputField').on('keypress', function(){
5671 * task.{@link #delay}(500);
5674 * Note that we are using a DelayedTask here to illustrate a point. The configuration
5675 * option `buffer` for {@link Ext.util.Observable#addListener addListener/on} will
5676 * also setup a delayed task for you to buffer events.
5678 * @constructor The parameters to this constructor serve as defaults and are not required.
5679 * @param {Function} fn (optional) The default function to call.
5680 * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
5681 * function is called. If not specified, <code>this</code> will refer to the browser window.
5682 * @param {Array} args (optional) The default Array of arguments.
5684 Ext.util.DelayedTask = function(fn, scope, args) {
5690 fn.apply(scope, args || []);
5694 * Cancels any pending timeout and queues a new one
5695 * @param {Number} delay The milliseconds to delay
5696 * @param {Function} newFn (optional) Overrides function passed to constructor
5697 * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
5698 * is specified, <code>this</code> will refer to the browser window.
5699 * @param {Array} newArgs (optional) Overrides args passed to constructor
5701 this.delay = function(delay, newFn, newScope, newArgs) {
5704 scope = newScope || scope;
5705 args = newArgs || args;
5706 id = setInterval(call, delay);
5710 * Cancel the last queued timeout
5712 this.cancel = function(){
5719 Ext.require('Ext.util.DelayedTask', function() {
5721 Ext.util.Event = Ext.extend(Object, (function() {
5722 function createBuffered(handler, listener, o, scope) {
5723 listener.task = new Ext.util.DelayedTask();
5725 listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
5729 function createDelayed(handler, listener, o, scope) {
5731 var task = new Ext.util.DelayedTask();
5732 if (!listener.tasks) {
5733 listener.tasks = [];
5735 listener.tasks.push(task);
5736 task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
5740 function createSingle(handler, listener, o, scope) {
5742 listener.ev.removeListener(listener.fn, scope);
5743 return handler.apply(scope, arguments);
5750 constructor: function(observable, name) {
5752 this.observable = observable;
5753 this.listeners = [];
5756 addListener: function(fn, scope, options) {
5759 scope = scope || me.observable;
5764 sourceClass: Ext.getClassName(this.observable),
5765 sourceMethod: "addListener",
5766 msg: "The specified callback function is undefined"
5771 if (!me.isListening(fn, scope)) {
5772 listener = me.createListener(fn, scope, options);
5774 // if we are currently firing this event, don't disturb the listener loop
5775 me.listeners = me.listeners.slice(0);
5777 me.listeners.push(listener);
5781 createListener: function(fn, scope, o) {
5783 scope = scope || this.observable;
5793 // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
5794 // because the event removal that the single listener does destroys the listener's DelayedTask(s)
5796 handler = createSingle(handler, listener, o, scope);
5799 handler = createDelayed(handler, listener, o, scope);
5802 handler = createBuffered(handler, listener, o, scope);
5805 listener.fireFn = handler;
5809 findListener: function(fn, scope) {
5810 var listeners = this.listeners,
5811 i = listeners.length,
5816 listener = listeners[i];
5819 if (listener.fn == fn && (s == scope || s == this.observable)) {
5828 isListening: function(fn, scope) {
5829 return this.findListener(fn, scope) !== -1;
5832 removeListener: function(fn, scope) {
5837 index = me.findListener(fn, scope);
5839 listener = me.listeners[index];
5842 me.listeners = me.listeners.slice(0);
5845 // cancel and remove a buffered handler that hasn't fired yet
5846 if (listener.task) {
5847 listener.task.cancel();
5848 delete listener.task;
5851 // cancel and remove all delayed handlers that haven't fired yet
5852 k = listener.tasks && listener.tasks.length;
5855 listener.tasks[k].cancel();
5857 delete listener.tasks;
5860 // remove this listener from the listeners array
5861 me.listeners.splice(index, 1);
5868 // Iterate to stop any buffered/delayed events
5869 clearListeners: function() {
5870 var listeners = this.listeners,
5871 i = listeners.length;
5874 this.removeListener(listeners[i].fn, listeners[i].scope);
5880 listeners = me.listeners,
5881 count = listeners.length,
5888 for (i = 0; i < count; i++) {
5889 listener = listeners[i];
5890 args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
5892 args.push(listener.o);
5894 if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
5895 return (me.firing = false);
5907 * @class Ext.EventManager
5908 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
5909 * several useful events directly.
5910 * See {@link Ext.EventObject} for more details on normalized event objects.
5913 Ext.EventManager = {
5915 // --------------------- onReady ---------------------
5918 * Check if we have bound our global onReady listener
5921 hasBoundOnReady: false,
5924 * Check if fireDocReady has been called
5927 hasFiredReady: false,
5930 * Timer for the document ready event in old IE versions
5936 * Checks if we have bound an onreadystatechange event
5939 hasOnReadyStateChange: false,
5942 * Holds references to any onReady functions
5945 readyEvent: new Ext.util.Event(),
5948 * Check the ready state for old IE versions
5950 * @return {Boolean} True if the document is ready
5952 checkReadyState: function(){
5953 var me = Ext.EventManager;
5955 if(window.attachEvent){
5956 // See here for reference: http://javascript.nwbox.com/IEContentLoaded/
5957 if (window != top) {
5961 document.documentElement.doScroll('left');
5968 if (document.readyState == 'complete') {
5972 me.readyTimeout = setTimeout(arguments.callee, 2);
5977 * Binds the appropriate browser event for checking if the DOM has loaded.
5980 bindReadyEvent: function(){
5981 var me = Ext.EventManager;
5982 if (me.hasBoundOnReady) {
5986 if (document.addEventListener) {
5987 document.addEventListener('DOMContentLoaded', me.fireDocReady, false);
5988 // fallback, load will ~always~ fire
5989 window.addEventListener('load', me.fireDocReady, false);
5991 // check if the document is ready, this will also kick off the scroll checking timer
5992 if (!me.checkReadyState()) {
5993 document.attachEvent('onreadystatechange', me.checkReadyState);
5994 me.hasOnReadyStateChange = true;
5996 // fallback, onload will ~always~ fire
5997 window.attachEvent('onload', me.fireDocReady, false);
5999 me.hasBoundOnReady = true;
6003 * We know the document is loaded, so trigger any onReady events.
6006 fireDocReady: function(){
6007 var me = Ext.EventManager;
6009 // only unbind these events once
6010 if (!me.hasFiredReady) {
6011 me.hasFiredReady = true;
6013 if (document.addEventListener) {
6014 document.removeEventListener('DOMContentLoaded', me.fireDocReady, false);
6015 window.removeEventListener('load', me.fireDocReady, false);
6017 if (me.readyTimeout !== null) {
6018 clearTimeout(me.readyTimeout);
6020 if (me.hasOnReadyStateChange) {
6021 document.detachEvent('onreadystatechange', me.checkReadyState);
6023 window.detachEvent('onload', me.fireDocReady);
6025 Ext.supports.init();
6029 me.onWindowUnload();
6030 me.readyEvent.fire();
6035 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
6036 * accessed shorthanded as Ext.onReady().
6037 * @param {Function} fn The method the event invokes.
6038 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
6039 * @param {boolean} options (optional) Options object as passed to {@link Ext.core.Element#addListener}.
6041 onDocumentReady: function(fn, scope, options){
6042 options = options || {};
6043 var me = Ext.EventManager,
6044 readyEvent = me.readyEvent;
6046 // force single to be true so our event is only ever fired once.
6047 options.single = true;
6049 // Document already loaded, let's just fire it
6051 readyEvent.addListener(fn, scope, options);
6054 options.delay = options.delay || 1;
6055 readyEvent.addListener(fn, scope, options);
6056 me.bindReadyEvent();
6061 // --------------------- event binding ---------------------
6064 * Contains a list of all document mouse downs, so we can ensure they fire even when stopEvent is called.
6067 stoppedMouseDownEvent: new Ext.util.Event(),
6070 * Options to parse for the 4th argument to addListener.
6073 propRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|freezeEvent)$/,
6076 * Get the id of the element. If one has not been assigned, automatically assign it.
6077 * @param {Mixed} element The element to get the id for.
6078 * @return {String} id
6080 getId : function(element) {
6081 var skipGarbageCollection = false,
6084 element = Ext.getDom(element);
6086 if (element === document || element === window) {
6087 id = element === document ? Ext.documentId : Ext.windowId;
6090 id = Ext.id(element);
6092 // skip garbage collection for special elements (window, document, iframes)
6093 if (element && (element.getElementById || element.navigator)) {
6094 skipGarbageCollection = true;
6097 if (!Ext.cache[id]){
6098 Ext.core.Element.addToCache(new Ext.core.Element(element), id);
6099 if (skipGarbageCollection) {
6100 Ext.cache[id].skipGarbageCollection = true;
6107 * Convert a "config style" listener into a set of flat arguments so they can be passed to addListener
6109 * @param {Object} element The element the event is for
6110 * @param {Object} event The event configuration
6111 * @param {Object} isRemove True if a removal should be performed, otherwise an add will be done.
6113 prepareListenerConfig: function(element, config, isRemove){
6118 // loop over all the keys in the object
6119 for (key in config) {
6120 if (config.hasOwnProperty(key)) {
6121 // if the key is something else then an event option
6122 if (!propRe.test(key)) {
6123 value = config[key];
6124 // if the value is a function it must be something like click: function(){}, scope: this
6125 // which means that there might be multiple event listeners with shared options
6126 if (Ext.isFunction(value)) {
6128 args = [element, key, value, config.scope, config];
6130 // if its not a function, it must be an object like click: {fn: function(){}, scope: this}
6131 args = [element, key, value.fn, value.scope, value];
6134 if (isRemove === true) {
6135 me.removeListener.apply(this, args);
6137 me.addListener.apply(me, args);
6145 * Normalize cross browser event differences
6147 * @param {Object} eventName The event name
6148 * @param {Object} fn The function to execute
6149 * @return {Object} The new event name/function
6151 normalizeEvent: function(eventName, fn){
6152 if (/mouseenter|mouseleave/.test(eventName) && !Ext.supports.MouseEnterLeave) {
6154 fn = Ext.Function.createInterceptor(fn, this.contains, this);
6156 eventName = eventName == 'mouseenter' ? 'mouseover' : 'mouseout';
6157 } else if (eventName == 'mousewheel' && !Ext.supports.MouseWheel && !Ext.isOpera){
6158 eventName = 'DOMMouseScroll';
6161 eventName: eventName,
6167 * Checks whether the event's relatedTarget is contained inside (or <b>is</b>) the element.
6169 * @param {Object} event
6171 contains: function(event){
6172 var parent = event.browserEvent.currentTarget,
6173 child = this.getRelatedTarget(event);
6175 if (parent && parent.firstChild) {
6177 if (child === parent) {
6180 child = child.parentNode;
6181 if (child && (child.nodeType != 1)) {
6190 * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
6191 * use {@link Ext.core.Element#addListener} directly on an Element in favor of calling this version.
6192 * @param {String/HTMLElement} el The html element or id to assign the event handler to.
6193 * @param {String} eventName The name of the event to listen for.
6194 * @param {Function} handler The handler function the event invokes. This function is passed
6195 * the following parameters:<ul>
6196 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
6197 * <li>t : Element<div class="sub-desc">The {@link Ext.core.Element Element} which was the target of the event.
6198 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
6199 * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
6201 * @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>.
6202 * @param {Object} options (optional) An object containing handler configuration properties.
6203 * This may contain any of the following properties:<ul>
6204 * <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>
6205 * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
6206 * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
6207 * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
6208 * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
6209 * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
6210 * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
6211 * <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>
6212 * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
6213 * by the specified number of milliseconds. If the event fires again within that time, the original
6214 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
6215 * <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>
6217 * <p>See {@link Ext.core.Element#addListener} for examples of how to use these options.</p>
6219 addListener: function(element, eventName, fn, scope, options){
6220 // Check if we've been passed a "config style" event.
6221 if (Ext.isObject(eventName)) {
6222 this.prepareListenerConfig(element, eventName);
6226 var dom = Ext.getDom(element),
6233 sourceClass: 'Ext.EventManager',
6234 sourceMethod: 'addListener',
6235 targetElement: element,
6236 eventName: eventName,
6237 msg: 'Error adding "' + eventName + '\" listener for nonexistent element "' + element + '"'
6242 sourceClass: 'Ext.EventManager',
6243 sourceMethod: 'addListener',
6244 targetElement: element,
6245 eventName: eventName,
6246 msg: 'Error adding "' + eventName + '\" listener. The handler function is undefined.'
6251 // create the wrapper function
6252 options = options || {};
6254 bind = this.normalizeEvent(eventName, fn);
6255 wrap = this.createListenerWrap(dom, eventName, bind.fn, scope, options);
6258 if (dom.attachEvent) {
6259 dom.attachEvent('on' + bind.eventName, wrap);
6261 dom.addEventListener(bind.eventName, wrap, options.capture || false);
6264 if (dom == document && eventName == 'mousedown') {
6265 this.stoppedMouseDownEvent.addListener(wrap);
6268 // add all required data into the event cache
6269 this.getEventListenerCache(dom, eventName).push({
6277 * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
6278 * you will use {@link Ext.core.Element#removeListener} directly on an Element in favor of calling this version.
6279 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
6280 * @param {String} eventName The name of the event.
6281 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
6282 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
6283 * then this must refer to the same object.
6285 removeListener : function(element, eventName, fn, scope) {
6286 // handle our listener config object syntax
6287 if (Ext.isObject(eventName)) {
6288 this.prepareListenerConfig(element, eventName, true);
6292 var dom = Ext.getDom(element),
6293 cache = this.getEventListenerCache(dom, eventName),
6294 bindName = this.normalizeEvent(eventName).eventName,
6295 i = cache.length, j,
6296 listener, wrap, tasks;
6300 listener = cache[i];
6302 if (listener && (!fn || listener.fn == fn) && (!scope || listener.scope === scope)) {
6303 wrap = listener.wrap;
6305 // clear buffered calls
6307 clearTimeout(wrap.task);
6311 // clear delayed calls
6312 j = wrap.tasks && wrap.tasks.length;
6315 clearTimeout(wrap.tasks[j]);
6320 if (dom.detachEvent) {
6321 dom.detachEvent('on' + bindName, wrap);
6323 dom.removeEventListener(bindName, wrap, false);
6326 if (wrap && dom == document && eventName == 'mousedown') {
6327 this.stoppedMouseDownEvent.removeListener(wrap);
6330 // remove listener from cache
6337 * Removes all event handers from an element. Typically you will use {@link Ext.core.Element#removeAllListeners}
6338 * directly on an Element in favor of calling this version.
6339 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
6341 removeAll : function(element){
6342 var dom = Ext.getDom(element),
6347 cache = this.getElementEventCache(dom);
6350 if (cache.hasOwnProperty(ev)) {
6351 this.removeListener(dom, ev);
6354 Ext.cache[dom.id].events = {};
6358 * Recursively removes all previous added listeners from an element and its children. Typically you will use {@link Ext.core.Element#purgeAllListeners}
6359 * directly on an Element in favor of calling this version.
6360 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
6361 * @param {String} eventName (optional) The name of the event.
6363 purgeElement : function(element, eventName) {
6364 var dom = Ext.getDom(element),
6368 this.removeListener(dom, eventName);
6371 this.removeAll(dom);
6374 if(dom && dom.childNodes) {
6375 for(len = element.childNodes.length; i < len; i++) {
6376 this.purgeElement(element.childNodes[i], eventName);
6382 * Create the wrapper function for the event
6384 * @param {HTMLElement} dom The dom element
6385 * @param {String} ename The event name
6386 * @param {Function} fn The function to execute
6387 * @param {Object} scope The scope to execute callback in
6388 * @param {Object} options The options
6389 * @return {Function} the wrapper function
6391 createListenerWrap : function(dom, ename, fn, scope, options) {
6392 options = !Ext.isObject(options) ? {} : options;
6396 return function wrap(e, args) {
6397 // Compile the implementation upon first firing
6399 f = ['if(!Ext) {return;}'];
6401 if(options.buffer || options.delay || options.freezeEvent) {
6402 f.push('e = new Ext.EventObjectImpl(e, ' + (options.freezeEvent ? 'true' : 'false' ) + ');');
6404 f.push('e = Ext.EventObject.setEvent(e);');
6407 if (options.delegate) {
6408 f.push('var t = e.getTarget("' + options.delegate + '", this);');
6409 f.push('if(!t) {return;}');
6411 f.push('var t = e.target;');
6414 if (options.target) {
6415 f.push('if(e.target !== options.target) {return;}');
6418 if(options.stopEvent) {
6419 f.push('e.stopEvent();');
6421 if(options.preventDefault) {
6422 f.push('e.preventDefault();');
6424 if(options.stopPropagation) {
6425 f.push('e.stopPropagation();');
6429 if(options.normalized === false) {
6430 f.push('e = e.browserEvent;');
6433 if(options.buffer) {
6434 f.push('(wrap.task && clearTimeout(wrap.task));');
6435 f.push('wrap.task = setTimeout(function(){');
6439 f.push('wrap.tasks = wrap.tasks || [];');
6440 f.push('wrap.tasks.push(setTimeout(function(){');
6443 // finally call the actual handler fn
6444 f.push('fn.call(scope || dom, e, t, options);');
6446 if(options.single) {
6447 f.push('Ext.EventManager.removeListener(dom, ename, fn, scope);');
6451 f.push('}, ' + options.delay + '));');
6454 if(options.buffer) {
6455 f.push('}, ' + options.buffer + ');');
6458 gen = Ext.functionFactory('e', 'options', 'fn', 'scope', 'ename', 'dom', 'wrap', 'args', f.join('\n'));
6461 gen.call(dom, e, options, fn, scope, ename, dom, wrap, args);
6466 * Get the event cache for a particular element for a particular event
6468 * @param {HTMLElement} element The element
6469 * @param {Object} eventName The event name
6470 * @return {Array} The events for the element
6472 getEventListenerCache : function(element, eventName) {
6473 var eventCache = this.getElementEventCache(element);
6474 return eventCache[eventName] || (eventCache[eventName] = []);
6478 * Gets the event cache for the object
6480 * @param {HTMLElement} element The element
6481 * @return {Object} The event cache for the object
6483 getElementEventCache : function(element) {
6484 var elementCache = Ext.cache[this.getId(element)];
6485 return elementCache.events || (elementCache.events = {});
6488 // --------------------- utility methods ---------------------
6489 mouseLeaveRe: /(mouseout|mouseleave)/,
6490 mouseEnterRe: /(mouseover|mouseenter)/,
6493 * Stop the event (preventDefault and stopPropagation)
6494 * @param {Event} The event to stop
6496 stopEvent: function(event) {
6497 this.stopPropagation(event);
6498 this.preventDefault(event);
6502 * Cancels bubbling of the event.
6503 * @param {Event} The event to stop bubbling.
6505 stopPropagation: function(event) {
6506 event = event.browserEvent || event;
6507 if (event.stopPropagation) {
6508 event.stopPropagation();
6510 event.cancelBubble = true;
6515 * Prevents the browsers default handling of the event.
6516 * @param {Event} The event to prevent the default
6518 preventDefault: function(event) {
6519 event = event.browserEvent || event;
6520 if (event.preventDefault) {
6521 event.preventDefault();
6523 event.returnValue = false;
6524 // Some keys events require setting the keyCode to -1 to be prevented
6526 // all ctrl + X and F1 -> F12
6527 if (event.ctrlKey || event.keyCode > 111 && event.keyCode < 124) {
6531 // see this outdated document http://support.microsoft.com/kb/934364/en-us for more info
6537 * Gets the related target from the event.
6538 * @param {Object} event The event
6539 * @return {HTMLElement} The related target.
6541 getRelatedTarget: function(event) {
6542 event = event.browserEvent || event;
6543 var target = event.relatedTarget;
6545 if (this.mouseLeaveRe.test(event.type)) {
6546 target = event.toElement;
6547 } else if (this.mouseEnterRe.test(event.type)) {
6548 target = event.fromElement;
6551 return this.resolveTextNode(target);
6555 * Gets the x coordinate from the event
6556 * @param {Object} event The event
6557 * @return {Number} The x coordinate
6559 getPageX: function(event) {
6560 return this.getXY(event)[0];
6564 * Gets the y coordinate from the event
6565 * @param {Object} event The event
6566 * @return {Number} The y coordinate
6568 getPageY: function(event) {
6569 return this.getXY(event)[1];
6573 * Gets the x & ycoordinate from the event
6574 * @param {Object} event The event
6575 * @return {Array} The x/y coordinate
6577 getPageXY: function(event) {
6578 event = event.browserEvent || event;
6579 var x = event.pageX,
6581 doc = document.documentElement,
6582 body = document.body;
6584 // pageX/pageY not available (undefined, not null), use clientX/clientY instead
6585 if (!x && x !== 0) {
6586 x = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
6587 y = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
6593 * Gets the target of the event.
6594 * @param {Object} event The event
6595 * @return {HTMLElement} target
6597 getTarget: function(event) {
6598 event = event.browserEvent || event;
6599 return this.resolveTextNode(event.target || event.srcElement);
6603 * Resolve any text nodes accounting for browser differences.
6605 * @param {HTMLElement} node The node
6606 * @return {HTMLElement} The resolved node
6608 // 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.
6609 resolveTextNode: Ext.isGecko ?
6614 // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197
6615 var s = HTMLElement.prototype.toString.call(node);
6616 if (s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]') {
6619 return node.nodeType == 3 ? node.parentNode: node;
6621 return node && node.nodeType == 3 ? node.parentNode: node;
6624 // --------------------- custom event binding ---------------------
6626 // Keep track of the current width/height
6631 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
6632 * passes new viewport width and height to handlers.
6633 * @param {Function} fn The handler function the window resize event invokes.
6634 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
6635 * @param {boolean} options Options object as passed to {@link Ext.core.Element#addListener}
6637 onWindowResize: function(fn, scope, options){
6638 var resize = this.resizeEvent;
6640 this.resizeEvent = resize = new Ext.util.Event();
6641 this.on(window, 'resize', this.fireResize, this, {buffer: 100});
6643 resize.addListener(fn, scope, options);
6647 * Fire the resize event.
6650 fireResize: function(){
6652 w = Ext.core.Element.getViewWidth(),
6653 h = Ext.core.Element.getViewHeight();
6655 //whacky problem in IE where the resize event will sometimes fire even though the w/h are the same.
6656 if(me.curHeight != h || me.curWidth != w){
6659 me.resizeEvent.fire(w, h);
6664 * Removes the passed window resize listener.
6665 * @param {Function} fn The method the event invokes
6666 * @param {Object} scope The scope of handler
6668 removeResizeListener: function(fn, scope){
6669 if (this.resizeEvent) {
6670 this.resizeEvent.removeListener(fn, scope);
6674 onWindowUnload: function() {
6675 var unload = this.unloadEvent;
6677 this.unloadEvent = unload = new Ext.util.Event();
6678 this.addListener(window, 'unload', this.fireUnload, this);
6683 * Fires the unload event for items bound with onWindowUnload
6686 fireUnload: function() {
6687 // wrap in a try catch, could have some problems during unload
6689 this.removeUnloadListener();
6690 // Work around FF3 remembering the last scroll position when refreshing the grid and then losing grid view
6692 var gridviews = Ext.ComponentQuery.query('gridview'),
6694 ln = gridviews.length;
6695 for (; i < ln; i++) {
6696 gridviews[i].scrollToTop();
6699 // Purge all elements in the cache
6703 if (cache.hasOwnProperty(el)) {
6704 Ext.EventManager.removeAll(el);
6712 * Removes the passed window unload listener.
6713 * @param {Function} fn The method the event invokes
6714 * @param {Object} scope The scope of handler
6716 removeUnloadListener: function(){
6717 if (this.unloadEvent) {
6718 this.removeListener(window, 'unload', this.fireUnload);
6723 * note 1: IE fires ONLY the keydown event on specialkey autorepeat
6724 * note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
6725 * (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
6728 useKeyDown: Ext.isWebKit ?
6729 parseInt(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1], 10) >= 525 :
6730 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera),
6733 * Indicates which event to use for getting key presses.
6734 * @return {String} The appropriate event name.
6736 getKeyEvent: function(){
6737 return this.useKeyDown ? 'keydown' : 'keypress';
6742 * Alias for {@link Ext.Loader#onReady Ext.Loader.onReady} with withDomReady set to true
6746 Ext.onReady = function(fn, scope, options) {
6747 Ext.Loader.onReady(fn, scope, true, options);
6751 * Alias for {@link Ext.EventManager#onDocumentReady Ext.EventManager.onDocumentReady}
6753 * @method onDocumentReady
6755 Ext.onDocumentReady = Ext.EventManager.onDocumentReady;
6758 * Alias for {@link Ext.EventManager#addListener Ext.EventManager.addListener}
6759 * @member Ext.EventManager
6762 Ext.EventManager.on = Ext.EventManager.addListener;
6765 * Alias for {@link Ext.EventManager#removeListener Ext.EventManager.removeListener}
6766 * @member Ext.EventManager
6769 Ext.EventManager.un = Ext.EventManager.removeListener;
6772 var initExtCss = function() {
6773 // find the body element
6774 var bd = document.body || document.getElementsByTagName('body')[0],
6775 baseCSSPrefix = Ext.baseCSSPrefix,
6784 html = bd.parentNode;
6786 //Let's keep this human readable!
6788 cls.push(baseCSSPrefix + 'ie');
6791 cls.push(baseCSSPrefix + 'ie6');
6794 cls.push(baseCSSPrefix + 'ie7');
6797 cls.push(baseCSSPrefix + 'ie8');
6800 cls.push(baseCSSPrefix + 'ie9');
6803 cls.push(baseCSSPrefix + 'gecko');
6806 cls.push(baseCSSPrefix + 'gecko3');
6809 cls.push(baseCSSPrefix + 'gecko4');
6812 cls.push(baseCSSPrefix + 'opera');
6815 cls.push(baseCSSPrefix + 'webkit');
6818 cls.push(baseCSSPrefix + 'safari');
6820 if (Ext.isSafari2) {
6821 cls.push(baseCSSPrefix + 'safari2');
6823 if (Ext.isSafari3) {
6824 cls.push(baseCSSPrefix + 'safari3');
6826 if (Ext.isSafari4) {
6827 cls.push(baseCSSPrefix + 'safari4');
6830 cls.push(baseCSSPrefix + 'chrome');
6833 cls.push(baseCSSPrefix + 'mac');
6836 cls.push(baseCSSPrefix + 'linux');
6838 if (!Ext.supports.CSS3BorderRadius) {
6839 cls.push(baseCSSPrefix + 'nbr');
6841 if (!Ext.supports.CSS3LinearGradient) {
6842 cls.push(baseCSSPrefix + 'nlg');
6844 if (!Ext.scopeResetCSS) {
6845 cls.push(baseCSSPrefix + 'reset');
6848 // add to the parent to allow for selectors x-strict x-border-box, also set the isBorderBox property correctly
6850 if (Ext.isStrict && (Ext.isIE6 || Ext.isIE7)) {
6851 Ext.isBorderBox = false;
6854 Ext.isBorderBox = true;
6857 htmlCls.push(baseCSSPrefix + (Ext.isBorderBox ? 'border-box' : 'strict'));
6858 if (!Ext.isStrict) {
6859 htmlCls.push(baseCSSPrefix + 'quirks');
6860 if (Ext.isIE && !Ext.isStrict) {
6861 Ext.isIEQuirks = true;
6864 Ext.fly(html, '_internal').addCls(htmlCls);
6867 Ext.fly(bd, '_internal').addCls(cls);
6871 Ext.onReady(initExtCss);
6875 * @class Ext.EventObject
6877 Just as {@link Ext.core.Element} wraps around a native DOM node, Ext.EventObject
6878 wraps the browser's native event-object normalizing cross-browser differences,
6879 such as which mouse button is clicked, keys pressed, mechanisms to stop
6880 event-propagation along with a method to prevent default actions from taking place.
6884 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
6886 var target = e.getTarget(); // same as t (the target HTMLElement)
6890 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.core.Element}
6891 myDiv.on( // 'on' is shorthand for addListener
6892 "click", // perform an action on click of myDiv
6893 handleClick // reference to the action handler
6896 // other methods to do the same:
6897 Ext.EventManager.on("myDiv", 'click', handleClick);
6898 Ext.EventManager.addListener("myDiv", 'click', handleClick);
6903 Ext.define('Ext.EventObjectImpl', {
6904 uses: ['Ext.util.Point'],
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 */
7044 /** Key constant @type Number */
7046 /** Key constant @type Number */
7048 /** Key constant @type Number */
7050 /** Key constant @type Number */
7052 /** Key constant @type Number */
7054 /** Key constant @type Number */
7056 /** Key constant @type Number */
7058 /** Key constant @type Number */
7060 /** Key constant @type Number */
7062 /** Key constant @type Number */
7064 /** Key constant @type Number */
7066 /** Key constant @type Number */
7068 /** Key constant @type Number */
7070 /** Key constant @type Number */
7072 /** Key constant @type Number */
7074 /** Key constant @type Number */
7076 /** Key constant @type Number */
7078 /** Key constant @type Number */
7082 * Simple click regex
7085 clickRe: /(dbl)?click/,
7086 // safari keypress events for special keys return bad keycodes
7093 63276: 33, // page up
7094 63277: 34, // page down
7095 63272: 46, // delete
7099 // normalize button clicks, don't see any way to feature detect this.
7100 btnMap: Ext.isIE ? {
7110 constructor: function(event, freezeEvent){
7112 this.setEvent(event.browserEvent || event, freezeEvent);
7116 setEvent: function(event, freezeEvent){
7117 var me = this, button, options;
7119 if (event == me || (event && event.browserEvent)) { // already wrapped
7122 me.browserEvent = event;
7124 // normalize buttons
7125 button = event.button ? me.btnMap[event.button] : (event.which ? event.which - 1 : -1);
7126 if (me.clickRe.test(event.type) && button == -1) {
7132 shiftKey: event.shiftKey,
7133 // mac metaKey behaves like ctrlKey
7134 ctrlKey: event.ctrlKey || event.metaKey || false,
7135 altKey: event.altKey,
7136 // in getKey these will be normalized for the mac
7137 keyCode: event.keyCode,
7138 charCode: event.charCode,
7139 // cache the targets for the delayed and or buffered events
7140 target: Ext.EventManager.getTarget(event),
7141 relatedTarget: Ext.EventManager.getRelatedTarget(event),
7142 currentTarget: event.currentTarget,
7143 xy: (freezeEvent ? me.getXY() : null)
7157 Ext.apply(me, options);
7162 * Stop the event (preventDefault and stopPropagation)
7164 stopEvent: function(){
7165 this.stopPropagation();
7166 this.preventDefault();
7170 * Prevents the browsers default handling of the event.
7172 preventDefault: function(){
7173 if (this.browserEvent) {
7174 Ext.EventManager.preventDefault(this.browserEvent);
7179 * Cancels bubbling of the event.
7181 stopPropagation: function(){
7182 var browserEvent = this.browserEvent;
7185 if (browserEvent.type == 'mousedown') {
7186 Ext.EventManager.stoppedMouseDownEvent.fire(this);
7188 Ext.EventManager.stopPropagation(browserEvent);
7193 * Gets the character code for the event.
7196 getCharCode: function(){
7197 return this.charCode || this.keyCode;
7201 * Returns a normalized keyCode for the event.
7202 * @return {Number} The key code
7205 return this.normalizeKey(this.keyCode || this.charCode);
7209 * Normalize key codes across browsers
7211 * @param {Number} key The key code
7212 * @return {Number} The normalized code
7214 normalizeKey: function(key){
7215 // can't feature detect this
7216 return Ext.isWebKit ? (this.safariKeys[key] || key) : key;
7220 * Gets the x coordinate of the event.
7222 * @deprecated 4.0 Replaced by {@link #getX}
7224 getPageX: function(){
7229 * Gets the y coordinate of the event.
7231 * @deprecated 4.0 Replaced by {@link #getY}
7233 getPageY: function(){
7238 * Gets the x coordinate of the event.
7242 return this.getXY()[0];
7246 * Gets the y coordinate of the event.
7250 return this.getXY()[1];
7254 * Gets the page coordinates of the event.
7255 * @return {Array} The xy values like [x, y]
7260 this.xy = Ext.EventManager.getPageXY(this.browserEvent);
7266 * Gets the target for the event.
7267 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7268 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
7269 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
7270 * @return {HTMLelement}
7272 getTarget : function(selector, maxDepth, returnEl){
7274 return Ext.fly(this.target).findParent(selector, maxDepth, returnEl);
7276 return returnEl ? Ext.get(this.target) : this.target;
7280 * Gets the related target.
7281 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7282 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 10 || document.body)
7283 * @param {Boolean} returnEl (optional) True to return a Ext.core.Element object instead of DOM node
7284 * @return {HTMLElement}
7286 getRelatedTarget : function(selector, maxDepth, returnEl){
7288 return Ext.fly(this.relatedTarget).findParent(selector, maxDepth, returnEl);
7290 return returnEl ? Ext.get(this.relatedTarget) : this.relatedTarget;
7294 * Normalizes mouse wheel delta across browsers
7295 * @return {Number} The delta
7297 getWheelDelta : function(){
7298 var event = this.browserEvent,
7301 if (event.wheelDelta) { /* IE/Opera. */
7302 delta = event.wheelDelta / 120;
7303 } else if (event.detail){ /* Mozilla case. */
7304 delta = -event.detail / 3;
7310 * 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.
7311 * Example usage:<pre><code>
7312 // Handle click on any child of an element
7313 Ext.getBody().on('click', function(e){
7314 if(e.within('some-el')){
7315 alert('Clicked on a child of some-el!');
7319 // Handle click directly on an element, ignoring clicks on child nodes
7320 Ext.getBody().on('click', function(e,t){
7321 if((t.id == 'some-el') && !e.within(t, true)){
7322 alert('Clicked directly on some-el!');
7326 * @param {Mixed} el The id, DOM element or Ext.core.Element to check
7327 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7328 * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
7331 within : function(el, related, allowEl){
7333 var t = related ? this.getRelatedTarget() : this.getTarget(),
7337 result = Ext.fly(el).contains(t);
7338 if (!result && allowEl) {
7339 result = t == Ext.getDom(el);
7348 * Checks if the key pressed was a "navigation" key
7349 * @return {Boolean} True if the press is a navigation keypress
7351 isNavKeyPress : function(){
7353 k = this.normalizeKey(me.keyCode);
7355 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
7362 * Checks if the key pressed was a "special" key
7363 * @return {Boolean} True if the press is a special keypress
7365 isSpecialKey : function(){
7366 var k = this.normalizeKey(this.keyCode);
7367 return (this.type == 'keypress' && this.ctrlKey) ||
7368 this.isNavKeyPress() ||
7369 (k == this.BACKSPACE) || // Backspace
7370 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
7371 (k >= 44 && k <= 46); // Print Screen, Insert, Delete
7375 * Returns a point object that consists of the object coordinates.
7376 * @return {Ext.util.Point} point
7378 getPoint : function(){
7379 var xy = this.getXY();
7380 return Ext.create('Ext.util.Point', xy[0], xy[1]);
7384 * Returns true if the control, meta, shift or alt key was pressed during this event.
7387 hasModifier : function(){
7388 return this.ctrlKey || this.altKey || this.shiftKey || this.metaKey;
7392 * Injects a DOM event using the data in this object and (optionally) a new target.
7393 * This is a low-level technique and not likely to be used by application code. The
7394 * currently supported event types are:
7395 * <p><b>HTMLEvents</b></p>
7406 * <p><b>MouseEvents</b></p>
7410 * <li>mousedown</li>
7412 * <li>mouseover</li>
7413 * <li>mousemove</li>
7416 * <p><b>UIEvents</b></p>
7424 * @param {Element/HTMLElement} target If specified, the target for the event. This
7425 * is likely to be used when relaying a DOM event. If not specified, {@link #getTarget}
7426 * is used to determine the target.
7428 injectEvent: function () {
7430 dispatchers = {}; // keyed by event type (e.g., 'mousedown')
7432 // Good reference: http://developer.yahoo.com/yui/docs/UserAction.js.html
7434 // IE9 has createEvent, but this code causes major problems with htmleditor (it
7435 // blocks all mouse events and maybe more). TODO
7437 if (!Ext.isIE && document.createEvent) { // if (DOM compliant)
7439 createHtmlEvent: function (doc, type, bubbles, cancelable) {
7440 var event = doc.createEvent('HTMLEvents');
7442 event.initEvent(type, bubbles, cancelable);
7446 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
7447 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
7448 button, relatedTarget) {
7449 var event = doc.createEvent('MouseEvents'),
7450 view = doc.defaultView || window;
7452 if (event.initMouseEvent) {
7453 event.initMouseEvent(type, bubbles, cancelable, view, detail,
7454 clientX, clientY, clientX, clientY, ctrlKey, altKey,
7455 shiftKey, metaKey, button, relatedTarget);
7456 } else { // old Safari
7457 event = doc.createEvent('UIEvents');
7458 event.initEvent(type, bubbles, cancelable);
7460 event.detail = detail;
7461 event.screenX = clientX;
7462 event.screenY = clientY;
7463 event.clientX = clientX;
7464 event.clientY = clientY;
7465 event.ctrlKey = ctrlKey;
7466 event.altKey = altKey;
7467 event.metaKey = metaKey;
7468 event.shiftKey = shiftKey;
7469 event.button = button;
7470 event.relatedTarget = relatedTarget;
7476 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
7477 var event = doc.createEvent('UIEvents'),
7478 view = doc.defaultView || window;
7480 event.initUIEvent(type, bubbles, cancelable, view, detail);
7484 fireEvent: function (target, type, event) {
7485 target.dispatchEvent(event);
7488 fixTarget: function (target) {
7489 // Safari3 doesn't have window.dispatchEvent()
7490 if (target == window && !target.dispatchEvent) {
7497 } else if (document.createEventObject) { // else if (IE)
7498 var crazyIEButtons = { 0: 1, 1: 4, 2: 2 };
7501 createHtmlEvent: function (doc, type, bubbles, cancelable) {
7502 var event = doc.createEventObject();
7503 event.bubbles = bubbles;
7504 event.cancelable = cancelable;
7508 createMouseEvent: function (doc, type, bubbles, cancelable, detail,
7509 clientX, clientY, ctrlKey, altKey, shiftKey, metaKey,
7510 button, relatedTarget) {
7511 var event = doc.createEventObject();
7512 event.bubbles = bubbles;
7513 event.cancelable = cancelable;
7514 event.detail = detail;
7515 event.screenX = clientX;
7516 event.screenY = clientY;
7517 event.clientX = clientX;
7518 event.clientY = clientY;
7519 event.ctrlKey = ctrlKey;
7520 event.altKey = altKey;
7521 event.shiftKey = shiftKey;
7522 event.metaKey = metaKey;
7523 event.button = crazyIEButtons[button] || button;
7524 event.relatedTarget = relatedTarget; // cannot assign to/fromElement
7528 createUIEvent: function (doc, type, bubbles, cancelable, detail) {
7529 var event = doc.createEventObject();
7530 event.bubbles = bubbles;
7531 event.cancelable = cancelable;
7535 fireEvent: function (target, type, event) {
7536 target.fireEvent('on' + type, event);
7539 fixTarget: function (target) {
7540 if (target == document) {
7541 // IE6,IE7 thinks window==document and doesn't have window.fireEvent()
7542 // IE6,IE7 cannot properly call document.fireEvent()
7543 return document.documentElement;
7555 load: [false, false],
7556 unload: [false, false],
7557 select: [true, false],
7558 change: [true, false],
7559 submit: [true, true],
7560 reset: [true, false],
7561 resize: [true, false],
7562 scroll: [true, false]
7564 function (name, value) {
7565 var bubbles = value[0], cancelable = value[1];
7566 dispatchers[name] = function (targetEl, srcEvent) {
7567 var e = API.createHtmlEvent(name, bubbles, cancelable);
7568 API.fireEvent(targetEl, name, e);
7575 function createMouseEventDispatcher (type, detail) {
7576 var cancelable = (type != 'mousemove');
7577 return function (targetEl, srcEvent) {
7578 var xy = srcEvent.getXY(),
7579 e = API.createMouseEvent(targetEl.ownerDocument, type, true, cancelable,
7580 detail, xy[0], xy[1], srcEvent.ctrlKey, srcEvent.altKey,
7581 srcEvent.shiftKey, srcEvent.metaKey, srcEvent.button,
7582 srcEvent.relatedTarget);
7583 API.fireEvent(targetEl, type, e);
7587 Ext.each(['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mousemove', 'mouseout'],
7588 function (eventName) {
7589 dispatchers[eventName] = createMouseEventDispatcher(eventName, 1);
7596 focusin: [true, false],
7597 focusout: [true, false],
7598 activate: [true, true],
7599 focus: [false, false],
7600 blur: [false, false]
7602 function (name, value) {
7603 var bubbles = value[0], cancelable = value[1];
7604 dispatchers[name] = function (targetEl, srcEvent) {
7605 var e = API.createUIEvent(targetEl.ownerDocument, name, bubbles, cancelable, 1);
7606 API.fireEvent(targetEl, name, e);
7612 // not even sure what ancient browsers fall into this category...
7614 dispatchers = {}; // never mind all those we just built :P
7617 fixTarget: function (t) {
7623 function cannotInject (target, srcEvent) {
7625 // TODO log something
7629 return function (target) {
7631 dispatcher = dispatchers[me.type] || cannotInject,
7632 t = target ? (target.dom || target) : me.getTarget();
7634 t = API.fixTarget(t);
7637 }() // call to produce method
7641 Ext.EventObject = new Ext.EventObjectImpl();
7647 * @class Ext.core.Element
7651 activeElement = null,
7652 isCSS1 = doc.compatMode == "CSS1Compat",
7653 ELEMENT = Ext.core.Element,
7656 _fly = new Ext.core.Element.Flyweight();
7662 // If the browser does not support document.activeElement we need some assistance.
7663 // This covers old Safari 3.2 (4.0 added activeElement along with just about all
7664 // other browsers). We need this support to handle issues with old Safari.
7665 if (!('activeElement' in doc) && doc.addEventListener) {
7666 doc.addEventListener('focus',
7668 if (ev && ev.target) {
7669 activeElement = (ev.target == doc) ? null : ev.target;
7675 * Helper function to create the function that will restore the selection.
7677 function makeSelectionRestoreFn (activeEl, start, end) {
7678 return function () {
7679 activeEl.selectionStart = start;
7680 activeEl.selectionEnd = end;
7684 Ext.apply(ELEMENT, {
7685 isAncestor : function(p, c) {
7692 return p.contains(c);
7693 } else if (p.compareDocumentPosition) {
7694 return !!(p.compareDocumentPosition(c) & 16);
7696 while ((c = c.parentNode)) {
7697 ret = c == p || ret;
7705 * Returns the active element in the DOM. If the browser supports activeElement
7706 * on the document, this is returned. If not, the focus is tracked and the active
7707 * element is maintained internally.
7708 * @return {HTMLElement} The active (focused) element in the document.
7710 getActiveElement: function () {
7711 return doc.activeElement || activeElement;
7715 * Creates a function to call to clean up problems with the work-around for the
7716 * WebKit RightMargin bug. The work-around is to add "display: 'inline-block'" to
7717 * the element before calling getComputedStyle and then to restore its original
7718 * display value. The problem with this is that it corrupts the selection of an
7719 * INPUT or TEXTAREA element (as in the "I-beam" goes away but ths focus remains).
7720 * To cleanup after this, we need to capture the selection of any such element and
7721 * then restore it after we have restored the display style.
7723 * @param target {Element} The top-most element being adjusted.
7726 getRightMarginFixCleaner: function (target) {
7727 var supports = Ext.supports,
7728 hasInputBug = supports.DisplayChangeInputSelectionBug,
7729 hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug;
7731 if (hasInputBug || hasTextAreaBug) {
7732 var activeEl = doc.activeElement || activeElement, // save a call
7733 tag = activeEl && activeEl.tagName,
7737 if ((hasTextAreaBug && tag == 'TEXTAREA') ||
7738 (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
7739 if (ELEMENT.isAncestor(target, activeEl)) {
7740 start = activeEl.selectionStart;
7741 end = activeEl.selectionEnd;
7743 if (Ext.isNumber(start) && Ext.isNumber(end)) { // to be safe...
7744 // We don't create the raw closure here inline because that
7745 // will be costly even if we don't want to return it (nested
7746 // function decls and exprs are often instantiated on entry
7747 // regardless of whether execution ever reaches them):
7748 return makeSelectionRestoreFn(activeEl, start, end);
7754 return Ext.emptyFn; // avoid special cases, just return a nop
7757 getViewWidth : function(full) {
7758 return full ? ELEMENT.getDocumentWidth() : ELEMENT.getViewportWidth();
7761 getViewHeight : function(full) {
7762 return full ? ELEMENT.getDocumentHeight() : ELEMENT.getViewportHeight();
7765 getDocumentHeight: function() {
7766 return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, ELEMENT.getViewportHeight());
7769 getDocumentWidth: function() {
7770 return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, ELEMENT.getViewportWidth());
7773 getViewportHeight: function(){
7775 (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
7779 getViewportWidth : function() {
7780 return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
7781 Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
7784 getY : function(el) {
7785 return ELEMENT.getXY(el)[1];
7788 getX : function(el) {
7789 return ELEMENT.getXY(el)[0];
7792 getXY : function(el) {
7803 bd = (doc.body || doc.documentElement),
7806 el = Ext.getDom(el);
7809 hasAbsolute = fly(el).isStyle("position", "absolute");
7811 if (el.getBoundingClientRect) {
7812 b = el.getBoundingClientRect();
7813 scroll = fly(document).getScroll();
7814 ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
7823 hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");
7826 y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
7827 x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
7829 if (p != el && !pe.isStyle('overflow','visible')) {
7837 if (Ext.isSafari && hasAbsolute) {
7842 if (Ext.isGecko && !hasAbsolute) {
7844 x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
7845 y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
7849 while (p && p != bd) {
7850 if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
7862 setXY : function(el, xy) {
7863 (el = Ext.fly(el, '_setXY')).position();
7865 var pts = el.translatePoints(xy),
7866 style = el.dom.style,
7870 if (!isNaN(pts[pos])) {
7871 style[pos] = pts[pos] + "px";
7876 setX : function(el, x) {
7877 ELEMENT.setXY(el, [x, false]);
7880 setY : function(el, y) {
7881 ELEMENT.setXY(el, [false, y]);
7885 * Serializes a DOM form into a url encoded string
7886 * @param {Object} form The form
7887 * @return {String} The url encoded form
7889 serializeForm: function(form) {
7890 var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
7892 encoder = encodeURIComponent,
7898 Ext.each(fElements, function(element){
7899 name = element.name;
7900 type = element.type;
7902 if (!element.disabled && name) {
7903 if (/select-(one|multiple)/i.test(type)) {
7904 Ext.each(element.options, function(opt){
7906 hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
7907 data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
7910 } else if (!(/file|undefined|reset|button/i.test(type))) {
7911 if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
7912 data += encoder(name) + '=' + encoder(element.value) + '&';
7913 hasSubmit = /submit/i.test(type);
7918 return data.substr(0, data.length - 1);
7924 * @class Ext.core.Element
7927 Ext.core.Element.addMethods({
7930 * Monitors this Element for the mouse leaving. Calls the function after the specified delay only if
7931 * the mouse was not moved back into the Element within the delay. If the mouse <i>was</i> moved
7932 * back in, the function is not called.
7933 * @param {Number} delay The delay <b>in milliseconds</b> to wait for possible mouse re-entry before calling the handler function.
7934 * @param {Function} handler The function to call if the mouse remains outside of this Element for the specified time.
7935 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to this Element.
7936 * @return {Object} The listeners object which was added to this element so that monitoring can be stopped. Example usage:</pre><code>
7937 // Hide the menu if the mouse moves out for 250ms or more
7938 this.mouseLeaveMonitor = this.menuEl.monitorMouseLeave(250, this.hideMenu, this);
7941 // Remove mouseleave monitor on menu destroy
7942 this.menuEl.un(this.mouseLeaveMonitor);
7945 monitorMouseLeave: function(delay, handler, scope) {
7949 mouseleave: function(e) {
7950 timer = setTimeout(Ext.Function.bind(handler, scope||me, [e]), delay);
7952 mouseenter: function() {
7953 clearTimeout(timer);
7963 * Stops the specified event(s) from bubbling and optionally prevents the default action
7964 * @param {String/Array} eventName an event / array of events to stop from bubbling
7965 * @param {Boolean} preventDefault (optional) true to prevent the default action too
7966 * @return {Ext.core.Element} this
7968 swallowEvent : function(eventName, preventDefault) {
7971 e.stopPropagation();
7972 if (preventDefault) {
7977 if (Ext.isArray(eventName)) {
7978 Ext.each(eventName, function(e) {
7983 me.on(eventName, fn);
7988 * Create an event handler on this element such that when the event fires and is handled by this element,
7989 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
7990 * @param {String} eventName The type of event to relay
7991 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
7992 * for firing the relayed event
7994 relayEvent : function(eventName, observable) {
7995 this.on(eventName, function(e) {
7996 observable.fireEvent(eventName, e);
8001 * Removes Empty, or whitespace filled text nodes. Combines adjacent text nodes.
8002 * @param {Boolean} forceReclean (optional) By default the element
8003 * keeps track if it has been cleaned already so
8004 * you can call this over and over. However, if you update the element and
8005 * need to force a reclean, you can pass true.
8007 clean : function(forceReclean) {
8014 if (Ext.core.Element.data(dom, 'isCleaned') && forceReclean !== true) {
8020 if (n.nodeType == 3) {
8021 // Remove empty/whitespace text nodes
8022 if (!(/\S/.test(n.nodeValue))) {
8024 // Combine adjacent text nodes
8025 } else if (nx && nx.nodeType == 3) {
8026 n.appendData(Ext.String.trim(nx.data));
8027 dom.removeChild(nx);
8032 // Recursively clean
8039 Ext.core.Element.data(dom, 'isCleaned', true);
8044 * Direct access to the Ext.ElementLoader {@link Ext.ElementLoader#load} method. The method takes the same object
8045 * parameter as {@link Ext.ElementLoader#load}
8046 * @return {Ext.core.Element} this
8048 load : function(options) {
8049 this.getLoader().load(options);
8054 * Gets this element's {@link Ext.ElementLoader ElementLoader}
8055 * @return {Ext.ElementLoader} The loader
8057 getLoader : function() {
8059 data = Ext.core.Element.data,
8060 loader = data(dom, 'loader');
8063 loader = Ext.create('Ext.ElementLoader', {
8066 data(dom, 'loader', loader);
8072 * Update the innerHTML of this element, optionally searching for and processing scripts
8073 * @param {String} html The new HTML
8074 * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
8075 * @param {Function} callback (optional) For async script loading you can be notified when the update completes
8076 * @return {Ext.core.Element} this
8078 update : function(html, loadScripts, callback) {
8090 if (loadScripts !== true) {
8091 dom.innerHTML = html;
8092 Ext.callback(callback, me);
8097 html += '<span id="' + id + '"></span>';
8099 interval = setInterval(function(){
8100 if (!document.getElementById(id)) {
8103 clearInterval(interval);
8105 hd = DOC.getElementsByTagName("head")[0],
8106 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
8107 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
8108 typeRe = /\stype=([\'\"])(.*?)\1/i,
8116 while ((match = re.exec(html))) {
8118 srcMatch = attrs ? attrs.match(srcRe) : false;
8119 if (srcMatch && srcMatch[2]) {
8120 s = DOC.createElement("script");
8121 s.src = srcMatch[2];
8122 typeMatch = attrs.match(typeRe);
8123 if (typeMatch && typeMatch[2]) {
8124 s.type = typeMatch[2];
8127 } else if (match[2] && match[2].length > 0) {
8128 if (window.execScript) {
8129 window.execScript(match[2]);
8131 window.eval(match[2]);
8136 el = DOC.getElementById(id);
8140 Ext.callback(callback, me);
8142 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, '');
8146 // inherit docs, overridden so we can add removeAnchor
8147 removeAllListeners : function() {
8148 this.removeAnchor();
8149 Ext.EventManager.removeAll(this.dom);
8154 * Creates a proxy element of this element
8155 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8156 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8157 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8158 * @return {Ext.core.Element} The new proxy element
8160 createProxy : function(config, renderTo, matchBox) {
8161 config = (typeof config == 'object') ? config : {tag : "div", cls: config};
8164 proxy = renderTo ? Ext.core.DomHelper.append(renderTo, config, true) :
8165 Ext.core.DomHelper.insertBefore(me.dom, config, true);
8167 proxy.setVisibilityMode(Ext.core.Element.DISPLAY);
8169 if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
8170 proxy.setBox(me.getBox());
8175 Ext.core.Element.prototype.clearListeners = Ext.core.Element.prototype.removeAllListeners;
8178 * @class Ext.core.Element
8180 Ext.core.Element.addMethods({
8182 * Gets the x,y coordinates specified by the anchor position on the element.
8183 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo}
8184 * for details on supported anchor positions.
8185 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
8186 * of page coordinates
8187 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8188 * {width: (target width), height: (target height)} (defaults to the element's current size)
8189 * @return {Array} [x, y] An array containing the element's x and y coordinates
8191 getAnchorXY : function(anchor, local, s){
8192 //Passing a different size is useful for pre-calculating anchors,
8193 //especially for anchored animations that change the el size.
8194 anchor = (anchor || "tl").toLowerCase();
8198 vp = me.dom == document.body || me.dom == document,
8199 w = s.width || vp ? Ext.core.Element.getViewWidth() : me.getWidth(),
8200 h = s.height || vp ? Ext.core.Element.getViewHeight() : me.getHeight(),
8204 scroll = me.getScroll(),
8205 extraX = vp ? scroll.left : !local ? o[0] : 0,
8206 extraY = vp ? scroll.top : !local ? o[1] : 0,
8208 c : [r(w * 0.5), r(h * 0.5)],
8209 t : [r(w * 0.5), 0],
8210 l : [0, r(h * 0.5)],
8211 r : [w, r(h * 0.5)],
8212 b : [r(w * 0.5), h],
8220 return [xy[0] + extraX, xy[1] + extraY];
8224 * Anchors an element to another element and realigns it when the window is resized.
8225 * @param {Mixed} element The element to align to.
8226 * @param {String} position The position to align to.
8227 * @param {Array} offsets (optional) Offset the positioning by [x, y]
8228 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8229 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8230 * is a number, it is used as the buffer delay (defaults to 50ms).
8231 * @param {Function} callback The function to call after the animation finishes
8232 * @return {Ext.core.Element} this
8234 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8237 scroll = !Ext.isEmpty(monitorScroll),
8238 action = function(){
8239 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
8240 Ext.callback(callback, Ext.fly(dom));
8242 anchor = this.getAnchor();
8244 // previous listener anchor, remove it
8245 this.removeAnchor();
8251 Ext.EventManager.onWindowResize(action, null);
8254 Ext.EventManager.on(window, 'scroll', action, null,
8255 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
8257 action.call(me); // align immediately
8262 * Remove any anchor to this element. See {@link #anchorTo}.
8263 * @return {Ext.core.Element} this
8265 removeAnchor : function(){
8267 anchor = this.getAnchor();
8269 if(anchor && anchor.fn){
8270 Ext.EventManager.removeResizeListener(anchor.fn);
8272 Ext.EventManager.un(window, 'scroll', anchor.fn);
8280 getAnchor : function(){
8281 var data = Ext.core.Element.data,
8286 var anchor = data(dom, '_anchor');
8289 anchor = data(dom, '_anchor', {});
8294 getAlignVector: function(el, spec, offset) {
8296 side = {t:"top", l:"left", r:"right", b: "bottom"},
8297 thisRegion = me.getRegion(),
8304 sourceClass: 'Ext.core.Element',
8305 sourceMethod: 'getAlignVector',
8306 msg: 'Attempted to align an element that doesn\'t exist'
8311 elRegion = el.getRegion();
8315 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8316 * supported position values.
8317 * @param {Mixed} element The element to align to.
8318 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
8319 * @param {Array} offsets (optional) Offset the positioning by [x, y]
8320 * @return {Array} [x, y]
8322 getAlignToXY : function(el, p, o){
8328 sourceClass: 'Ext.core.Element',
8329 sourceMethod: 'getAlignToXY',
8330 msg: 'Attempted to align an element that doesn\'t exist'
8336 p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
8344 //constrain the aligned el to viewport if necessary
8348 dw = Ext.core.Element.getViewWidth() -10, // 10px of margin for ie
8349 dh = Ext.core.Element.getViewHeight()-10, // 10px of margin for ie
8357 docElement = doc.documentElement,
8359 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
8360 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
8361 c = false, //constrain to viewport
8364 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8369 sourceClass: 'Ext.core.Element',
8370 sourceMethod: 'getAlignToXY',
8374 msg: 'Attemmpted to align an element with an invalid position: "' + p + '"'
8383 //Subtract the aligned el's internal xy from the target's offset xy
8384 //plus custom offset to get the aligned el's new offset xy
8385 a1 = me.getAnchorXY(p1, true);
8386 a2 = el.getAnchorXY(p2, false);
8388 x = a2[0] - a1[0] + o[0];
8389 y = a2[1] - a1[1] + o[1];
8395 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8396 //perpendicular to the vp border, allow the aligned el to slide on that border,
8397 //otherwise swap the aligned el to the opposite border of the target.
8399 p1x = p1.charAt(p1.length-1);
8401 p2x = p2.charAt(p2.length-1);
8402 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8403 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8406 if (x + w > dw + scrollX) {
8407 x = swapX ? r.left-w : dw+scrollX-w;
8410 x = swapX ? r.right : scrollX;
8412 if (y + h > dh + scrollY) {
8413 y = swapY ? r.top-h : dh+scrollY-h;
8416 y = swapY ? r.bottom : scrollY;
8423 * Aligns this element with another element relative to the specified anchor points. If the other element is the
8424 * document it aligns it to the viewport.
8425 * The position parameter is optional, and can be specified in any one of the following formats:
8427 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8428 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8429 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
8430 * deprecated in favor of the newer two anchor syntax below</i>.</li>
8431 * <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
8432 * element's anchor point, and the second value is used as the target's anchor point.</li>
8434 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
8435 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8436 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
8437 * that specified in order to enforce the viewport constraints.
8438 * Following are all of the supported anchor positions:
8441 ----- -----------------------------
8442 tl The top left corner (default)
8443 t The center of the top edge
8444 tr The top right corner
8445 l The center of the left edge
8446 c In the center of the element
8447 r The center of the right edge
8448 bl The bottom left corner
8449 b The center of the bottom edge
8450 br The bottom right corner
8454 // align el to other-el using the default positioning ("tl-bl", non-constrained)
8455 el.alignTo("other-el");
8457 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8458 el.alignTo("other-el", "tr?");
8460 // align the bottom right corner of el with the center left edge of other-el
8461 el.alignTo("other-el", "br-l?");
8463 // align the center of el with the bottom left corner of other-el and
8464 // adjust the x position by -6 pixels (and the y position by 0)
8465 el.alignTo("other-el", "c-bl", [-6, 0]);
8467 * @param {Mixed} element The element to align to.
8468 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
8469 * @param {Array} offsets (optional) Offset the positioning by [x, y]
8470 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8471 * @return {Ext.core.Element} this
8473 alignTo : function(element, position, offsets, animate){
8475 return me.setXY(me.getAlignToXY(element, position, offsets),
8476 me.anim && !!animate ? me.anim(animate) : false);
8479 // private ==> used outside of core
8480 adjustForConstraints : function(xy, parent) {
8481 var vector = this.getConstrainVector(parent, xy);
8490 * <p>Returns the <code>[X, Y]</code> vector by which this element must be translated to make a best attempt
8491 * to constrain within the passed constraint. Returns <code>false</code> is this element does not need to be moved.</p>
8492 * <p>Priority is given to constraining the top and left within the constraint.</p>
8493 * <p>The constraint may either be an existing element into which this element is to be constrained, or
8494 * an {@link Ext.util.Region Region} into which this element is to be constrained.</p>
8495 * @param constrainTo {Mixed} The Element or {@link Ext.util.Region Region} into which this element is to be constrained.
8496 * @param proposedPosition {Array} A proposed <code>[X, Y]</code> position to test for validity and to produce a vector for instead
8497 * of using this Element's current position;
8498 * @returns {Array} <b>If</b> this element <i>needs</i> to be translated, an <code>[X, Y]</code>
8499 * vector by which this element must be translated. Otherwise, <code>false</code>.
8501 getConstrainVector: function(constrainTo, proposedPosition) {
8502 if (!(constrainTo instanceof Ext.util.Region)) {
8503 constrainTo = Ext.get(constrainTo).getViewRegion();
8505 var thisRegion = this.getRegion(),
8507 shadowSize = this.shadow && this.shadow.offset,
8510 // Shift this region to occupy the proposed position
8511 if (proposedPosition) {
8512 thisRegion.translateBy(proposedPosition[0] - thisRegion.x, proposedPosition[1] - thisRegion.y);
8515 // Reduce the constrain region to allow for shadow
8516 // TODO: Rewrite the Shadow class. When that's done, get the extra for each side from the Shadow.
8518 constrainTo.adjust(0, -shadowSize, -shadowSize, shadowSize);
8521 // Constrain the X coordinate by however much this Element overflows
8522 if (thisRegion.right > constrainTo.right) {
8524 vector[0] = (constrainTo.right - thisRegion.right); // overflowed the right
8526 if (thisRegion.left + vector[0] < constrainTo.left) {
8528 vector[0] = (constrainTo.left - thisRegion.left); // overflowed the left
8531 // Constrain the Y coordinate by however much this Element overflows
8532 if (thisRegion.bottom > constrainTo.bottom) {
8534 vector[1] = (constrainTo.bottom - thisRegion.bottom); // overflowed the bottom
8536 if (thisRegion.top + vector[1] < constrainTo.top) {
8538 vector[1] = (constrainTo.top - thisRegion.top); // overflowed the top
8540 return overflowed ? vector : false;
8544 * Calculates the x, y to center this element on the screen
8545 * @return {Array} The x, y values [x, y]
8547 getCenterXY : function(){
8548 return this.getAlignToXY(document, 'c-c');
8552 * Centers the Element in either the viewport, or another Element.
8553 * @param {Mixed} centerIn (optional) The element in which to center the element.
8555 center : function(centerIn){
8556 return this.alignTo(centerIn || document, 'c-c');
8561 * @class Ext.core.Element
8565 var ELEMENT = Ext.core.Element,
8570 POSITION = "position",
8572 RELATIVE = "relative",
8576 Ext.override(Ext.core.Element, {
8578 * 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).
8579 * @return {Number} The X position of the element
8582 return ELEMENT.getX(this.dom);
8586 * 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).
8587 * @return {Number} The Y position of the element
8590 return ELEMENT.getY(this.dom);
8594 * 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).
8595 * @return {Array} The XY position of the element
8598 return ELEMENT.getXY(this.dom);
8602 * 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.
8603 * @param {Mixed} element The element to get the offsets from.
8604 * @return {Array} The XY page offsets (e.g. [100, -200])
8606 getOffsetsTo : function(el){
8607 var o = this.getXY(),
8608 e = Ext.fly(el, '_internal').getXY();
8609 return [o[0]-e[0],o[1]-e[1]];
8613 * 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).
8614 * @param {Number} The X position of the element
8615 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8616 * @return {Ext.core.Element} this
8618 setX : function(x, animate){
8619 return this.setXY([x, this.getY()], animate);
8623 * 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).
8624 * @param {Number} The Y position of the element
8625 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8626 * @return {Ext.core.Element} this
8628 setY : function(y, animate){
8629 return this.setXY([this.getX(), y], animate);
8633 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8634 * @param {String} left The left CSS property value
8635 * @return {Ext.core.Element} this
8637 setLeft : function(left){
8638 this.setStyle(LEFT, this.addUnits(left));
8643 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8644 * @param {String} top The top CSS property value
8645 * @return {Ext.core.Element} this
8647 setTop : function(top){
8648 this.setStyle(TOP, this.addUnits(top));
8653 * Sets the element's CSS right style.
8654 * @param {String} right The right CSS property value
8655 * @return {Ext.core.Element} this
8657 setRight : function(right){
8658 this.setStyle(RIGHT, this.addUnits(right));
8663 * Sets the element's CSS bottom style.
8664 * @param {String} bottom The bottom CSS property value
8665 * @return {Ext.core.Element} this
8667 setBottom : function(bottom){
8668 this.setStyle(BOTTOM, this.addUnits(bottom));
8673 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8674 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8675 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8676 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8677 * @return {Ext.core.Element} this
8679 setXY: function(pos, animate) {
8681 if (!animate || !me.anim) {
8682 ELEMENT.setXY(me.dom, pos);
8685 if (!Ext.isObject(animate)) {
8688 me.animate(Ext.applyIf({ to: { x: pos[0], y: pos[1] } }, animate));
8694 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8695 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8696 * @param {Number} x X value for new position (coordinates are page-based)
8697 * @param {Number} y Y value for new position (coordinates are page-based)
8698 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8699 * @return {Ext.core.Element} this
8701 setLocation : function(x, y, animate){
8702 return this.setXY([x, y], animate);
8706 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8707 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8708 * @param {Number} x X value for new position (coordinates are page-based)
8709 * @param {Number} y Y value for new position (coordinates are page-based)
8710 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8711 * @return {Ext.core.Element} this
8713 moveTo : function(x, y, animate){
8714 return this.setXY([x, y], animate);
8718 * Gets the left X coordinate
8719 * @param {Boolean} local True to get the local css position instead of page coordinate
8722 getLeft : function(local){
8723 return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
8727 * Gets the right X coordinate of the element (element X position + element width)
8728 * @param {Boolean} local True to get the local css position instead of page coordinate
8731 getRight : function(local){
8733 return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
8737 * Gets the top Y coordinate
8738 * @param {Boolean} local True to get the local css position instead of page coordinate
8741 getTop : function(local) {
8742 return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
8746 * Gets the bottom Y coordinate of the element (element Y position + element height)
8747 * @param {Boolean} local True to get the local css position instead of page coordinate
8750 getBottom : function(local){
8752 return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
8756 * Initializes positioning on this element. If a desired position is not passed, it will make the
8757 * the element positioned relative IF it is not already positioned.
8758 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8759 * @param {Number} zIndex (optional) The zIndex to apply
8760 * @param {Number} x (optional) Set the page X position
8761 * @param {Number} y (optional) Set the page Y position
8763 position : function(pos, zIndex, x, y) {
8766 if (!pos && me.isStyle(POSITION, STATIC)){
8767 me.setStyle(POSITION, RELATIVE);
8769 me.setStyle(POSITION, pos);
8772 me.setStyle(ZINDEX, zIndex);
8775 me.setXY([x || false, y || false]);
8780 * Clear positioning back to the default when the document was loaded
8781 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8782 * @return {Ext.core.Element} this
8784 clearPositioning : function(value){
8785 value = value || '';
8798 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8799 * snapshot before performing an update and then restoring the element.
8802 getPositioning : function(){
8803 var l = this.getStyle(LEFT);
8804 var t = this.getStyle(TOP);
8806 "position" : this.getStyle(POSITION),
8808 "right" : l ? "" : this.getStyle(RIGHT),
8810 "bottom" : t ? "" : this.getStyle(BOTTOM),
8811 "z-index" : this.getStyle(ZINDEX)
8816 * Set positioning with an object returned by getPositioning().
8817 * @param {Object} posCfg
8818 * @return {Ext.core.Element} this
8820 setPositioning : function(pc){
8822 style = me.dom.style;
8826 if(pc.right == AUTO){
8829 if(pc.bottom == AUTO){
8837 * Translates the passed page coordinates into left/top css values for this element
8838 * @param {Number/Array} x The page x or an array containing [x, y]
8839 * @param {Number} y (optional) The page y, required if x is not an array
8840 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
8842 translatePoints: function(x, y) {
8843 if (Ext.isArray(x)) {
8848 relative = me.isStyle(POSITION, RELATIVE),
8850 left = parseInt(me.getStyle(LEFT), 10),
8851 top = parseInt(me.getStyle(TOP), 10);
8853 if (!Ext.isNumber(left)) {
8854 left = relative ? 0 : me.dom.offsetLeft;
8856 if (!Ext.isNumber(top)) {
8857 top = relative ? 0 : me.dom.offsetTop;
8859 left = (Ext.isNumber(x)) ? x - o[0] + left : undefined;
8860 top = (Ext.isNumber(y)) ? y - o[1] + top : undefined;
8868 * 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.
8869 * @param {Object} box The box to fill {x, y, width, height}
8870 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8871 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8872 * @return {Ext.core.Element} this
8874 setBox: function(box, adjust, animate) {
8878 if ((adjust && !me.autoBoxAdjust) && !me.isBorderBox()) {
8879 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
8880 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
8882 me.setBounds(box.x, box.y, w, h, animate);
8887 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
8888 * set another Element's size/location to match this element.
8889 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8890 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8891 * @return {Object} box An object in the format<pre><code>
8893 x: <Element's X position>,
8894 y: <Element's Y position>,
8895 width: <Element's width>,
8896 height: <Element's height>,
8897 bottom: <Element's lower bound>,
8898 right: <Element's rightmost bound>
8901 * The returned object may also be addressed as an Array where index 0 contains the X position
8902 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
8904 getBox: function(contentBox, local) {
8909 getBorderWidth = me.getBorderWidth,
8910 getPadding = me.getPadding,
8911 l, r, t, b, w, h, bx;
8915 left = parseInt(me.getStyle("left"), 10) || 0;
8916 top = parseInt(me.getStyle("top"), 10) || 0;
8931 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
8932 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
8933 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
8934 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
8944 bx.right = bx.x + bx.width;
8945 bx.bottom = bx.y + bx.height;
8950 * Move this element relative to its current position.
8951 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
8952 * @param {Number} distance How far to move the element in pixels
8953 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8954 * @return {Ext.core.Element} this
8956 move: function(direction, distance, animate) {
8961 left = [x - distance, y],
8962 right = [x + distance, y],
8963 top = [x, y - distance],
8964 bottom = [x, y + distance],
8978 direction = direction.toLowerCase();
8979 me.moveTo(hash[direction][0], hash[direction][1], animate);
8983 * Quick set left and top adding default units
8984 * @param {String} left The left CSS property value
8985 * @param {String} top The top CSS property value
8986 * @return {Ext.core.Element} this
8988 setLeftTop: function(left, top) {
8990 style = me.dom.style;
8991 style.left = me.addUnits(left);
8992 style.top = me.addUnits(top);
8997 * Returns the region of this element.
8998 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8999 * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
9001 getRegion: function() {
9002 return this.getPageBox(true);
9006 * Returns the <b>content</b> region of this element. That is the region within the borders and padding.
9007 * @return {Region} A Ext.util.Region containing "top, left, bottom, right" member data.
9009 getViewRegion: function() {
9011 isBody = me.dom === document.body,
9012 scroll, pos, top, left, width, height;
9014 // For the body we want to do some special logic
9016 scroll = me.getScroll();
9019 width = Ext.core.Element.getViewportWidth();
9020 height = Ext.core.Element.getViewportHeight();
9024 left = pos[0] + me.getBorderWidth('l') + me.getPadding('l');
9025 top = pos[1] + me.getBorderWidth('t') + me.getPadding('t');
9026 width = me.getWidth(true);
9027 height = me.getHeight(true);
9030 return Ext.create('Ext.util.Region', top, left + width, top + height, left);
9034 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
9035 * set another Element's size/location to match this element.
9036 * @param {Boolean} asRegion(optional) If true an Ext.util.Region will be returned
9037 * @return {Object} box An object in the format<pre><code>
9039 x: <Element's X position>,
9040 y: <Element's Y position>,
9041 width: <Element's width>,
9042 height: <Element's height>,
9043 bottom: <Element's lower bound>,
9044 right: <Element's rightmost bound>
9047 * The returned object may also be addressed as an Array where index 0 contains the X position
9048 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
9050 getPageBox : function(getRegion) {
9053 isDoc = el === document.body,
9054 w = isDoc ? Ext.core.Element.getViewWidth() : el.offsetWidth,
9055 h = isDoc ? Ext.core.Element.getViewHeight() : el.offsetHeight,
9063 return Ext.create('Ext.util.Region', t, r, b, l);
9078 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9079 * @param {Number} x X value for new position (coordinates are page-based)
9080 * @param {Number} y Y value for new position (coordinates are page-based)
9081 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
9082 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
9083 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
9085 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
9086 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
9087 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
9089 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9090 * @return {Ext.core.Element} this
9092 setBounds: function(x, y, width, height, animate) {
9094 if (!animate || !me.anim) {
9095 me.setSize(width, height);
9096 me.setLocation(x, y);
9098 if (!Ext.isObject(animate)) {
9101 me.animate(Ext.applyIf({
9105 width: me.adjustWidth(width),
9106 height: me.adjustHeight(height)
9114 * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
9115 * @param {Ext.util.Region} region The region to fill
9116 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9117 * @return {Ext.core.Element} this
9119 setRegion: function(region, animate) {
9120 return this.setBounds(region.left, region.top, region.right - region.left, region.bottom - region.top, animate);
9126 * @class Ext.core.Element
9128 Ext.override(Ext.core.Element, {
9130 * Returns true if this element is scrollable.
9133 isScrollable : function(){
9135 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9139 * Returns the current scroll position of the element.
9140 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9142 getScroll : function() {
9146 docElement = doc.documentElement,
9151 if (d == doc || d == body) {
9152 if (Ext.isIE && Ext.isStrict) {
9153 l = docElement.scrollLeft;
9154 t = docElement.scrollTop;
9156 l = window.pageXOffset;
9157 t = window.pageYOffset;
9160 left: l || (body ? body.scrollLeft : 0),
9161 top : t || (body ? body.scrollTop : 0)
9174 * 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().
9175 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9176 * @param {Number} value The new scroll value
9177 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9178 * @return {Element} this
9180 scrollTo : function(side, value, animate) {
9181 //check if we're scrolling top or left
9182 var top = /top/i.test(side),
9187 if (!animate || !me.anim) {
9188 // just setting the value, so grab the direction
9189 prop = 'scroll' + (top ? 'Top' : 'Left');
9193 if (!Ext.isObject(animate)) {
9196 obj['scroll' + (top ? 'Top' : 'Left')] = value;
9197 me.animate(Ext.applyIf({
9205 * Scrolls this element into view within the passed container.
9206 * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a
9207 * string (id), dom node, or Ext.core.Element.
9208 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
9209 * @return {Ext.core.Element} this
9211 scrollIntoView : function(container, hscroll) {
9212 container = Ext.getDom(container) || Ext.getBody().dom;
9214 offsets = this.getOffsetsTo(container),
9216 left = offsets[0] + container.scrollLeft,
9217 top = offsets[1] + container.scrollTop,
9218 bottom = top + el.offsetHeight,
9219 right = left + el.offsetWidth,
9221 ctClientHeight = container.clientHeight,
9222 ctScrollTop = parseInt(container.scrollTop, 10),
9223 ctScrollLeft = parseInt(container.scrollLeft, 10),
9224 ctBottom = ctScrollTop + ctClientHeight,
9225 ctRight = ctScrollLeft + container.clientWidth;
9227 if (el.offsetHeight > ctClientHeight || top < ctScrollTop) {
9228 container.scrollTop = top;
9229 } else if (bottom > ctBottom) {
9230 container.scrollTop = bottom - ctClientHeight;
9232 // corrects IE, other browsers will ignore
9233 container.scrollTop = container.scrollTop;
9235 if (hscroll !== false) {
9236 if (el.offsetWidth > container.clientWidth || left < ctScrollLeft) {
9237 container.scrollLeft = left;
9239 else if (right > ctRight) {
9240 container.scrollLeft = right - container.clientWidth;
9242 container.scrollLeft = container.scrollLeft;
9248 scrollChildIntoView : function(child, hscroll) {
9249 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
9253 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9254 * within this element's scrollable range.
9255 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
9256 * @param {Number} distance How far to scroll the element in pixels
9257 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9258 * @return {Boolean} Returns true if a scroll was triggered or false if the element
9259 * was scrolled as far as it could go.
9261 scroll : function(direction, distance, animate) {
9262 if (!this.isScrollable()) {
9266 l = el.scrollLeft, t = el.scrollTop,
9267 w = el.scrollWidth, h = el.scrollHeight,
9268 cw = el.clientWidth, ch = el.clientHeight,
9269 scrolled = false, v,
9271 l: Math.min(l + distance, w-cw),
9272 r: v = Math.max(l - distance, 0),
9273 t: Math.max(t - distance, 0),
9274 b: Math.min(t + distance, h-ch)
9279 direction = direction.substr(0, 1);
9280 if ((v = hash[direction]) > -1) {
9282 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.anim(animate));
9288 * @class Ext.core.Element
9290 Ext.core.Element.addMethods(
9292 var VISIBILITY = "visibility",
9293 DISPLAY = "display",
9296 XMASKED = Ext.baseCSSPrefix + "masked",
9297 XMASKEDRELATIVE = Ext.baseCSSPrefix + "masked-relative",
9298 data = Ext.core.Element.data;
9302 * Checks whether the element is currently visible using both visibility and display properties.
9303 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
9304 * @return {Boolean} True if the element is currently visible, else false
9306 isVisible : function(deep) {
9307 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
9308 p = this.dom.parentNode;
9310 if (deep !== true || !vis) {
9314 while (p && !(/^body/i.test(p.tagName))) {
9315 if (!Ext.fly(p, '_isVisible').isVisible()) {
9324 * Returns true if display is not "none"
9327 isDisplayed : function() {
9328 return !this.isStyle(DISPLAY, NONE);
9332 * Convenience method for setVisibilityMode(Element.DISPLAY)
9333 * @param {String} display (optional) What to set display to when visible
9334 * @return {Ext.core.Element} this
9336 enableDisplayMode : function(display) {
9337 this.setVisibilityMode(Ext.core.Element.DISPLAY);
9339 if (!Ext.isEmpty(display)) {
9340 data(this.dom, 'originalDisplay', display);
9347 * Puts a mask over this element to disable user interaction. Requires core.css.
9348 * This method can only be applied to elements which accept child nodes.
9349 * @param {String} msg (optional) A message to display in the mask
9350 * @param {String} msgCls (optional) A css class to apply to the msg element
9351 * @return {Element} The mask element
9353 mask : function(msg, msgCls) {
9356 setExpression = dom.style.setExpression,
9357 dh = Ext.core.DomHelper,
9358 EXTELMASKMSG = Ext.baseCSSPrefix + "mask-msg",
9362 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
9363 me.addCls(XMASKEDRELATIVE);
9365 el = data(dom, 'maskMsg');
9369 el = data(dom, 'mask');
9374 mask = dh.append(dom, {cls : Ext.baseCSSPrefix + "mask"}, true);
9375 data(dom, 'mask', mask);
9378 mask.setDisplayed(true);
9380 if (typeof msg == 'string') {
9381 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
9382 data(dom, 'maskMsg', mm);
9383 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
9384 mm.dom.firstChild.innerHTML = msg;
9385 mm.setDisplayed(true);
9388 // NOTE: CSS expressions are resource intensive and to be used only as a last resort
9389 // These expressions are removed as soon as they are no longer necessary - in the unmask method.
9390 // In normal use cases an element will be masked for a limited period of time.
9391 // Fix for https://sencha.jira.com/browse/EXTJSIV-19.
9392 // IE6 strict mode and IE6-9 quirks mode takes off left+right padding when calculating width!
9393 if (!Ext.supports.IncludePaddingInWidthCalculation && setExpression) {
9394 mask.dom.style.setExpression('width', 'this.parentNode.offsetWidth + "px"');
9397 // Some versions and modes of IE subtract top+bottom padding when calculating height.
9398 // Different versions from those which make the same error for width!
9399 if (!Ext.supports.IncludePaddingInHeightCalculation && setExpression) {
9400 mask.dom.style.setExpression('height', 'this.parentNode.offsetHeight + "px"');
9402 // ie will not expand full height automatically
9403 else if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
9404 mask.setSize(undefined, me.getHeight());
9410 * Removes a previously applied mask.
9412 unmask : function() {
9415 mask = data(dom, 'mask'),
9416 maskMsg = data(dom, 'maskMsg');
9419 // Remove resource-intensive CSS expressions as soon as they are not required.
9420 if (mask.dom.style.clearExpression) {
9421 mask.dom.style.clearExpression('width');
9422 mask.dom.style.clearExpression('height');
9426 data(dom, 'maskMsg', undefined);
9430 data(dom, 'mask', undefined);
9431 me.removeCls([XMASKED, XMASKEDRELATIVE]);
9435 * Returns true if this element is masked. Also re-centers any displayed message within the mask.
9438 isMasked : function() {
9440 mask = data(me.dom, 'mask'),
9441 maskMsg = data(me.dom, 'maskMsg');
9443 if (mask && mask.isVisible()) {
9453 * Creates an iframe shim for this element to keep selects and other windowed objects from
9455 * @return {Ext.core.Element} The new shim element
9457 createShim : function() {
9458 var el = document.createElement('iframe'),
9461 el.frameBorder = '0';
9462 el.className = Ext.baseCSSPrefix + 'shim';
9463 el.src = Ext.SSL_SECURE_URL;
9464 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
9465 shim.autoBoxAdjust = false;
9472 * @class Ext.core.Element
9474 Ext.core.Element.addMethods({
9476 * Convenience method for constructing a KeyMap
9477 * @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:
9478 * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
9479 * @param {Function} fn The function to call
9480 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
9481 * @return {Ext.util.KeyMap} The KeyMap created
9483 addKeyListener : function(key, fn, scope){
9485 if(typeof key != 'object' || Ext.isArray(key)){
9501 return Ext.create('Ext.util.KeyMap', this, config);
9505 * Creates a KeyMap for this element
9506 * @param {Object} config The KeyMap config. See {@link Ext.util.KeyMap} for more details
9507 * @return {Ext.util.KeyMap} The KeyMap created
9509 addKeyMap : function(config){
9510 return Ext.create('Ext.util.KeyMap', this, config);
9514 //Import the newly-added Ext.core.Element functions into CompositeElementLite. We call this here because
9515 //Element.keys.js is the last extra Ext.core.Element include in the ext-all.js build
9516 Ext.CompositeElementLite.importElementMethods();
9519 * @class Ext.CompositeElementLite
9521 Ext.apply(Ext.CompositeElementLite.prototype, {
9522 addElements : function(els, root){
9526 if(typeof els == "string"){
9527 els = Ext.core.Element.selectorFunction(els, root);
9529 var yels = this.elements;
9530 Ext.each(els, function(e) {
9531 yels.push(Ext.get(e));
9537 * Returns the first Element
9538 * @return {Ext.core.Element}
9541 return this.item(0);
9545 * Returns the last Element
9546 * @return {Ext.core.Element}
9549 return this.item(this.getCount()-1);
9553 * Returns true if this composite contains the passed element
9554 * @param el {Mixed} The id of an element, or an Ext.core.Element, or an HtmlElement to find within the composite collection.
9557 contains : function(el){
9558 return this.indexOf(el) != -1;
9562 * Removes the specified element(s).
9563 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9564 * or an array of any of those.
9565 * @param {Boolean} removeDom (optional) True to also remove the element from the document
9566 * @return {CompositeElement} this
9568 removeElement : function(keys, removeDom){
9570 els = this.elements,
9572 Ext.each(keys, function(val){
9573 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
9589 * @class Ext.CompositeElement
9590 * @extends Ext.CompositeElementLite
9591 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
9592 * members, or to perform collective actions upon the whole set.</p>
9593 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.core.Element} and
9594 * {@link Ext.fx.Anim}. The methods from these classes will be performed on all the elements in this collection.</p>
9595 * <p>All methods return <i>this</i> and can be chained.</p>
9598 var els = Ext.select("#some-el div.some-class", true);
9599 // or select directly from an existing element
9600 var el = Ext.get('some-el');
9601 el.select('div.some-class', true);
9603 els.setWidth(100); // all elements become 100 width
9604 els.hide(true); // all elements fade out and hide
9606 els.setWidth(100).hide(true);
9609 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
9611 constructor : function(els, root){
9613 this.add(els, root);
9617 getElement : function(el){
9618 // In this case just return it, since we already have a reference to it
9623 transformElement : function(el){
9628 * Adds elements to this composite.
9629 * @param {String/Array} els A string CSS selector, an array of elements or an element
9630 * @return {CompositeElement} this
9634 * Returns the Element object at the specified index
9635 * @param {Number} index
9636 * @return {Ext.core.Element}
9640 * Iterates each `element` in this `composite` calling the supplied function using {@link Ext#each Ext.each}.
9641 * @param {Function} fn
9643 The function to be called with each
9644 `element`. If the supplied function returns <tt>false</tt>,
9645 iteration stops. This function is called with the following arguments:
9647 - `element` : __Ext.core.Element++
9648 The element at the current `index` in the `composite`
9650 - `composite` : __Object__
9653 - `index` : __Number__
9654 The current index within the `composite`
9656 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed.
9657 * Defaults to the <code>element</code> at the current <code>index</code>
9658 * within the composite.
9659 * @return {CompositeElement} this
9665 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
9666 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9667 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9668 * @param {String/Array} selector The CSS selector or an array of elements
9669 * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
9670 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9671 * @return {CompositeElementLite/CompositeElement}
9672 * @member Ext.core.Element
9675 Ext.core.Element.select = function(selector, unique, root){
9677 if(typeof selector == "string"){
9678 els = Ext.core.Element.selectorFunction(selector, root);
9679 }else if(selector.length !== undefined){
9684 sourceClass: "Ext.core.Element",
9685 sourceMethod: "select",
9689 msg: "Invalid selector specified: " + selector
9693 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
9697 * Selects elements based on the passed CSS selector to enable {@link Ext.core.Element Element} methods
9698 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9699 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9700 * @param {String/Array} selector The CSS selector or an array of elements
9701 * @param {Boolean} unique (optional) true to create a unique Ext.core.Element for each element (defaults to a shared flyweight object)
9702 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9703 * @return {CompositeElementLite/CompositeElement}
9707 Ext.select = Ext.core.Element.select;