3 * Copyright(c) 2006-2010 Ext JS, Inc.
5 * http://www.extjs.com/license
9 * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
10 * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
11 * from your DOM building code.</p>
13 * <p><b><u>DomHelper element specification object</u></b></p>
14 * <p>A specification object is used when creating elements. Attributes of this object
15 * are assumed to be element attributes, except for 4 special attributes:
16 * <div class="mdetail-params"><ul>
17 * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
18 * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
19 * same kind of element definition objects to be created and appended. These can be nested
20 * as deep as you want.</div></li>
21 * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
22 * This will end up being either the "class" attribute on a HTML fragment or className
23 * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
24 * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
27 * <p><b><u>Insertion methods</u></b></p>
28 * <p>Commonly used insertion methods:
29 * <div class="mdetail-params"><ul>
30 * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
31 * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
32 * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
33 * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
34 * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
35 * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
38 * <p><b><u>Example</u></b></p>
39 * <p>This is an example, where an unordered list with 3 children items is appended to an existing
40 * element with id <tt>'my-div'</tt>:<br>
42 var dh = Ext.DomHelper; // create shorthand alias
43 // specification object
48 // append children after creating
49 children: [ // may also specify 'cn' instead of 'children'
50 {tag: 'li', id: 'item0', html: 'List Item 0'},
51 {tag: 'li', id: 'item1', html: 'List Item 1'},
52 {tag: 'li', id: 'item2', html: 'List Item 2'}
56 'my-div', // the context element 'my-div' can either be the id or the actual node
57 spec // the specification object
60 * <p>Element creation specification parameters in this class may also be passed as an Array of
61 * specification objects. This can be used to insert multiple sibling nodes into an existing
62 * container very efficiently. For example, to add more list items to the example above:<pre><code>
64 {tag: 'li', id: 'item3', html: 'List Item 3'},
65 {tag: 'li', id: 'item4', html: 'List Item 4'}
69 * <p><b><u>Templating</u></b></p>
70 * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
71 * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
72 * insert new elements. Revisiting the example above, we could utilize templating this time:
75 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
77 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
79 for(var i = 0; i < 5, i++){
80 tpl.append(list, [i]); // use template to append to the actual node
83 * <p>An example using a template:<pre><code>
84 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
86 var tpl = new Ext.DomHelper.createTemplate(html);
87 tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack's Site"]);
88 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
91 * <p>The same example using named parameters:<pre><code>
92 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
94 var tpl = new Ext.DomHelper.createTemplate(html);
95 tpl.append('blog-roll', {
97 url: 'http://www.jackslocum.com/',
98 text: "Jack's Site"
100 tpl.append('blog-roll', {
102 url: 'http://www.dustindiaz.com/',
103 text: "Dustin's Site"
107 * <p><b><u>Compiling Templates</u></b></p>
108 * <p>Templates are applied using regular expressions. The performance is great, but if
109 * you are adding a bunch of DOM elements using the same template, you can increase
110 * performance even further by {@link Ext.Template#compile "compiling"} the template.
111 * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
112 * broken up at the different variable points and a dynamic function is created and eval'ed.
113 * The generated function performs string concatenation of these parts and the passed
114 * variables instead of using regular expressions.
116 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
118 var tpl = new Ext.DomHelper.createTemplate(html);
121 //... use template like normal
124 * <p><b><u>Performance Boost</u></b></p>
125 * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
126 * of DOM can significantly boost performance.</p>
127 * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
128 * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
129 * results in the creation of a text node. Usage:</p>
131 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
135 Ext.DomHelper = function(){
136 var tempTableEl = null,
137 emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
138 tableRe = /^table|tbody|tr|td$/i,
139 confRe = /tag|children|cn|html$/i,
140 tableElRe = /td|tr|tbody/i,
141 cssRe = /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
144 // kill repeat to save bytes
145 afterbegin = 'afterbegin',
146 afterend = 'afterend',
147 beforebegin = 'beforebegin',
148 beforeend = 'beforeend',
157 function doInsert(el, o, returnElement, pos, sibling, append){
158 var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));
159 return returnElement ? Ext.get(newNode, true) : newNode;
162 // build as innerHTML where available
163 function createHtml(o){
171 if(typeof o == "string"){
173 } else if (Ext.isArray(o)) {
174 for (var i=0; i < o.length; i++) {
176 b += createHtml(o[i]);
180 b += '<' + (o.tag = o.tag || 'div');
183 if(!confRe.test(attr)){
184 if (typeof val == "object") {
185 b += ' ' + attr + '="';
187 b += key + ':' + val[key] + ';';
191 b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
195 // Now either just close the tag or try to add children and close the tag.
196 if (emptyTags.test(o.tag)) {
200 if ((cn = o.children || o.cn)) {
205 b += '</' + o.tag + '>';
211 function ieTable(depth, s, h, e){
212 tempTableEl.innerHTML = [s, h, e].join('');
219 // If the result is multiple siblings, then encapsulate them into one fragment.
220 if(ns = el.nextSibling){
221 var df = document.createDocumentFragment();
234 * Nasty code for IE's broken table implementation
236 function insertIntoTable(tag, where, el, html) {
240 tempTableEl = tempTableEl || document.createElement('div');
242 if(tag == 'td' && (where == afterbegin || where == beforeend) ||
243 !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
246 before = where == beforebegin ? el :
247 where == afterend ? el.nextSibling :
248 where == afterbegin ? el.firstChild : null;
250 if (where == beforebegin || where == afterend) {
254 if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
255 node = ieTable(4, trs, html, tre);
256 } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
257 (tag == 'tr' && (where == beforebegin || where == afterend))) {
258 node = ieTable(3, tbs, html, tbe);
260 node = ieTable(2, ts, html, te);
262 el.insertBefore(node, before);
269 * Returns the markup for the passed Element(s) config.
270 * @param {Object} o The DOM object spec (and children)
273 markup : function(o){
274 return createHtml(o);
278 * Applies a style specification to an element.
279 * @param {String/HTMLElement} el The element to apply styles to
280 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
281 * a function which returns such a specification.
283 applyStyles : function(el, styles){
291 if(typeof styles == "function"){
292 styles = styles.call();
294 if(typeof styles == "string"){
295 while((matches = cssRe.exec(styles))){
296 el.setStyle(matches[1], matches[2]);
298 }else if (typeof styles == "object"){
305 * Inserts an HTML fragment into the DOM.
306 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
307 * @param {HTMLElement} el The context element
308 * @param {String} html The HTML fragment
309 * @return {HTMLElement} The new node
311 insertHtml : function(where, el, html){
320 where = where.toLowerCase();
321 // add these here because they are used in both branches of the condition.
322 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
323 hash[afterend] = ['AfterEnd', 'nextSibling'];
325 if (el.insertAdjacentHTML) {
326 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
329 // add these two to the hash.
330 hash[afterbegin] = ['AfterBegin', 'firstChild'];
331 hash[beforeend] = ['BeforeEnd', 'lastChild'];
332 if ((hashVal = hash[where])) {
333 el.insertAdjacentHTML(hashVal[0], html);
334 return el[hashVal[1]];
337 range = el.ownerDocument.createRange();
338 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
341 frag = range.createContextualFragment(html);
342 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
343 return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
345 rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
347 range[setStart](el[rangeEl]);
348 frag = range.createContextualFragment(html);
349 if(where == afterbegin){
350 el.insertBefore(frag, el.firstChild);
352 el.appendChild(frag);
360 throw 'Illegal insertion point -> "' + where + '"';
364 * Creates new DOM element(s) and inserts them before el.
365 * @param {Mixed} el The context element
366 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
367 * @param {Boolean} returnElement (optional) true to return a Ext.Element
368 * @return {HTMLElement/Ext.Element} The new node
370 insertBefore : function(el, o, returnElement){
371 return doInsert(el, o, returnElement, beforebegin);
375 * Creates new DOM element(s) and inserts them after el.
376 * @param {Mixed} el The context element
377 * @param {Object} o The DOM object spec (and children)
378 * @param {Boolean} returnElement (optional) true to return a Ext.Element
379 * @return {HTMLElement/Ext.Element} The new node
381 insertAfter : function(el, o, returnElement){
382 return doInsert(el, o, returnElement, afterend, 'nextSibling');
386 * Creates new DOM element(s) and inserts them as the first child of el.
387 * @param {Mixed} el The context element
388 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
389 * @param {Boolean} returnElement (optional) true to return a Ext.Element
390 * @return {HTMLElement/Ext.Element} The new node
392 insertFirst : function(el, o, returnElement){
393 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
397 * Creates new DOM element(s) and appends them to el.
398 * @param {Mixed} el The context element
399 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
400 * @param {Boolean} returnElement (optional) true to return a Ext.Element
401 * @return {HTMLElement/Ext.Element} The new node
403 append : function(el, o, returnElement){
404 return doInsert(el, o, returnElement, beforeend, '', true);
408 * Creates new DOM element(s) and overwrites the contents of el with them.
409 * @param {Mixed} el The context element
410 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
411 * @param {Boolean} returnElement (optional) true to return a Ext.Element
412 * @return {HTMLElement/Ext.Element} The new node
414 overwrite : function(el, o, returnElement){
416 el.innerHTML = createHtml(o);
417 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
420 createHtml : createHtml
425 * @class Ext.DomHelper
427 Ext.apply(Ext.DomHelper,
430 afterbegin = 'afterbegin',
431 afterend = 'afterend',
432 beforebegin = 'beforebegin',
433 beforeend = 'beforeend',
434 confRe = /tag|children|cn|html$/i;
437 function doInsert(el, o, returnElement, pos, sibling, append){
441 newNode = createDom(o, null);
443 el.appendChild(newNode);
445 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
448 newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
450 return returnElement ? Ext.get(newNode, true) : newNode;
455 function createDom(o, parentNode){
463 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
464 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
465 for (var i = 0, l = o.length; i < l; i++) {
468 } else if (typeof o == 'string') { // Allow a string as a child spec.
469 el = doc.createTextNode(o);
471 el = doc.createElement( o.tag || 'div' );
472 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
473 for (var attr in o) {
474 if(!confRe.test(attr)){
480 el.setAttribute(attr, val);
487 Ext.DomHelper.applyStyles(el, o.style);
489 if ((cn = o.children || o.cn)) {
492 el.innerHTML = o.html;
496 parentNode.appendChild(el);
503 * Creates a new Ext.Template from the DOM object spec.
504 * @param {Object} o The DOM object spec (and children)
505 * @return {Ext.Template} The new template
507 createTemplate : function(o){
508 var html = Ext.DomHelper.createHtml(o);
509 return new Ext.Template(html);
512 /** True to force the use of DOM instead of html fragments @type Boolean */
516 * Creates new DOM element(s) and inserts them before el.
517 * @param {Mixed} el The context element
518 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
519 * @param {Boolean} returnElement (optional) true to return a Ext.Element
520 * @return {HTMLElement/Ext.Element} The new node
523 insertBefore : function(el, o, returnElement){
524 return doInsert(el, o, returnElement, beforebegin);
528 * Creates new DOM element(s) and inserts them after el.
529 * @param {Mixed} el The context element
530 * @param {Object} o The DOM object spec (and children)
531 * @param {Boolean} returnElement (optional) true to return a Ext.Element
532 * @return {HTMLElement/Ext.Element} The new node
535 insertAfter : function(el, o, returnElement){
536 return doInsert(el, o, returnElement, afterend, 'nextSibling');
540 * Creates new DOM element(s) and inserts them as the first child of el.
541 * @param {Mixed} el The context element
542 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
543 * @param {Boolean} returnElement (optional) true to return a Ext.Element
544 * @return {HTMLElement/Ext.Element} The new node
547 insertFirst : function(el, o, returnElement){
548 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
552 * Creates new DOM element(s) and appends them to el.
553 * @param {Mixed} el The context element
554 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
555 * @param {Boolean} returnElement (optional) true to return a Ext.Element
556 * @return {HTMLElement/Ext.Element} The new node
559 append: function(el, o, returnElement){
560 return doInsert(el, o, returnElement, beforeend, '', true);
564 * Creates new DOM element(s) without inserting them to the document.
565 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
566 * @return {HTMLElement} The new uninserted node
573 * @class Ext.Template
574 * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
575 * for greater performance.</p>
576 * <p>For example usage {@link #Template see the constructor}.</p>
579 * An instance of this class may be created by passing to the constructor either
580 * a single argument, or multiple arguments:
581 * <div class="mdetail-params"><ul>
582 * <li><b>single argument</b> : String/Array
583 * <div class="sub-desc">
584 * The single argument may be either a String or an Array:<ul>
585 * <li><tt>String</tt> : </li><pre><code>
586 var t = new Ext.Template("<div>Hello {0}.</div>");
587 t.{@link #append}('some-element', ['foo']);
589 * <li><tt>Array</tt> : </li>
590 * An Array will be combined with <code>join('')</code>.
592 var t = new Ext.Template([
593 '<div name="{id}">',
594 '<span class="{cls}">{name:trim} {value:ellipsis(10)}</span>',
597 t.{@link #compile}();
598 t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
601 * <li><b>multiple arguments</b> : String, Object, Array, ...
602 * <div class="sub-desc">
603 * Multiple arguments will be combined with <code>join('')</code>.
605 var t = new Ext.Template(
606 '<div name="{id}">',
607 '<span class="{cls}">{name} {value}</span>',
609 // a configuration object:
611 compiled: true, // {@link #compile} immediately
612 disableFormats: true // See Notes below.
616 * <p><b>Notes</b>:</p>
617 * <div class="mdetail-params"><ul>
618 * <li>Formatting and <code>disableFormats</code> are not applicable for Ext Core.</li>
619 * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
620 * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
621 * when no formatting is required.</li>
625 * @param {Mixed} config
627 Ext.Template = function(html){
633 if (Ext.isArray(html)) {
634 html = html.join("");
635 } else if (a.length > 1) {
636 for(var i = 0, len = a.length; i < len; i++){
638 if(typeof v == 'object'){
650 * @cfg {Boolean} compiled Specify <tt>true</tt> to compile the template
651 * immediately (see <code>{@link #compile}</code>).
652 * Defaults to <tt>false</tt>.
658 Ext.Template.prototype = {
660 * @cfg {RegExp} re The regular expression used to match template variables.
661 * Defaults to:<pre><code>
662 * re : /\{([\w-]+)\}/g // for Ext Core
663 * re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g // for Ext JS
666 re : /\{([\w-]+)\}/g,
668 * See <code>{@link #re}</code>.
674 * Returns an HTML fragment of this template with the specified <code>values</code> applied.
675 * @param {Object/Array} values
676 * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
677 * or an object (i.e. <code>{foo: 'bar'}</code>).
678 * @return {String} The HTML fragment
680 applyTemplate : function(values){
684 me.compiled(values) :
685 me.html.replace(me.re, function(m, name){
686 return values[name] !== undefined ? values[name] : "";
691 * Sets the HTML used as the template and optionally compiles it.
692 * @param {String} html
693 * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
694 * @return {Ext.Template} this
696 set : function(html, compile){
700 return compile ? me.compile() : me;
704 * Compiles the template into an internal function, eliminating the RegEx overhead.
705 * @return {Ext.Template} this
707 compile : function(){
709 sep = Ext.isGecko ? "+" : ",";
711 function fn(m, name){
712 name = "values['" + name + "']";
713 return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
716 eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
717 me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
718 (Ext.isGecko ? "';};" : "'].join('');};"));
723 * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
724 * @param {Mixed} el The context element
725 * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
726 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
727 * @return {HTMLElement/Ext.Element} The new node or Element
729 insertFirst: function(el, values, returnElement){
730 return this.doInsert('afterBegin', el, values, returnElement);
734 * Applies the supplied values to the template and inserts the new node(s) before el.
735 * @param {Mixed} el The context element
736 * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
737 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
738 * @return {HTMLElement/Ext.Element} The new node or Element
740 insertBefore: function(el, values, returnElement){
741 return this.doInsert('beforeBegin', el, values, returnElement);
745 * Applies the supplied values to the template and inserts the new node(s) after el.
746 * @param {Mixed} el The context element
747 * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
748 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
749 * @return {HTMLElement/Ext.Element} The new node or Element
751 insertAfter : function(el, values, returnElement){
752 return this.doInsert('afterEnd', el, values, returnElement);
756 * Applies the supplied <code>values</code> to the template and appends
757 * the new node(s) to the specified <code>el</code>.
758 * <p>For example usage {@link #Template see the constructor}.</p>
759 * @param {Mixed} el The context element
760 * @param {Object/Array} values
761 * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
762 * or an object (i.e. <code>{foo: 'bar'}</code>).
763 * @param {Boolean} returnElement (optional) true to return an Ext.Element (defaults to undefined)
764 * @return {HTMLElement/Ext.Element} The new node or Element
766 append : function(el, values, returnElement){
767 return this.doInsert('beforeEnd', el, values, returnElement);
770 doInsert : function(where, el, values, returnEl){
772 var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
773 return returnEl ? Ext.get(newNode, true) : newNode;
777 * Applies the supplied values to the template and overwrites the content of el with the new node(s).
778 * @param {Mixed} el The context element
779 * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
780 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
781 * @return {HTMLElement/Ext.Element} The new node or Element
783 overwrite : function(el, values, returnElement){
785 el.innerHTML = this.applyTemplate(values);
786 return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
790 * Alias for {@link #applyTemplate}
791 * Returns an HTML fragment of this template with the specified <code>values</code> applied.
792 * @param {Object/Array} values
793 * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
794 * or an object (i.e. <code>{foo: 'bar'}</code>).
795 * @return {String} The HTML fragment
796 * @member Ext.Template
799 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
802 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
803 * @param {String/HTMLElement} el A DOM element or its id
804 * @param {Object} config A configuration object
805 * @return {Ext.Template} The created template
808 Ext.Template.from = function(el, config){
810 return new Ext.Template(el.value || el.innerHTML, config || '');
813 * @class Ext.Template
815 Ext.apply(Ext.Template.prototype, {
817 * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format
818 * functions in the template. If the template does not contain
819 * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>
820 * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.
822 var t = new Ext.Template(
823 '<div name="{id}">',
824 '<span class="{cls}">{name} {value}</span>',
827 compiled: true, // {@link #compile} immediately
828 disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting
832 * For a list of available format functions, see {@link Ext.util.Format}.
834 disableFormats : false,
836 * See <code>{@link #disableFormats}</code>.
838 * @property disableFormats
842 * The regular expression used to match template variables
847 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
848 argsRe : /^\s*['"](.*)["']\s*$/,
850 compileBRe : /(\r\n|\n)/g,
854 * Returns an HTML fragment of this template with the specified values applied.
855 * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
856 * @return {String} The HTML fragment
859 applyTemplate : function(values){
861 useF = me.disableFormats !== true,
862 fm = Ext.util.Format,
866 return me.compiled(values);
868 function fn(m, name, format, args){
869 if (format && useF) {
870 if (format.substr(0, 5) == "this.") {
871 return tpl.call(format.substr(5), values[name], values);
874 // quoted values are required for strings in compiled templates,
875 // but for non compiled we need to strip them
876 // quoted reversed for jsmin
878 args = args.split(',');
879 for(var i = 0, len = args.length; i < len; i++){
880 args[i] = args[i].replace(re, "$1");
882 args = [values[name]].concat(args);
884 args = [values[name]];
886 return fm[format].apply(fm, args);
889 return values[name] !== undefined ? values[name] : "";
892 return me.html.replace(me.re, fn);
896 * Compiles the template into an internal function, eliminating the RegEx overhead.
897 * @return {Ext.Template} this
900 compile : function(){
902 fm = Ext.util.Format,
903 useF = me.disableFormats !== true,
904 sep = Ext.isGecko ? "+" : ",",
907 function fn(m, name, format, args){
909 args = args ? ',' + args : "";
910 if(format.substr(0, 5) != "this."){
911 format = "fm." + format + '(';
913 format = 'this.call("'+ format.substr(5) + '", ';
917 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
919 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
922 // branched to use + in gecko and [].join() in others
924 body = "this.compiled = function(values){ return '" +
925 me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn) +
928 body = ["this.compiled = function(values){ return ['"];
929 body.push(me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn));
930 body.push("'].join('');};");
931 body = body.join('');
937 // private function used to call members
938 call : function(fnName, value, allValues){
939 return this[fnName](value, allValues);
942 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
944 * This is code is also distributed under MIT license for use
945 * with jQuery and prototype JavaScript libraries.
948 * @class Ext.DomQuery
949 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).
951 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>
954 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.
956 <h4>Element Selectors:</h4>
958 <li> <b>*</b> any element</li>
959 <li> <b>E</b> an element with the tag E</li>
960 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
961 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
962 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
963 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
965 <h4>Attribute Selectors:</h4>
966 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
968 <li> <b>E[foo]</b> has an attribute "foo"</li>
969 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
970 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
971 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
972 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
973 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
974 <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
976 <h4>Pseudo Classes:</h4>
978 <li> <b>E:first-child</b> E is the first child of its parent</li>
979 <li> <b>E:last-child</b> E is the last child of its parent</li>
980 <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>
981 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
982 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
983 <li> <b>E:only-child</b> E is the only child of its parent</li>
984 <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>
985 <li> <b>E:first</b> the first E in the resultset</li>
986 <li> <b>E:last</b> the last E in the resultset</li>
987 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
988 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
989 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
990 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
991 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
992 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
993 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
994 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
995 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
996 <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
998 <h4>CSS Value Selectors:</h4>
1000 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
1001 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
1002 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
1003 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
1004 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
1005 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
1009 Ext.DomQuery = function(){
1014 trimRe = /^\s+|\s+$/g,
1015 tplRe = /\{(\d+)\}/g,
1016 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
1017 tagTokenRe = /^(#)?([\w-\*]+)/,
1018 nthRe = /(\d*)n\+?(\d*)/,
1020 // This is for IE MSXML which does not support expandos.
1021 // IE runs the same speed using setAttribute, however FF slows way down
1022 // and Safari completely fails so they need to continue to use expandos.
1023 isIE = window.ActiveXObject ? true : false,
1026 // this eval is stop the compressor from
1027 // renaming the variable to something shorter
1028 eval("var batch = 30803;");
1030 // Retrieve the child node from a particular
1031 // parent at the specified index.
1032 function child(parent, index){
1034 n = parent.firstChild;
1036 if(n.nodeType == 1){
1046 // retrieve the next element node
1048 while((n = n.nextSibling) && n.nodeType != 1);
1052 // retrieve the previous element node
1054 while((n = n.previousSibling) && n.nodeType != 1);
1058 // Mark each child node with a nodeIndex skipping and
1059 // removing empty text nodes.
1060 function children(parent){
1061 var n = parent.firstChild,
1065 nextNode = n.nextSibling;
1066 // clean worthless empty nodes.
1067 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
1068 parent.removeChild(n);
1070 // add an expando nodeIndex
1071 n.nodeIndex = ++nodeIndex;
1079 // nodeSet - array of nodes
1081 function byClassName(nodeSet, cls){
1085 var result = [], ri = -1;
1086 for(var i = 0, ci; ci = nodeSet[i]; i++){
1087 if((' '+ci.className+' ').indexOf(cls) != -1){
1094 function attrValue(n, attr){
1095 // if its an array, use the first node.
1096 if(!n.tagName && typeof n.length != "undefined"){
1106 if(attr == "class" || attr == "className"){
1109 return n.getAttribute(attr) || n[attr];
1115 // mode - false, /, >, +, ~
1116 // tagName - defaults to "*"
1117 function getNodes(ns, mode, tagName){
1118 var result = [], ri = -1, cs;
1122 tagName = tagName || "*";
1124 if(typeof ns.getElementsByTagName != "undefined"){
1128 // no mode specified, grab all elements by tagName
1131 for(var i = 0, ni; ni = ns[i]; i++){
1132 cs = ni.getElementsByTagName(tagName);
1133 for(var j = 0, ci; ci = cs[j]; j++){
1137 // Direct Child mode (/ or >)
1138 // E > F or E/F all direct children elements of E that have the tag
1139 } else if(mode == "/" || mode == ">"){
1140 var utag = tagName.toUpperCase();
1141 for(var i = 0, ni, cn; ni = ns[i]; i++){
1143 for(var j = 0, cj; cj = cn[j]; j++){
1144 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
1149 // Immediately Preceding mode (+)
1150 // E + F all elements with the tag F that are immediately preceded by an element with the tag E
1151 }else if(mode == "+"){
1152 var utag = tagName.toUpperCase();
1153 for(var i = 0, n; n = ns[i]; i++){
1154 while((n = n.nextSibling) && n.nodeType != 1);
1155 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
1160 // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
1161 }else if(mode == "~"){
1162 var utag = tagName.toUpperCase();
1163 for(var i = 0, n; n = ns[i]; i++){
1164 while((n = n.nextSibling)){
1165 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
1174 function concat(a, b){
1178 for(var i = 0, l = b.length; i < l; i++){
1184 function byTag(cs, tagName){
1185 if(cs.tagName || cs == document){
1191 var result = [], ri = -1;
1192 tagName = tagName.toLowerCase();
1193 for(var i = 0, ci; ci = cs[i]; i++){
1194 if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
1201 function byId(cs, id){
1202 if(cs.tagName || cs == document){
1208 var result = [], ri = -1;
1209 for(var i = 0, ci; ci = cs[i]; i++){
1210 if(ci && ci.id == id){
1218 // operators are =, !=, ^=, $=, *=, %=, |= and ~=
1219 // custom can be "{"
1220 function byAttribute(cs, attr, value, op, custom){
1223 useGetStyle = custom == "{",
1224 fn = Ext.DomQuery.operators[op],
1227 for(var i = 0, ci; ci = cs[i]; i++){
1228 // skip non-element nodes.
1229 if(ci.nodeType != 1){
1233 innerHTML = ci.innerHTML;
1234 // we only need to change the property names if we're dealing with html nodes, not XML
1235 if(innerHTML !== null && innerHTML !== undefined){
1237 a = Ext.DomQuery.getStyle(ci, attr);
1238 } else if (attr == "class" || attr == "className"){
1240 } else if (attr == "for"){
1242 } else if (attr == "href"){
1243 // getAttribute href bug
1244 // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
1245 a = ci.getAttribute("href", 2);
1247 a = ci.getAttribute(attr);
1250 a = ci.getAttribute(attr);
1252 if((fn && fn(a, value)) || (!fn && a)){
1259 function byPseudo(cs, name, value){
1260 return Ext.DomQuery.pseudos[name](cs, value);
1263 function nodupIEXml(cs){
1266 cs[0].setAttribute("_nodup", d);
1268 for(var i = 1, len = cs.length; i < len; i++){
1270 if(!c.getAttribute("_nodup") != d){
1271 c.setAttribute("_nodup", d);
1275 for(var i = 0, len = cs.length; i < len; i++){
1276 cs[i].removeAttribute("_nodup");
1285 var len = cs.length, c, i, r = cs, cj, ri = -1;
1286 if(!len || typeof cs.nodeType != "undefined" || len == 1){
1289 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
1290 return nodupIEXml(cs);
1294 for(i = 1; c = cs[i]; i++){
1299 for(var j = 0; j < i; j++){
1302 for(j = i+1; cj = cs[j]; j++){
1314 function quickDiffIEXml(c1, c2){
1317 for(var i = 0, len = c1.length; i < len; i++){
1318 c1[i].setAttribute("_qdiff", d);
1320 for(var i = 0, len = c2.length; i < len; i++){
1321 if(c2[i].getAttribute("_qdiff") != d){
1322 r[r.length] = c2[i];
1325 for(var i = 0, len = c1.length; i < len; i++){
1326 c1[i].removeAttribute("_qdiff");
1331 function quickDiff(c1, c2){
1332 var len1 = c1.length,
1338 if(isIE && typeof c1[0].selectSingleNode != "undefined"){
1339 return quickDiffIEXml(c1, c2);
1341 for(var i = 0; i < len1; i++){
1344 for(var i = 0, len = c2.length; i < len; i++){
1345 if(c2[i]._qdiff != d){
1346 r[r.length] = c2[i];
1352 function quickId(ns, mode, root, id){
1354 var d = root.ownerDocument || root;
1355 return d.getElementById(id);
1357 ns = getNodes(ns, mode, "*");
1358 return byId(ns, id);
1362 getStyle : function(el, name){
1363 return Ext.fly(el).getStyle(name);
1366 * Compiles a selector/xpath query into a reusable function. The returned function
1367 * takes one parameter "root" (optional), which is the context node from where the query should start.
1368 * @param {String} selector The selector/xpath query
1369 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
1370 * @return {Function}
1372 compile : function(path, type){
1373 type = type || "select";
1375 // setup fn preamble
1376 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
1379 matchers = Ext.DomQuery.matchers,
1380 matchersLn = matchers.length,
1382 // accept leading mode switch
1383 lmode = path.match(modeRe);
1385 if(lmode && lmode[1]){
1386 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
1387 path = path.replace(lmode[1], "");
1390 // strip leading slashes
1391 while(path.substr(0, 1)=="/"){
1392 path = path.substr(1);
1395 while(path && lastPath != path){
1397 var tokenMatch = path.match(tagTokenRe);
1398 if(type == "select"){
1401 if(tokenMatch[1] == "#"){
1402 fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
1404 fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1406 path = path.replace(tokenMatch[0], "");
1407 }else if(path.substr(0, 1) != '@'){
1408 fn[fn.length] = 'n = getNodes(n, mode, "*");';
1413 if(tokenMatch[1] == "#"){
1414 fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1416 fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1418 path = path.replace(tokenMatch[0], "");
1421 while(!(modeMatch = path.match(modeRe))){
1422 var matched = false;
1423 for(var j = 0; j < matchersLn; j++){
1424 var t = matchers[j];
1425 var m = path.match(t.re);
1427 fn[fn.length] = t.select.replace(tplRe, function(x, i){
1430 path = path.replace(m[0], "");
1435 // prevent infinite loop on bad selector
1437 throw 'Error parsing selector, parsing failed at "' + path + '"';
1441 fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1442 path = path.replace(modeMatch[1], "");
1446 fn[fn.length] = "return nodup(n);\n}";
1448 // eval fn and return it
1454 * Selects a group of elements.
1455 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1456 * @param {Node/String} root (optional) The start of the query (defaults to document).
1457 * @return {Array} An Array of DOM elements which match the selector. If there are
1458 * no matches, and empty Array is returned.
1460 jsSelect: function(path, root, type){
1461 // set root to doc if not specified.
1462 root = root || document;
1464 if(typeof root == "string"){
1465 root = document.getElementById(root);
1467 var paths = path.split(","),
1470 // loop over each selector
1471 for(var i = 0, len = paths.length; i < len; i++){
1472 var subPath = paths[i].replace(trimRe, "");
1473 // compile and place in cache
1474 if(!cache[subPath]){
1475 cache[subPath] = Ext.DomQuery.compile(subPath);
1476 if(!cache[subPath]){
1477 throw subPath + " is not a valid selector";
1480 var result = cache[subPath](root);
1481 if(result && result != document){
1482 results = results.concat(result);
1486 // if there were multiple selectors, make sure dups
1488 if(paths.length > 1){
1489 return nodup(results);
1493 isXml: function(el) {
1494 var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1495 return docEl ? docEl.nodeName !== "HTML" : false;
1497 select : document.querySelectorAll ? function(path, root, type) {
1498 root = root || document;
1499 if (!Ext.DomQuery.isXml(root)) {
1501 var cs = root.querySelectorAll(path);
1502 return Ext.toArray(cs);
1506 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1507 } : function(path, root, type) {
1508 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1512 * Selects a single element.
1513 * @param {String} selector The selector/xpath query
1514 * @param {Node} root (optional) The start of the query (defaults to document).
1515 * @return {Element} The DOM element which matched the selector.
1517 selectNode : function(path, root){
1518 return Ext.DomQuery.select(path, root)[0];
1522 * Selects the value of a node, optionally replacing null with the defaultValue.
1523 * @param {String} selector The selector/xpath query
1524 * @param {Node} root (optional) The start of the query (defaults to document).
1525 * @param {String} defaultValue
1528 selectValue : function(path, root, defaultValue){
1529 path = path.replace(trimRe, "");
1530 if(!valueCache[path]){
1531 valueCache[path] = Ext.DomQuery.compile(path, "select");
1533 var n = valueCache[path](root), v;
1534 n = n[0] ? n[0] : n;
1536 // overcome a limitation of maximum textnode size
1537 // Rumored to potentially crash IE6 but has not been confirmed.
1538 // http://reference.sitepoint.com/javascript/Node/normalize
1539 // https://developer.mozilla.org/En/DOM/Node.normalize
1540 if (typeof n.normalize == 'function') n.normalize();
1542 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1543 return ((v === null||v === undefined||v==='') ? defaultValue : v);
1547 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1548 * @param {String} selector The selector/xpath query
1549 * @param {Node} root (optional) The start of the query (defaults to document).
1550 * @param {Number} defaultValue
1553 selectNumber : function(path, root, defaultValue){
1554 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1555 return parseFloat(v);
1559 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1560 * @param {String/HTMLElement/Array} el An element id, element or array of elements
1561 * @param {String} selector The simple selector to test
1564 is : function(el, ss){
1565 if(typeof el == "string"){
1566 el = document.getElementById(el);
1568 var isArray = Ext.isArray(el),
1569 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1570 return isArray ? (result.length == el.length) : (result.length > 0);
1574 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1575 * @param {Array} el An array of elements to filter
1576 * @param {String} selector The simple selector to test
1577 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1578 * the selector instead of the ones that match
1579 * @return {Array} An Array of DOM elements which match the selector. If there are
1580 * no matches, and empty Array is returned.
1582 filter : function(els, ss, nonMatches){
1583 ss = ss.replace(trimRe, "");
1584 if(!simpleCache[ss]){
1585 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1587 var result = simpleCache[ss](els);
1588 return nonMatches ? quickDiff(result, els) : result;
1592 * Collection of matching regular expressions and code snippets.
1593 * Each capture group within () will be replace the {} in the select
1594 * statement as specified by their index.
1598 select: 'n = byClassName(n, " {1} ");'
1600 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1601 select: 'n = byPseudo(n, "{1}", "{2}");'
1603 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1604 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1607 select: 'n = byId(n, "{1}");'
1610 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1615 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1616 * 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, > <.
1619 "=" : function(a, v){
1622 "!=" : function(a, v){
1625 "^=" : function(a, v){
1626 return a && a.substr(0, v.length) == v;
1628 "$=" : function(a, v){
1629 return a && a.substr(a.length-v.length) == v;
1631 "*=" : function(a, v){
1632 return a && a.indexOf(v) !== -1;
1634 "%=" : function(a, v){
1635 return (a % v) == 0;
1637 "|=" : function(a, v){
1638 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1640 "~=" : function(a, v){
1641 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1646 * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed
1647 * two parameters:</p><div class="mdetail-params"><ul>
1648 * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>
1649 * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>
1651 * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>
1652 * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,
1653 * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>
1654 * <p>For example, to filter <code><a></code> elements to only return links to <i>external</i> resources:</p>
1656 Ext.DomQuery.pseudos.external = function(c, v){
1657 var r = [], ri = -1;
1658 for(var i = 0, ci; ci = c[i]; i++){
1659 // Include in result set only if it's a link to an external resource
1660 if(ci.hostname != location.hostname){
1666 * Then external links could be gathered with the following statement:<code><pre>
1667 var externalLinks = Ext.select("a:external");
1671 "first-child" : function(c){
1672 var r = [], ri = -1, n;
1673 for(var i = 0, ci; ci = n = c[i]; i++){
1674 while((n = n.previousSibling) && n.nodeType != 1);
1682 "last-child" : function(c){
1683 var r = [], ri = -1, n;
1684 for(var i = 0, ci; ci = n = c[i]; i++){
1685 while((n = n.nextSibling) && n.nodeType != 1);
1693 "nth-child" : function(c, a) {
1694 var r = [], ri = -1,
1695 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1696 f = (m[1] || 1) - 0, l = m[2] - 0;
1697 for(var i = 0, n; n = c[i]; i++){
1698 var pn = n.parentNode;
1699 if (batch != pn._batch) {
1701 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1702 if(cn.nodeType == 1){
1709 if (l == 0 || n.nodeIndex == l){
1712 } else if ((n.nodeIndex + l) % f == 0){
1720 "only-child" : function(c){
1721 var r = [], ri = -1;;
1722 for(var i = 0, ci; ci = c[i]; i++){
1723 if(!prev(ci) && !next(ci)){
1730 "empty" : function(c){
1731 var r = [], ri = -1;
1732 for(var i = 0, ci; ci = c[i]; i++){
1733 var cns = ci.childNodes, j = 0, cn, empty = true;
1736 if(cn.nodeType == 1 || cn.nodeType == 3){
1748 "contains" : function(c, v){
1749 var r = [], ri = -1;
1750 for(var i = 0, ci; ci = c[i]; i++){
1751 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
1758 "nodeValue" : function(c, v){
1759 var r = [], ri = -1;
1760 for(var i = 0, ci; ci = c[i]; i++){
1761 if(ci.firstChild && ci.firstChild.nodeValue == v){
1768 "checked" : function(c){
1769 var r = [], ri = -1;
1770 for(var i = 0, ci; ci = c[i]; i++){
1771 if(ci.checked == true){
1778 "not" : function(c, ss){
1779 return Ext.DomQuery.filter(c, ss, true);
1782 "any" : function(c, selectors){
1783 var ss = selectors.split('|'),
1785 for(var i = 0, ci; ci = c[i]; i++){
1786 for(var j = 0; s = ss[j]; j++){
1787 if(Ext.DomQuery.is(ci, s)){
1796 "odd" : function(c){
1797 return this["nth-child"](c, "odd");
1800 "even" : function(c){
1801 return this["nth-child"](c, "even");
1804 "nth" : function(c, a){
1805 return c[a-1] || [];
1808 "first" : function(c){
1812 "last" : function(c){
1813 return c[c.length-1] || [];
1816 "has" : function(c, ss){
1817 var s = Ext.DomQuery.select,
1819 for(var i = 0, ci; ci = c[i]; i++){
1820 if(s(ss, ci).length > 0){
1827 "next" : function(c, ss){
1828 var is = Ext.DomQuery.is,
1830 for(var i = 0, ci; ci = c[i]; i++){
1839 "prev" : function(c, ss){
1840 var is = Ext.DomQuery.is,
1842 for(var i = 0, ci; ci = c[i]; i++){
1855 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
1856 * @param {String} path The selector/xpath query
1857 * @param {Node} root (optional) The start of the query (defaults to document).
1862 Ext.query = Ext.DomQuery.select;
1864 * @class Ext.util.DelayedTask
1865 * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
1866 * performing setTimeout where a new timeout cancels the old timeout. When called, the
1867 * task will wait the specified time period before executing. If durng that time period,
1868 * the task is called again, the original call will be cancelled. This continues so that
1869 * the function is only called a single time for each iteration.</p>
1870 * <p>This method is especially useful for things like detecting whether a user has finished
1871 * typing in a text field. An example would be performing validation on a keypress. You can
1872 * use this class to buffer the keypress events for a certain number of milliseconds, and
1873 * perform only if they stop for that amount of time. Usage:</p><pre><code>
1874 var task = new Ext.util.DelayedTask(function(){
1875 alert(Ext.getDom('myInputField').value.length);
1877 // Wait 500ms before calling our function. If the user presses another key
1878 // during that 500ms, it will be cancelled and we'll wait another 500ms.
1879 Ext.get('myInputField').on('keypress', function(){
1880 task.{@link #delay}(500);
1883 * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
1884 * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
1885 * also setup a delayed task for you to buffer events.</p>
1886 * @constructor The parameters to this constructor serve as defaults and are not required.
1887 * @param {Function} fn (optional) The default function to call.
1888 * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
1889 * function is called. If not specified, <code>this</code> will refer to the browser window.
1890 * @param {Array} args (optional) The default Array of arguments.
1892 Ext.util.DelayedTask = function(fn, scope, args){
1898 fn.apply(scope, args || []);
1902 * Cancels any pending timeout and queues a new one
1903 * @param {Number} delay The milliseconds to delay
1904 * @param {Function} newFn (optional) Overrides function passed to constructor
1905 * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
1906 * is specified, <code>this</code> will refer to the browser window.
1907 * @param {Array} newArgs (optional) Overrides args passed to constructor
1909 me.delay = function(delay, newFn, newScope, newArgs){
1912 scope = newScope || scope;
1913 args = newArgs || args;
1914 id = setInterval(call, delay);
1918 * Cancel the last queued timeout
1920 me.cancel = function(){
1928 var EXTUTIL = Ext.util,
1933 * @class Ext.util.Observable
1934 * Base class that provides a common interface for publishing events. Subclasses are expected to
1935 * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1936 * with configured listeners defined.<br>
1939 Employee = Ext.extend(Ext.util.Observable, {
1940 constructor: function(config){
1941 this.name = config.name;
1947 // Copy configured listeners into *this* object so that the base class's
1948 // constructor will add them.
1949 this.listeners = config.listeners;
1951 // Call our superclass constructor to complete construction process.
1952 Employee.superclass.constructor.call(this, config)
1956 * This could then be used like this:<pre><code>
1957 var newEmployee = new Employee({
1961 // By default, "this" will be the object that fired the event.
1962 alert(this.name + " has quit!");
1968 EXTUTIL.Observable = function(){
1970 * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1971 * object during initialization. This should be a valid listeners config object as specified in the
1972 * {@link #addListener} example for attaching multiple handlers at once.</p>
1973 * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1974 * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1975 * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1976 * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1977 * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1978 * has been rendered. A plugin can simplify this step:<pre><code>
1979 // Plugin is configured with a listeners config object.
1980 // The Component is appended to the argument list of all handler functions.
1981 Ext.DomObserver = Ext.extend(Object, {
1982 constructor: function(config) {
1983 this.listeners = config.listeners ? config.listeners : config;
1986 // Component passes itself into plugin's init method
1988 var p, l = this.listeners;
1990 if (Ext.isFunction(l[p])) {
1991 l[p] = this.createHandler(l[p], c);
1993 l[p].fn = this.createHandler(l[p].fn, c);
1997 // Add the listeners to the Element immediately following the render call
1998 c.render = c.render.{@link Function#createSequence createSequence}(function() {
2006 createHandler: function(fn, c) {
2007 return function(e) {
2008 fn.call(this, e, c);
2013 var combo = new Ext.form.ComboBox({
2015 // Collapse combo when its element is clicked on
2016 plugins: [ new Ext.DomObserver({
2017 click: function(evt, comp) {
2024 triggerAction: 'all'
2028 var me = this, e = me.events;
2030 me.on(me.listeners);
2031 delete me.listeners;
2033 me.events = e || {};
2036 EXTUTIL.Observable.prototype = {
2038 filterOptRe : /^(?:scope|delay|buffer|single)$/,
2041 * <p>Fires the specified event with the passed parameters (minus the event name).</p>
2042 * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
2043 * by calling {@link #enableBubble}.</p>
2044 * @param {String} eventName The name of the event to fire.
2045 * @param {Object...} args Variable number of parameters are passed to handlers.
2046 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
2048 fireEvent : function(){
2049 var a = Array.prototype.slice.call(arguments, 0),
2050 ename = a[0].toLowerCase(),
2053 ce = me.events[ename],
2057 if (me.eventsSuspended === TRUE) {
2058 if (q = me.eventQueue) {
2062 else if(typeof ce == 'object') {
2064 if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
2067 c = me.getBubbleTarget && me.getBubbleTarget();
2068 if(c && c.enableBubble) {
2069 cc = c.events[ename];
2070 if(!cc || typeof cc != 'object' || !cc.bubble) {
2071 c.enableBubble(ename);
2073 return c.fireEvent.apply(c, a);
2078 ret = ce.fire.apply(ce, a);
2085 * Appends an event handler to this object.
2086 * @param {String} eventName The name of the event to listen for.
2087 * @param {Function} handler The method the event invokes.
2088 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2089 * <b>If omitted, defaults to the object which fired the event.</b>
2090 * @param {Object} options (optional) An object containing handler configuration.
2091 * properties. This may contain any of the following properties:<ul>
2092 * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2093 * <b>If omitted, defaults to the object which fired the event.</b></div></li>
2094 * <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>
2095 * <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>
2096 * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2097 * by the specified number of milliseconds. If the event fires again within that time, the original
2098 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2099 * <li><b>target</b> : Observable<div class="sub-desc">Only call the handler if the event was fired on the target Observable, <i>not</i>
2100 * if the event was bubbled up from a child Observable.</div></li>
2103 * <b>Combining Options</b><br>
2104 * Using the options argument, it is possible to combine different types of listeners:<br>
2106 * A delayed, one-time listener.
2108 myDataView.on('click', this.onClick, this, {
2113 * <b>Attaching multiple handlers in 1 call</b><br>
2114 * The method also allows for a single argument to be passed which is a config object containing properties
2115 * which specify multiple handlers.
2125 fn: this.onMouseOver,
2129 fn: this.onMouseOut,
2134 * Or a shorthand syntax:<br>
2137 'click' : this.onClick,
2138 'mouseover' : this.onMouseOver,
2139 'mouseout' : this.onMouseOut,
2143 addListener : function(eventName, fn, scope, o){
2149 if (typeof eventName == 'object') {
2153 if (!me.filterOptRe.test(e)) {
2154 me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
2158 eventName = eventName.toLowerCase();
2159 ce = me.events[eventName] || TRUE;
2160 if (typeof ce == 'boolean') {
2161 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
2163 ce.addListener(fn, scope, typeof o == 'object' ? o : {});
2168 * Removes an event handler.
2169 * @param {String} eventName The type of event the handler was associated with.
2170 * @param {Function} handler The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2171 * @param {Object} scope (optional) The scope originally specified for the handler.
2173 removeListener : function(eventName, fn, scope){
2174 var ce = this.events[eventName.toLowerCase()];
2175 if (typeof ce == 'object') {
2176 ce.removeListener(fn, scope);
2181 * Removes all listeners for this object
2183 purgeListeners : function(){
2184 var events = this.events,
2189 if(typeof evt == 'object'){
2190 evt.clearListeners();
2196 * Adds the specified events to the list of events which this Observable may fire.
2197 * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
2198 * or the first event name string if multiple event names are being passed as separate parameters.
2199 * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
2201 this.addEvents('storeloaded', 'storecleared');
2204 addEvents : function(o){
2206 me.events = me.events || {};
2207 if (typeof o == 'string') {
2211 me.events[a[i]] = me.events[a[i]] || TRUE;
2214 Ext.applyIf(me.events, o);
2219 * Checks to see if this object has any listeners for a specified event
2220 * @param {String} eventName The name of the event to check for
2221 * @return {Boolean} True if the event is being listened for, else false
2223 hasListener : function(eventName){
2224 var e = this.events[eventName.toLowerCase()];
2225 return typeof e == 'object' && e.listeners.length > 0;
2229 * Suspend the firing of all events. (see {@link #resumeEvents})
2230 * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
2231 * after the {@link #resumeEvents} call instead of discarding all suspended events;
2233 suspendEvents : function(queueSuspended){
2234 this.eventsSuspended = TRUE;
2235 if(queueSuspended && !this.eventQueue){
2236 this.eventQueue = [];
2241 * Resume firing events. (see {@link #suspendEvents})
2242 * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
2243 * events fired during event suspension will be sent to any listeners now.
2245 resumeEvents : function(){
2247 queued = me.eventQueue || [];
2248 me.eventsSuspended = FALSE;
2249 delete me.eventQueue;
2250 EACH(queued, function(e) {
2251 me.fireEvent.apply(me, e);
2256 var OBSERVABLE = EXTUTIL.Observable.prototype;
2258 * Appends an event handler to this object (shorthand for {@link #addListener}.)
2259 * @param {String} eventName The type of event to listen for
2260 * @param {Function} handler The method the event invokes
2261 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2262 * <b>If omitted, defaults to the object which fired the event.</b>
2263 * @param {Object} options (optional) An object containing handler configuration.
2266 OBSERVABLE.on = OBSERVABLE.addListener;
2268 * Removes an event handler (shorthand for {@link #removeListener}.)
2269 * @param {String} eventName The type of event the handler was associated with.
2270 * @param {Function} handler The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2271 * @param {Object} scope (optional) The scope originally specified for the handler.
2274 OBSERVABLE.un = OBSERVABLE.removeListener;
2277 * Removes <b>all</b> added captures from the Observable.
2278 * @param {Observable} o The Observable to release
2281 EXTUTIL.Observable.releaseCapture = function(o){
2282 o.fireEvent = OBSERVABLE.fireEvent;
2285 function createTargeted(h, o, scope){
2287 if(o.target == arguments[0]){
2288 h.apply(scope, Array.prototype.slice.call(arguments, 0));
2293 function createBuffered(h, o, l, scope){
2294 l.task = new EXTUTIL.DelayedTask();
2296 l.task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
2300 function createSingle(h, e, fn, scope){
2302 e.removeListener(fn, scope);
2303 return h.apply(scope, arguments);
2307 function createDelayed(h, o, l, scope){
2309 var task = new EXTUTIL.DelayedTask();
2314 task.delay(o.delay || 10, h, scope, Array.prototype.slice.call(arguments, 0));
2318 EXTUTIL.Event = function(obj, name){
2321 this.listeners = [];
2324 EXTUTIL.Event.prototype = {
2325 addListener : function(fn, scope, options){
2328 scope = scope || me.obj;
2329 if(!me.isListening(fn, scope)){
2330 l = me.createListener(fn, scope, options);
2331 if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2332 me.listeners = me.listeners.slice(0);
2334 me.listeners.push(l);
2338 createListener: function(fn, scope, o){
2339 o = o || {}, scope = scope || this.obj;
2346 h = createTargeted(h, o, scope);
2349 h = createDelayed(h, o, l, scope);
2352 h = createSingle(h, this, fn, scope);
2355 h = createBuffered(h, o, l, scope);
2361 findListener : function(fn, scope){
2362 var list = this.listeners,
2366 scope = scope || this.obj;
2370 if(l.fn == fn && l.scope == scope){
2378 isListening : function(fn, scope){
2379 return this.findListener(fn, scope) != -1;
2382 removeListener : function(fn, scope){
2388 if((index = me.findListener(fn, scope)) != -1){
2390 me.listeners = me.listeners.slice(0);
2392 l = me.listeners[index];
2397 k = l.tasks && l.tasks.length;
2400 l.tasks[k].cancel();
2404 me.listeners.splice(index, 1);
2410 // Iterate to stop any buffered/delayed events
2411 clearListeners : function(){
2416 me.removeListener(l[i].fn, l[i].scope);
2422 listeners = me.listeners,
2423 len = listeners.length,
2429 var args = Array.prototype.slice.call(arguments, 0);
2430 for (; i < len; i++) {
2432 if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2433 return (me.firing = FALSE);
2444 * @class Ext.util.Observable
2446 Ext.apply(Ext.util.Observable.prototype, function(){
2447 // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
2448 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2450 function getMethodEvent(method){
2451 var e = (this.methodEvents = this.methodEvents ||
2452 {})[method], returnValue, v, cancel, obj = this;
2455 this.methodEvents[method] = e = {};
2456 e.originalFn = this[method];
2457 e.methodName = method;
2461 var makeCall = function(fn, scope, args){
2462 if((v = fn.apply(scope || obj, args)) !== undefined){
2463 if (typeof v == 'object') {
2464 if(v.returnValue !== undefined){
2465 returnValue = v.returnValue;
2469 cancel = !!v.cancel;
2481 this[method] = function(){
2482 var args = Array.prototype.slice.call(arguments, 0),
2484 returnValue = v = undefined;
2487 for(var i = 0, len = e.before.length; i < len; i++){
2489 makeCall(b.fn, b.scope, args);
2495 if((v = e.originalFn.apply(obj, args)) !== undefined){
2499 for(var i = 0, len = e.after.length; i < len; i++){
2501 makeCall(b.fn, b.scope, args);
2513 // these are considered experimental
2514 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2515 // adds an 'interceptor' called before the original method
2516 beforeMethod : function(method, fn, scope){
2517 getMethodEvent.call(this, method).before.push({
2523 // adds a 'sequence' called after the original method
2524 afterMethod : function(method, fn, scope){
2525 getMethodEvent.call(this, method).after.push({
2531 removeMethodListener: function(method, fn, scope){
2532 var e = this.getMethodEvent(method);
2533 for(var i = 0, len = e.before.length; i < len; i++){
2534 if(e.before[i].fn == fn && e.before[i].scope == scope){
2535 e.before.splice(i, 1);
2539 for(var i = 0, len = e.after.length; i < len; i++){
2540 if(e.after[i].fn == fn && e.after[i].scope == scope){
2541 e.after.splice(i, 1);
2548 * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
2549 * @param {Object} o The Observable whose events this object is to relay.
2550 * @param {Array} events Array of event names to relay.
2552 relayEvents : function(o, events){
2554 function createHandler(ename){
2556 return me.fireEvent.apply(me, [ename].concat(Array.prototype.slice.call(arguments, 0)));
2559 for(var i = 0, len = events.length; i < len; i++){
2560 var ename = events[i];
2561 me.events[ename] = me.events[ename] || true;
2562 o.on(ename, createHandler(ename), me);
2567 * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
2568 * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
2569 * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default
2570 * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
2571 * access the required target more quickly.</p>
2572 * <p>Example:</p><pre><code>
2573 Ext.override(Ext.form.Field, {
2574 // Add functionality to Field's initComponent to enable the change event to bubble
2575 initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
2576 this.enableBubble('change');
2579 // We know that we want Field's events to bubble directly to the FormPanel.
2580 getBubbleTarget : function() {
2581 if (!this.formPanel) {
2582 this.formPanel = this.findParentByType('form');
2584 return this.formPanel;
2588 var myForm = new Ext.formPanel({
2589 title: 'User Details',
2594 change: function() {
2595 // Title goes red if form has been modified.
2596 myForm.header.setStyle('color', 'red');
2601 * @param {String/Array} events The event name to bubble, or an Array of event names.
2603 enableBubble : function(events){
2605 if(!Ext.isEmpty(events)){
2606 events = Ext.isArray(events) ? events : Array.prototype.slice.call(arguments, 0);
2607 for(var i = 0, len = events.length; i < len; i++){
2608 var ename = events[i];
2609 ename = ename.toLowerCase();
2610 var ce = me.events[ename] || true;
2611 if (typeof ce == 'boolean') {
2612 ce = new Ext.util.Event(me, ename);
2613 me.events[ename] = ce;
2624 * Starts capture on the specified Observable. All events will be passed
2625 * to the supplied function with the event name + standard signature of the event
2626 * <b>before</b> the event is fired. If the supplied function returns false,
2627 * the event will not fire.
2628 * @param {Observable} o The Observable to capture events from.
2629 * @param {Function} fn The function to call when an event is fired.
2630 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
2633 Ext.util.Observable.capture = function(o, fn, scope){
2634 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
2639 * Sets observability on the passed class constructor.<p>
2640 * <p>This makes any event fired on any instance of the passed class also fire a single event through
2641 * the <i>class</i> allowing for central handling of events on many instances at once.</p>
2642 * <p>Usage:</p><pre><code>
2643 Ext.util.Observable.observeClass(Ext.data.Connection);
2644 Ext.data.Connection.on('beforerequest', function(con, options) {
2645 console.log('Ajax request made to ' + options.url);
2647 * @param {Function} c The class constructor to make observable.
2648 * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
2651 Ext.util.Observable.observeClass = function(c, listeners){
2654 Ext.apply(c, new Ext.util.Observable());
2655 Ext.util.Observable.capture(c.prototype, c.fireEvent, c);
2657 if(typeof listeners == 'object'){
2664 * @class Ext.EventManager
2665 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
2666 * several useful events directly.
2667 * See {@link Ext.EventObject} for more details on normalized event objects.
2671 Ext.EventManager = function(){
2674 docReadyState = false,
2675 DETECT_NATIVE = Ext.isGecko || Ext.isWebKit || Ext.isSafari,
2680 DOMCONTENTLOADED = "DOMContentLoaded",
2681 COMPLETE = 'complete',
2682 propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
2684 * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep
2685 * a reference to them so we can look them up at a later point.
2687 specialElCache = [];
2692 len = specialElCache.length,
2697 if(el.getElementById || el.navigator){
2699 for(; i < len; ++i){
2700 o = specialElCache[i];
2707 // for browsers that support it, ensure that give the el the same id
2709 specialElCache.push({
2718 if(!Ext.elCache[id]){
2719 Ext.Element.addToCache(new Ext.Element(el), id);
2721 Ext.elCache[id].skipGC = true;
2728 /// There is some jquery work around stuff here that isn't needed in Ext Core.
2729 function addListener(el, ename, fn, task, wrap, scope){
2730 el = Ext.getDom(el);
2732 es = Ext.elCache[id].events,
2735 wfn = E.on(el, ename, wrap);
2736 es[ename] = es[ename] || [];
2738 /* 0 = Original Function,
2739 1 = Event Manager Wrapped Function,
2741 3 = Adapter Wrapped Function,
2744 es[ename].push([fn, wrap, scope, wfn, task]);
2746 // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
2747 // without breaking ExtJS.
2749 // workaround for jQuery
2750 if(el.addEventListener && ename == "mousewheel"){
2751 var args = ["DOMMouseScroll", wrap, false];
2752 el.addEventListener.apply(el, args);
2753 Ext.EventManager.addListener(WINDOW, 'unload', function(){
2754 el.removeEventListener.apply(el, args);
2758 // fix stopped mousedowns on the document
2759 if(el == DOC && ename == "mousedown"){
2760 Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
2764 function doScrollChk(){
2766 'doScroll' will NOT work in a IFRAME/FRAMESET.
2767 The method succeeds but, a DOM query done immediately after -- FAILS.
2774 DOC.documentElement.doScroll('left');
2783 * @return {Boolean} True if the document is in a 'complete' state (or was determined to
2784 * be true by other means). If false, the state is evaluated again until canceled.
2786 function checkReadyState(e){
2788 if(Ext.isIE && doScrollChk()){
2791 if(DOC.readyState == COMPLETE){
2795 docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2800 function checkStyleSheets(e){
2801 styles || (styles = Ext.query('style, link[rel=stylesheet]'));
2802 if(styles.length == DOC.styleSheets.length){
2806 docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2810 function OperaDOMContentLoaded(e){
2811 DOC.removeEventListener(DOMCONTENTLOADED, arguments.callee, false);
2815 function fireDocReady(e){
2817 docReadyState = true; //only attempt listener removal once
2820 clearTimeout(docReadyProcId);
2823 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
2825 if(Ext.isIE && checkReadyState.bindIE){ //was this was actually set ??
2826 DOC.detachEvent('onreadystatechange', checkReadyState);
2828 E.un(WINDOW, "load", arguments.callee);
2830 if(docReadyEvent && !Ext.isReady){
2832 docReadyEvent.fire();
2833 docReadyEvent.listeners = [];
2838 function initDocReady(){
2839 docReadyEvent || (docReadyEvent = new Ext.util.Event());
2840 if (DETECT_NATIVE) {
2841 DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
2844 * Handle additional (exceptional) detection strategies here
2847 //Use readystatechange as a backup AND primary detection mechanism for a FRAME/IFRAME
2848 //See if page is already loaded
2849 if(!checkReadyState()){
2850 checkReadyState.bindIE = true;
2851 DOC.attachEvent('onreadystatechange', checkReadyState);
2854 }else if(Ext.isOpera ){
2856 Opera needs special treatment needed here because CSS rules are NOT QUITE
2857 available after DOMContentLoaded is raised.
2860 //See if page is already loaded and all styleSheets are in place
2861 (DOC.readyState == COMPLETE && checkStyleSheets()) ||
2862 DOC.addEventListener(DOMCONTENTLOADED, OperaDOMContentLoaded, false);
2864 }else if (Ext.isWebKit){
2865 //Fallback for older Webkits without DOMCONTENTLOADED support
2868 // no matter what, make sure it fires on load
2869 E.on(WINDOW, "load", fireDocReady);
2872 function createTargeted(h, o){
2874 var args = Ext.toArray(arguments);
2875 if(o.target == Ext.EventObject.setEvent(args[0]).target){
2876 h.apply(this, args);
2881 function createBuffered(h, o, task){
2883 // create new event object impl so new events don't wipe out properties
2884 task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
2888 function createSingle(h, el, ename, fn, scope){
2890 Ext.EventManager.removeListener(el, ename, fn, scope);
2895 function createDelayed(h, o, fn){
2897 var task = new Ext.util.DelayedTask(h);
2901 fn.tasks.push(task);
2902 task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);
2906 function listen(element, ename, opt, fn, scope){
2907 var o = (!opt || typeof opt == "boolean") ? {} : opt,
2908 el = Ext.getDom(element), task;
2911 scope = scope || o.scope;
2914 throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
2917 // prevent errors while unload occurring
2918 if(!Ext){// !window[xname]){ ==> can't we do this?
2921 e = Ext.EventObject.setEvent(e);
2924 if(!(t = e.getTarget(o.delegate, el))){
2933 if (o.preventDefault) {
2936 if (o.stopPropagation) {
2937 e.stopPropagation();
2943 fn.call(scope || el, e, t, o);
2946 h = createTargeted(h, o);
2949 h = createDelayed(h, o, fn);
2952 h = createSingle(h, el, ename, fn, scope);
2955 task = new Ext.util.DelayedTask(h);
2956 h = createBuffered(h, o, task);
2959 addListener(el, ename, fn, task, h, scope);
2965 * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
2966 * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
2967 * @param {String/HTMLElement} el The html element or id to assign the event handler to.
2968 * @param {String} eventName The name of the event to listen for.
2969 * @param {Function} handler The handler function the event invokes. This function is passed
2970 * the following parameters:<ul>
2971 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
2972 * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
2973 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
2974 * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
2976 * @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>.
2977 * @param {Object} options (optional) An object containing handler configuration properties.
2978 * This may contain any of the following properties:<ul>
2979 * <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>
2980 * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
2981 * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
2982 * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
2983 * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
2984 * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
2985 * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
2986 * <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>
2987 * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2988 * by the specified number of milliseconds. If the event fires again within that time, the original
2989 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2990 * <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>
2992 * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
2994 addListener : function(element, eventName, fn, scope, options){
2995 if(typeof eventName == 'object'){
2996 var o = eventName, e, val;
2999 if(!propRe.test(e)){
3000 if(Ext.isFunction(val)){
3002 listen(element, e, o, val, o.scope);
3004 // individual options
3005 listen(element, e, val);
3010 listen(element, eventName, options, fn, scope);
3015 * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
3016 * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
3017 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
3018 * @param {String} eventName The name of the event.
3019 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
3020 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
3021 * then this must refer to the same object.
3023 removeListener : function(el, eventName, fn, scope){
3024 el = Ext.getDom(el);
3026 f = el && (Ext.elCache[id].events)[eventName] || [],
3027 wrap, i, l, k, len, fnc;
3029 for (i = 0, len = f.length; i < len; i++) {
3031 /* 0 = Original Function,
3032 1 = Event Manager Wrapped Function,
3034 3 = Adapter Wrapped Function,
3037 if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) {
3041 k = fn.tasks && fn.tasks.length;
3044 fn.tasks[k].cancel();
3049 E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);
3051 // jQuery workaround that should be removed from Ext Core
3052 if(wrap && el.addEventListener && eventName == "mousewheel"){
3053 el.removeEventListener("DOMMouseScroll", wrap, false);
3056 // fix stopped mousedowns on the document
3057 if(wrap && el == DOC && eventName == "mousedown"){
3058 Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3062 if (f.length === 0) {
3063 delete Ext.elCache[id].events[eventName];
3065 for (k in Ext.elCache[id].events) {
3068 Ext.elCache[id].events = {};
3075 * Removes all event handers from an element. Typically you will use {@link Ext.Element#removeAllListeners}
3076 * directly on an Element in favor of calling this version.
3077 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
3079 removeAll : function(el){
3080 el = Ext.getDom(el);
3082 ec = Ext.elCache[id] || {},
3083 es = ec.events || {},
3084 f, i, len, ename, fn, k, wrap;
3087 if(es.hasOwnProperty(ename)){
3089 /* 0 = Original Function,
3090 1 = Event Manager Wrapped Function,
3092 3 = Adapter Wrapped Function,
3095 for (i = 0, len = f.length; i < len; i++) {
3100 if(fn[0].tasks && (k = fn[0].tasks.length)) {
3102 fn[0].tasks[k].cancel();
3107 E.un(el, ename, E.extAdapter ? fn[3] : wrap);
3109 // jQuery workaround that should be removed from Ext Core
3110 if(el.addEventListener && wrap && ename == "mousewheel"){
3111 el.removeEventListener("DOMMouseScroll", wrap, false);
3114 // fix stopped mousedowns on the document
3115 if(wrap && el == DOC && ename == "mousedown"){
3116 Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3121 if (Ext.elCache[id]) {
3122 Ext.elCache[id].events = {};
3126 getListeners : function(el, eventName) {
3127 el = Ext.getDom(el);
3129 ec = Ext.elCache[id] || {},
3130 es = ec.events || {},
3132 if (es && es[eventName]) {
3133 return es[eventName];
3139 purgeElement : function(el, recurse, eventName) {
3140 el = Ext.getDom(el);
3142 ec = Ext.elCache[id] || {},
3143 es = ec.events || {},
3146 if (es && es.hasOwnProperty(eventName)) {
3148 for (i = 0, len = f.length; i < len; i++) {
3149 Ext.EventManager.removeListener(el, eventName, f[i][0]);
3153 Ext.EventManager.removeAll(el);
3155 if (recurse && el && el.childNodes) {
3156 for (i = 0, len = el.childNodes.length; i < len; i++) {
3157 Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);
3162 _unload : function() {
3164 for (el in Ext.elCache) {
3165 Ext.EventManager.removeAll(el);
3168 delete Ext.Element._flyweights;
3170 // Abort any outstanding Ajax requests
3174 ajax = Ext.lib.Ajax;
3175 (typeof ajax.conn == 'object') ? conn = ajax.conn : conn = {};
3179 ajax.abort({conn: c, tId: tid});
3184 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
3185 * accessed shorthanded as Ext.onReady().
3186 * @param {Function} fn The method the event invokes.
3187 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3188 * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3189 * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3191 onDocumentReady : function(fn, scope, options){
3192 if(Ext.isReady){ // if it already fired or document.body is present
3193 docReadyEvent || (docReadyEvent = new Ext.util.Event());
3194 docReadyEvent.addListener(fn, scope, options);
3195 docReadyEvent.fire();
3196 docReadyEvent.listeners = [];
3201 options = options || {};
3202 options.delay = options.delay || 1;
3203 docReadyEvent.addListener(fn, scope, options);
3208 * Forces a document ready state transition for the framework. Used when Ext is loaded
3209 * into a DOM structure AFTER initial page load (Google API or other dynamic load scenario.
3210 * Any pending 'onDocumentReady' handlers will be fired (if not already handled).
3212 fireDocReady : fireDocReady
3215 * Appends an event handler to an element. Shorthand for {@link #addListener}.
3216 * @param {String/HTMLElement} el The html element or id to assign the event handler to
3217 * @param {String} eventName The name of the event to listen for.
3218 * @param {Function} handler The handler function the event invokes.
3219 * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.
3220 * @param {Object} options (optional) An object containing standard {@link #addListener} options
3221 * @member Ext.EventManager
3224 pub.on = pub.addListener;
3226 * Removes an event handler from an element. Shorthand for {@link #removeListener}.
3227 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
3228 * @param {String} eventName The name of the event.
3229 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #on} call.</b>
3230 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
3231 * then this must refer to the same object.
3232 * @member Ext.EventManager
3235 pub.un = pub.removeListener;
3237 pub.stoppedMouseDownEvent = new Ext.util.Event();
3241 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
3242 * @param {Function} fn The method the event invokes.
3243 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3244 * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3245 * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3249 Ext.onReady = Ext.EventManager.onDocumentReady;
3252 //Initialize doc classes
3255 var initExtCss = function(){
3256 // find the body element
3257 var bd = document.body || document.getElementsByTagName('body')[0];
3258 if(!bd){ return false; }
3260 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
3261 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
3262 : Ext.isOpera ? "ext-opera"
3263 : Ext.isWebKit ? "ext-webkit" : ""];
3266 cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
3267 }else if(Ext.isChrome){
3268 cls.push("ext-chrome");
3272 cls.push("ext-mac");
3275 cls.push("ext-linux");
3278 if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
3279 var p = bd.parentNode;
3281 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';
3284 bd.className += cls.join(' ');
3289 Ext.onReady(initExtCss);
3295 * @class Ext.EventObject
3296 * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
3297 * wraps the browser's native event-object normalizing cross-browser differences,
3298 * such as which mouse button is clicked, keys pressed, mechanisms to stop
3299 * event-propagation along with a method to prevent default actions from taking place.
3300 * <p>For example:</p>
3302 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
3304 var target = e.getTarget(); // same as t (the target HTMLElement)
3307 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.Element}
3308 myDiv.on( // 'on' is shorthand for addListener
3309 "click", // perform an action on click of myDiv
3310 handleClick // reference to the action handler
3312 // other methods to do the same:
3313 Ext.EventManager.on("myDiv", 'click', handleClick);
3314 Ext.EventManager.addListener("myDiv", 'click', handleClick);
3318 Ext.EventObject = function(){
3319 var E = Ext.lib.Event,
3320 // safari keypress events for special keys return bad keycodes
3324 63235 : 39, // right
3327 63276 : 33, // page up
3328 63277 : 34, // page down
3329 63272 : 46, // delete
3333 // normalize button clicks
3334 btnMap = Ext.isIE ? {1:0,4:1,2:2} :
3335 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
3337 Ext.EventObjectImpl = function(e){
3339 this.setEvent(e.browserEvent || e);
3343 Ext.EventObjectImpl.prototype = {
3345 setEvent : function(e){
3347 if(e == me || (e && e.browserEvent)){ // already wrapped
3350 me.browserEvent = e;
3352 // normalize buttons
3353 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
3354 if(e.type == 'click' && me.button == -1){
3358 me.shiftKey = e.shiftKey;
3359 // mac metaKey behaves like ctrlKey
3360 me.ctrlKey = e.ctrlKey || e.metaKey || false;
3361 me.altKey = e.altKey;
3362 // in getKey these will be normalized for the mac
3363 me.keyCode = e.keyCode;
3364 me.charCode = e.charCode;
3365 // cache the target for the delayed and or buffered events
3366 me.target = E.getTarget(e);
3371 me.shiftKey = false;
3383 * Stop the event (preventDefault and stopPropagation)
3385 stopEvent : function(){
3387 if(me.browserEvent){
3388 if(me.browserEvent.type == 'mousedown'){
3389 Ext.EventManager.stoppedMouseDownEvent.fire(me);
3391 E.stopEvent(me.browserEvent);
3396 * Prevents the browsers default handling of the event.
3398 preventDefault : function(){
3399 if(this.browserEvent){
3400 E.preventDefault(this.browserEvent);
3405 * Cancels bubbling of the event.
3407 stopPropagation : function(){
3409 if(me.browserEvent){
3410 if(me.browserEvent.type == 'mousedown'){
3411 Ext.EventManager.stoppedMouseDownEvent.fire(me);
3413 E.stopPropagation(me.browserEvent);
3418 * Gets the character code for the event.
3421 getCharCode : function(){
3422 return this.charCode || this.keyCode;
3426 * Returns a normalized keyCode for the event.
3427 * @return {Number} The key code
3429 getKey : function(){
3430 return this.normalizeKey(this.keyCode || this.charCode)
3434 normalizeKey: function(k){
3435 return Ext.isSafari ? (safariKeys[k] || k) : k;
3439 * Gets the x coordinate of the event.
3442 getPageX : function(){
3447 * Gets the y coordinate of the event.
3450 getPageY : function(){
3455 * Gets the page coordinates of the event.
3456 * @return {Array} The xy values like [x, y]
3463 * Gets the target for the event.
3464 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
3465 * @param {Number/Mixed} maxDepth (optional) The max depth to
3466 search as a number or element (defaults to 10 || document.body)
3467 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
3468 * @return {HTMLelement}
3470 getTarget : function(selector, maxDepth, returnEl){
3471 return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
3475 * Gets the related target.
3476 * @return {HTMLElement}
3478 getRelatedTarget : function(){
3479 return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
3483 * Normalizes mouse wheel delta across browsers
3484 * @return {Number} The delta
3486 getWheelDelta : function(){
3487 var e = this.browserEvent;
3489 if(e.wheelDelta){ /* IE/Opera. */
3490 delta = e.wheelDelta/120;
3491 }else if(e.detail){ /* Mozilla case. */
3492 delta = -e.detail/3;
3498 * 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.
3499 * Example usage:<pre><code>
3500 // Handle click on any child of an element
3501 Ext.getBody().on('click', function(e){
3502 if(e.within('some-el')){
3503 alert('Clicked on a child of some-el!');
3507 // Handle click directly on an element, ignoring clicks on child nodes
3508 Ext.getBody().on('click', function(e,t){
3509 if((t.id == 'some-el') && !e.within(t, true)){
3510 alert('Clicked directly on some-el!');
3514 * @param {Mixed} el The id, DOM element or Ext.Element to check
3515 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
3516 * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
3519 within : function(el, related, allowEl){
3521 var t = this[related ? "getRelatedTarget" : "getTarget"]();
3522 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
3528 return new Ext.EventObjectImpl();
3531 * @class Ext.EventManager
3533 Ext.apply(Ext.EventManager, function(){
3539 propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
3542 // note 1: IE fires ONLY the keydown event on specialkey autorepeat
3543 // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
3544 // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
3545 useKeydown = Ext.isWebKit ?
3546 Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
3547 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
3551 doResizeEvent: function(){
3552 var h = D.getViewHeight(),
3553 w = D.getViewWidth();
3555 //whacky problem in IE where the resize event will fire even though the w/h are the same.
3556 if(curHeight != h || curWidth != w){
3557 resizeEvent.fire(curWidth = w, curHeight = h);
3562 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
3563 * passes new viewport width and height to handlers.
3564 * @param {Function} fn The handler function the window resize event invokes.
3565 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3566 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
3568 onWindowResize : function(fn, scope, options){
3570 resizeEvent = new Ext.util.Event();
3571 resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
3572 Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
3574 resizeEvent.addListener(fn, scope, options);
3577 // exposed only to allow manual firing
3578 fireWindowResize : function(){
3580 resizeTask.delay(100);
3585 * Adds a listener to be notified when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
3586 * @param {Function} fn The function the event invokes.
3587 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3588 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
3590 onTextResize : function(fn, scope, options){
3592 textEvent = new Ext.util.Event();
3593 var textEl = new Ext.Element(document.createElement('div'));
3594 textEl.dom.className = 'x-text-resize';
3595 textEl.dom.innerHTML = 'X';
3596 textEl.appendTo(document.body);
3597 textSize = textEl.dom.offsetHeight;
3598 setInterval(function(){
3599 if(textEl.dom.offsetHeight != textSize){
3600 textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
3602 }, this.textResizeInterval);
3604 textEvent.addListener(fn, scope, options);
3608 * Removes the passed window resize listener.
3609 * @param {Function} fn The method the event invokes
3610 * @param {Object} scope The scope of handler
3612 removeResizeListener : function(fn, scope){
3614 resizeEvent.removeListener(fn, scope);
3619 fireResize : function(){
3621 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
3626 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
3628 textResizeInterval : 50,
3631 * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
3635 // protected for use inside the framework
3636 // detects whether we should use keydown or keypress based on the browser.
3637 useKeydown: useKeydown
3641 Ext.EventManager.on = Ext.EventManager.addListener;
3644 Ext.apply(Ext.EventObjectImpl.prototype, {
3645 /** Key constant @type Number */
3647 /** Key constant @type Number */
3649 /** Key constant @type Number */
3651 /** Key constant @type Number */
3653 /** Key constant @type Number */
3655 /** Key constant @type Number */
3657 /** Key constant @type Number */
3659 CONTROL : 17, // legacy
3660 /** Key constant @type Number */
3662 /** Key constant @type Number */
3664 /** Key constant @type Number */
3666 /** Key constant @type Number */
3668 /** Key constant @type Number */
3670 /** Key constant @type Number */
3672 PAGEUP : 33, // legacy
3673 /** Key constant @type Number */
3675 PAGEDOWN : 34, // legacy
3676 /** Key constant @type Number */
3678 /** Key constant @type Number */
3680 /** Key constant @type Number */
3682 /** Key constant @type Number */
3684 /** Key constant @type Number */
3686 /** Key constant @type Number */
3688 /** Key constant @type Number */
3690 /** Key constant @type Number */
3692 /** Key constant @type Number */
3694 /** Key constant @type Number */
3696 /** Key constant @type Number */
3698 /** Key constant @type Number */
3700 /** Key constant @type Number */
3702 /** Key constant @type Number */
3704 /** Key constant @type Number */
3706 /** Key constant @type Number */
3708 /** Key constant @type Number */
3710 /** Key constant @type Number */
3712 /** Key constant @type Number */
3714 /** Key constant @type Number */
3716 /** Key constant @type Number */
3718 /** Key constant @type Number */
3720 /** Key constant @type Number */
3722 /** Key constant @type Number */
3724 /** Key constant @type Number */
3726 /** Key constant @type Number */
3728 /** Key constant @type Number */
3730 /** Key constant @type Number */
3732 /** Key constant @type Number */
3734 /** Key constant @type Number */
3736 /** Key constant @type Number */
3738 /** Key constant @type Number */
3740 /** Key constant @type Number */
3742 /** Key constant @type Number */
3744 /** Key constant @type Number */
3746 /** Key constant @type Number */
3748 /** Key constant @type Number */
3750 /** Key constant @type Number */
3752 /** Key constant @type Number */
3754 /** Key constant @type Number */
3756 /** Key constant @type Number */
3758 /** Key constant @type Number */
3760 /** Key constant @type Number */
3762 /** Key constant @type Number */
3764 /** Key constant @type Number */
3766 /** Key constant @type Number */
3768 /** Key constant @type Number */
3770 /** Key constant @type Number */
3772 /** Key constant @type Number */
3774 /** Key constant @type Number */
3776 /** Key constant @type Number */
3778 /** Key constant @type Number */
3780 /** Key constant @type Number */
3782 /** Key constant @type Number */
3784 /** Key constant @type Number */
3786 /** Key constant @type Number */
3788 /** Key constant @type Number */
3790 /** Key constant @type Number */
3792 /** Key constant @type Number */
3794 /** Key constant @type Number */
3796 /** Key constant @type Number */
3798 /** Key constant @type Number */
3800 /** Key constant @type Number */
3802 /** Key constant @type Number */
3804 /** Key constant @type Number */
3806 /** Key constant @type Number */
3808 /** Key constant @type Number */
3810 /** Key constant @type Number */
3812 /** Key constant @type Number */
3814 /** Key constant @type Number */
3816 /** Key constant @type Number */
3818 /** Key constant @type Number */
3820 /** Key constant @type Number */
3824 isNavKeyPress : function(){
3826 k = this.normalizeKey(me.keyCode);
3827 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
3833 isSpecialKey : function(){
3834 var k = this.normalizeKey(this.keyCode);
3835 return (this.type == 'keypress' && this.ctrlKey) ||
3836 this.isNavKeyPress() ||
3837 (k == this.BACKSPACE) || // Backspace
3838 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
3839 (k >= 44 && k <= 46); // Print Screen, Insert, Delete
3842 getPoint : function(){
3843 return new Ext.lib.Point(this.xy[0], this.xy[1]);
3847 * Returns true if the control, meta, shift or alt key was pressed during this event.
3850 hasModifier : function(){
3851 return ((this.ctrlKey || this.altKey) || this.shiftKey);
3854 * @class Ext.Element
3855 * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
3856 * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
3857 * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
3858 * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
3859 * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
3863 var el = Ext.get("my-div");
3865 // by DOM element reference
3866 var el = Ext.get(myDivElement);
3868 * <b>Animations</b><br />
3869 * <p>When an element is manipulated, by default there is no animation.</p>
3871 var el = Ext.get("my-div");
3876 * <p>Many of the functions for manipulating an element have an optional "animate" parameter. This
3877 * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
3879 // default animation
3880 el.setWidth(100, true);
3883 * <p>To configure the effects, an object literal with animation options to use as the Element animation
3884 * configuration object can also be specified. Note that the supported Element animation configuration
3885 * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects. The supported
3886 * Element animation configuration options are:</p>
3888 Option Default Description
3889 --------- -------- ---------------------------------------------
3890 {@link Ext.Fx#duration duration} .35 The duration of the animation in seconds
3891 {@link Ext.Fx#easing easing} easeOut The easing method
3892 {@link Ext.Fx#callback callback} none A function to execute when the anim completes
3893 {@link Ext.Fx#scope scope} this The scope (this) of the callback function
3897 // Element animation options object
3899 {@link Ext.Fx#duration duration}: 1,
3900 {@link Ext.Fx#easing easing}: 'elasticIn',
3901 {@link Ext.Fx#callback callback}: this.foo,
3902 {@link Ext.Fx#scope scope}: this
3904 // animation with some options set
3905 el.setWidth(100, opt);
3907 * <p>The Element animation object being used for the animation will be set on the options
3908 * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
3910 // using the "anim" property to get the Anim object
3911 if(opt.anim.isAnimated()){
3915 * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
3916 * <p><b> Composite (Collections of) Elements</b></p>
3917 * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
3918 * @constructor Create a new Element directly.
3919 * @param {String/HTMLElement} element
3920 * @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).
3925 Ext.Element = function(element, forceNew){
3926 var dom = typeof element == "string" ?
3927 DOC.getElementById(element) : element,
3930 if(!dom) return null;
3934 if(!forceNew && id && Ext.elCache[id]){ // element object already exists
3935 return Ext.elCache[id].el;
3945 * The DOM element ID
3948 this.id = id || Ext.id(dom);
3951 var D = Ext.lib.Dom,
3960 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
3961 * @param {Object} o The object with the attributes
3962 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
3963 * @return {Ext.Element} this
3965 set : function(o, useSet){
3969 useSet = (useSet !== false) && !!el.setAttribute;
3972 if (o.hasOwnProperty(attr)) {
3974 if (attr == 'style') {
3975 DH.applyStyles(el, val);
3976 } else if (attr == 'cls') {
3978 } else if (useSet) {
3979 el.setAttribute(attr, val);
3991 * Fires when a mouse click is detected within the element.
3992 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3993 * @param {HtmlElement} t The target of the event.
3994 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3997 * @event contextmenu
3998 * Fires when a right click is detected within the element.
3999 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4000 * @param {HtmlElement} t The target of the event.
4001 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4005 * Fires when a mouse double click is detected within the element.
4006 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4007 * @param {HtmlElement} t The target of the event.
4008 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4012 * Fires when a mousedown is detected within the element.
4013 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4014 * @param {HtmlElement} t The target of the event.
4015 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4019 * Fires when a mouseup is detected within the element.
4020 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4021 * @param {HtmlElement} t The target of the event.
4022 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4026 * Fires when a mouseover is detected within the element.
4027 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4028 * @param {HtmlElement} t The target of the event.
4029 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4033 * Fires when a mousemove is detected with the element.
4034 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4035 * @param {HtmlElement} t The target of the event.
4036 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4040 * Fires when a mouseout is detected with the element.
4041 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4042 * @param {HtmlElement} t The target of the event.
4043 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4047 * Fires when the mouse enters the element.
4048 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4049 * @param {HtmlElement} t The target of the event.
4050 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4054 * Fires when the mouse leaves the element.
4055 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4056 * @param {HtmlElement} t The target of the event.
4057 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4063 * Fires when a keypress is detected within the element.
4064 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4065 * @param {HtmlElement} t The target of the event.
4066 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4070 * Fires when a keydown is detected within the element.
4071 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4072 * @param {HtmlElement} t The target of the event.
4073 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4077 * Fires when a keyup is detected within the element.
4078 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4079 * @param {HtmlElement} t The target of the event.
4080 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4084 // HTML frame/object events
4087 * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
4088 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4089 * @param {HtmlElement} t The target of the event.
4090 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4094 * 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.
4095 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4096 * @param {HtmlElement} t The target of the event.
4097 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4101 * Fires when an object/image is stopped from loading before completely loaded.
4102 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4103 * @param {HtmlElement} t The target of the event.
4104 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4108 * Fires when an object/image/frame cannot be loaded properly.
4109 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4110 * @param {HtmlElement} t The target of the event.
4111 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4115 * Fires when a document view is resized.
4116 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4117 * @param {HtmlElement} t The target of the event.
4118 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4122 * Fires when a document view is scrolled.
4123 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4124 * @param {HtmlElement} t The target of the event.
4125 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4131 * Fires when a user selects some text in a text field, including input and textarea.
4132 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4133 * @param {HtmlElement} t The target of the event.
4134 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4138 * Fires when a control loses the input focus and its value has been modified since gaining focus.
4139 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4140 * @param {HtmlElement} t The target of the event.
4141 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4145 * Fires when a form is submitted.
4146 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4147 * @param {HtmlElement} t The target of the event.
4148 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4152 * Fires when a form is reset.
4153 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4154 * @param {HtmlElement} t The target of the event.
4155 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4159 * Fires when an element receives focus either via the pointing device or by tab navigation.
4160 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4161 * @param {HtmlElement} t The target of the event.
4162 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4166 * Fires when an element loses focus either via the pointing device or by tabbing navigation.
4167 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4168 * @param {HtmlElement} t The target of the event.
4169 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4172 // User Interface events
4175 * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
4176 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4177 * @param {HtmlElement} t The target of the event.
4178 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4181 * @event DOMFocusOut
4182 * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
4183 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4184 * @param {HtmlElement} t The target of the event.
4185 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4188 * @event DOMActivate
4189 * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
4190 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4191 * @param {HtmlElement} t The target of the event.
4192 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4195 // DOM Mutation events
4197 * @event DOMSubtreeModified
4198 * Where supported. Fires when the subtree is modified.
4199 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4200 * @param {HtmlElement} t The target of the event.
4201 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4204 * @event DOMNodeInserted
4205 * Where supported. Fires when a node has been added as a child of another node.
4206 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4207 * @param {HtmlElement} t The target of the event.
4208 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4211 * @event DOMNodeRemoved
4212 * Where supported. Fires when a descendant node of the element is removed.
4213 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4214 * @param {HtmlElement} t The target of the event.
4215 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4218 * @event DOMNodeRemovedFromDocument
4219 * Where supported. Fires when a node is being removed from a document.
4220 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4221 * @param {HtmlElement} t The target of the event.
4222 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4225 * @event DOMNodeInsertedIntoDocument
4226 * Where supported. Fires when a node is being inserted into a document.
4227 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4228 * @param {HtmlElement} t The target of the event.
4229 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4232 * @event DOMAttrModified
4233 * Where supported. Fires when an attribute has been modified.
4234 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4235 * @param {HtmlElement} t The target of the event.
4236 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4239 * @event DOMCharacterDataModified
4240 * Where supported. Fires when the character data has been modified.
4241 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4242 * @param {HtmlElement} t The target of the event.
4243 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4247 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
4253 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
4254 * @param {String} selector The simple selector to test
4255 * @return {Boolean} True if this element matches the selector, else false
4257 is : function(simpleSelector){
4258 return Ext.DomQuery.is(this.dom, simpleSelector);
4262 * Tries to focus the element. Any exceptions are caught and ignored.
4263 * @param {Number} defer (optional) Milliseconds to defer the focus
4264 * @return {Ext.Element} this
4266 focus : function(defer, /* private */ dom) {
4268 dom = dom || me.dom;
4271 me.focus.defer(defer, null, [null, dom]);
4280 * Tries to blur the element. Any exceptions are caught and ignored.
4281 * @return {Ext.Element} this
4291 * Returns the value of the "value" attribute
4292 * @param {Boolean} asNumber true to parse the value as a number
4293 * @return {String/Number}
4295 getValue : function(asNumber){
4296 var val = this.dom.value;
4297 return asNumber ? parseInt(val, 10) : val;
4301 * Appends an event handler to this element. The shorthand version {@link #on} is equivalent.
4302 * @param {String} eventName The name of event to handle.
4303 * @param {Function} fn The handler function the event invokes. This function is passed
4304 * the following parameters:<ul>
4305 * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
4306 * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
4307 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
4308 * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
4310 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4311 * <b>If omitted, defaults to this Element.</b>.
4312 * @param {Object} options (optional) An object containing handler configuration properties.
4313 * This may contain any of the following properties:<ul>
4314 * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4315 * <b>If omitted, defaults to this Element.</b></div></li>
4316 * <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>
4317 * <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>
4318 * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
4319 * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
4320 * <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>
4321 * <li><b>target</b> Ext.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>
4322 * <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>
4323 * <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>
4324 * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
4325 * by the specified number of milliseconds. If the event fires again within that time, the original
4326 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
4329 * <b>Combining Options</b><br>
4330 * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
4331 * addListener. The two are equivalent. Using the options argument, it is possible to combine different
4332 * types of listeners:<br>
4334 * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
4335 * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
4337 el.on('click', this.onClick, this, {
4342 });</code></pre></p>
4344 * <b>Attaching multiple handlers in 1 call</b><br>
4345 * The method also allows for a single argument to be passed which is a config object containing properties
4346 * which specify multiple handlers.</p>
4356 fn: this.onMouseOver,
4360 fn: this.onMouseOut,
4365 * Or a shorthand syntax:<br>
4366 * Code:<pre><code></p>
4368 'click' : this.onClick,
4369 'mouseover' : this.onMouseOver,
4370 'mouseout' : this.onMouseOut,
4374 * <p><b>delegate</b></p>
4375 * <p>This is a configuration option that you can pass along when registering a handler for
4376 * an event to assist with event delegation. Event delegation is a technique that is used to
4377 * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
4378 * for a container element as opposed to each element within a container. By setting this
4379 * configuration option to a simple selector, the target element will be filtered to look for
4380 * a descendant of the target.
4381 * For example:<pre><code>
4382 // using this markup:
4384 <p id='p1'>paragraph one</p>
4385 <p id='p2' class='clickable'>paragraph two</p>
4386 <p id='p3'>paragraph three</p>
4388 // utilize event delegation to registering just one handler on the container element:
4389 el = Ext.get('elId');
4394 console.info(t.id); // 'p2'
4398 // filter the target element to be a descendant with the class 'clickable'
4399 delegate: '.clickable'
4403 * @return {Ext.Element} this
4405 addListener : function(eventName, fn, scope, options){
4406 Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
4411 * Removes an event handler from this element. The shorthand version {@link #un} is equivalent.
4412 * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
4413 * listener, the same scope must be specified here.
4416 el.removeListener('click', this.handlerFn);
4418 el.un('click', this.handlerFn);
4420 * @param {String} eventName The name of the event from which to remove the handler.
4421 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4422 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4423 * then this must refer to the same object.
4424 * @return {Ext.Element} this
4426 removeListener : function(eventName, fn, scope){
4427 Ext.EventManager.removeListener(this.dom, eventName, fn, scope || this);
4432 * Removes all previous added listeners from this element
4433 * @return {Ext.Element} this
4435 removeAllListeners : function(){
4436 Ext.EventManager.removeAll(this.dom);
4441 * Recursively removes all previous added listeners from this element and its children
4442 * @return {Ext.Element} this
4444 purgeAllListeners : function() {
4445 Ext.EventManager.purgeElement(this, true);
4449 * @private Test if size has a unit, otherwise appends the default
4451 addUnits : function(size){
4452 if(size === "" || size == "auto" || size === undefined){
4454 } else if(!isNaN(size) || !unitPattern.test(size)){
4455 size = size + (this.defaultUnit || 'px');
4461 * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
4462 * from a specified URL. Note that this is subject to the <a href="http://en.wikipedia.org/wiki/Same_origin_policy">Same Origin Policy</a></p>
4463 * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt><script></tt> elements. This is a browser restriction.</p>
4464 * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
4465 * exactly how to request the HTML.
4466 * @return {Ext.Element} this
4468 load : function(url, params, cb){
4469 Ext.Ajax.request(Ext.apply({
4471 url: url.url || url,
4474 indicatorText: url.indicatorText || ''
4475 }, Ext.isObject(url) ? url : {}));
4480 * Tests various css rules/browsers to determine if this element uses a border box
4483 isBorderBox : function(){
4484 return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
4488 * <p>Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode}</p>
4490 remove : function(){
4496 Ext.removeNode(dom);
4501 * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
4502 * @param {Function} overFn The function to call when the mouse enters the Element.
4503 * @param {Function} outFn The function to call when the mouse leaves the Element.
4504 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
4505 * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
4506 * @return {Ext.Element} this
4508 hover : function(overFn, outFn, scope, options){
4510 me.on('mouseenter', overFn, scope || me.dom, options);
4511 me.on('mouseleave', outFn, scope || me.dom, options);
4516 * Returns true if this element is an ancestor of the passed element
4517 * @param {HTMLElement/String} el The element to check
4518 * @return {Boolean} True if this element is an ancestor of el, else false
4520 contains : function(el){
4521 return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
4525 * Returns the value of a namespaced attribute from the element's underlying DOM node.
4526 * @param {String} namespace The namespace in which to look for the attribute
4527 * @param {String} name The attribute name
4528 * @return {String} The attribute value
4531 getAttributeNS : function(ns, name){
4532 return this.getAttribute(name, ns);
4536 * Returns the value of an attribute from the element's underlying DOM node.
4537 * @param {String} name The attribute name
4538 * @param {String} namespace (optional) The namespace in which to look for the attribute
4539 * @return {String} The attribute value
4541 getAttribute : Ext.isIE ? function(name, ns){
4543 type = typeof d[ns + ":" + name];
4545 if(['undefined', 'unknown'].indexOf(type) == -1){
4546 return d[ns + ":" + name];
4549 } : function(name, ns){
4551 return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
4555 * Update the innerHTML of this element
4556 * @param {String} html The new HTML
4557 * @return {Ext.Element} this
4559 update : function(html) {
4561 this.dom.innerHTML = html;
4567 var ep = El.prototype;
4569 El.addMethods = function(o){
4574 * Appends an event handler (shorthand for {@link #addListener}).
4575 * @param {String} eventName The name of event to handle.
4576 * @param {Function} fn The handler function the event invokes.
4577 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
4578 * @param {Object} options (optional) An object containing standard {@link #addListener} options
4579 * @member Ext.Element
4582 ep.on = ep.addListener;
4585 * Removes an event handler from this element (see {@link #removeListener} for additional notes).
4586 * @param {String} eventName The name of the event from which to remove the handler.
4587 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4588 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4589 * then this must refer to the same object.
4590 * @return {Ext.Element} this
4591 * @member Ext.Element
4594 ep.un = ep.removeListener;
4597 * true to automatically adjust width and height settings for box-model issues (default to true)
4599 ep.autoBoxAdjust = true;
4602 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
4610 * Retrieves Ext.Element objects.
4611 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4612 * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4613 * its ID, use {@link Ext.ComponentMgr#get}.</p>
4614 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4615 * object was recreated with the same id via AJAX or DOM.</p>
4616 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4617 * @return {Element} The Element object (or null if no matching element was found)
4619 * @member Ext.Element
4622 El.get = function(el){
4626 if(!el){ return null; }
4627 if (typeof el == "string") { // element id
4628 if (!(elm = DOC.getElementById(el))) {
4631 if (EC[el] && EC[el].el) {
4635 ex = El.addToCache(new El(elm));
4638 } else if (el.tagName) { // dom element
4642 if (EC[id] && EC[id].el) {
4646 ex = El.addToCache(new El(el));
4649 } else if (el instanceof El) {
4651 // refresh dom element in case no longer valid,
4652 // catch case where it hasn't been appended
4654 // If an el instance is passed, don't pass to getElementById without some kind of id
4655 if (Ext.isIE && (el.id == undefined || el.id == '')) {
4658 el.dom = DOC.getElementById(el.id) || el.dom;
4662 } else if(el.isComposite) {
4664 } else if(Ext.isArray(el)) {
4665 return El.select(el);
4666 } else if(el == DOC) {
4667 // create a bogus element object representing the document object
4669 var f = function(){};
4670 f.prototype = El.prototype;
4679 El.addToCache = function(el, id){
4689 // private method for getting and setting element data
4690 El.data = function(el, key, value){
4695 var c = EC[el.id].data;
4696 if(arguments.length == 2){
4699 return (c[key] = value);
4704 // Garbage collection - uncache elements/purge listeners on orphaned elements
4705 // so we don't hold a reference and cause the browser to retain them
4706 function garbageCollect(){
4707 if(!Ext.enableGarbageCollector){
4708 clearInterval(El.collectorThreadId);
4722 // -------------------------------------------------------
4723 // Determining what is garbage:
4724 // -------------------------------------------------------
4726 // dom node is null, definitely garbage
4727 // -------------------------------------------------------
4729 // no parentNode == direct orphan, definitely garbage
4730 // -------------------------------------------------------
4731 // !d.offsetParent && !document.getElementById(eid)
4732 // display none elements have no offsetParent so we will
4733 // also try to look it up by it's id. However, check
4734 // offsetParent first so we don't do unneeded lookups.
4735 // This enables collection of elements that are not orphans
4736 // directly, but somewhere up the line they have an orphan
4738 // -------------------------------------------------------
4739 if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
4740 if(Ext.enableListenerCollection){
4741 Ext.EventManager.removeAll(d);
4746 // Cleanup IE Object leaks
4752 EC = Ext.elCache = t;
4756 El.collectorThreadId = setInterval(garbageCollect, 30000);
4758 var flyFn = function(){};
4759 flyFn.prototype = El.prototype;
4762 El.Flyweight = function(dom){
4766 El.Flyweight.prototype = new flyFn();
4767 El.Flyweight.prototype.isFlyweight = true;
4768 El._flyweights = {};
4771 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4772 * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4773 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4774 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4775 * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4776 * @param {String/HTMLElement} el The dom node or id
4777 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4778 * (e.g. internally Ext uses "_global")
4779 * @return {Element} The shared Element object (or null if no matching element was found)
4780 * @member Ext.Element
4783 El.fly = function(el, named){
4785 named = named || '_global';
4787 if (el = Ext.getDom(el)) {
4788 (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
4789 ret = El._flyweights[named];
4795 * Retrieves Ext.Element objects.
4796 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4797 * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4798 * its ID, use {@link Ext.ComponentMgr#get}.</p>
4799 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4800 * object was recreated with the same id via AJAX or DOM.</p>
4801 * Shorthand of {@link Ext.Element#get}
4802 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4803 * @return {Element} The Element object (or null if no matching element was found)
4810 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4811 * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4812 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4813 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4814 * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4815 * @param {String/HTMLElement} el The dom node or id
4816 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4817 * (e.g. internally Ext uses "_global")
4818 * @return {Element} The shared Element object (or null if no matching element was found)
4824 // speedy lookup for elements never to box adjust
4825 var noBoxAdjust = Ext.isStrict ? {
4828 input:1, select:1, textarea:1
4830 if(Ext.isIE || Ext.isGecko){
4831 noBoxAdjust['button'] = 1;
4836 * @class Ext.Element
4838 Ext.Element.addMethods({
4840 * Stops the specified event(s) from bubbling and optionally prevents the default action
4841 * @param {String/Array} eventName an event / array of events to stop from bubbling
4842 * @param {Boolean} preventDefault (optional) true to prevent the default action too
4843 * @return {Ext.Element} this
4845 swallowEvent : function(eventName, preventDefault){
4848 e.stopPropagation();
4853 if(Ext.isArray(eventName)){
4854 Ext.each(eventName, function(e) {
4859 me.on(eventName, fn);
4864 * Create an event handler on this element such that when the event fires and is handled by this element,
4865 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
4866 * @param {String} eventName The type of event to relay
4867 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
4868 * for firing the relayed event
4870 relayEvent : function(eventName, observable){
4871 this.on(eventName, function(e){
4872 observable.fireEvent(eventName, e);
4877 * Removes worthless text nodes
4878 * @param {Boolean} forceReclean (optional) By default the element
4879 * keeps track if it has been cleaned already so
4880 * you can call this over and over. However, if you update the element and
4881 * need to force a reclean, you can pass true.
4883 clean : function(forceReclean){
4889 if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){
4894 var nx = n.nextSibling;
4895 if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){
4902 Ext.Element.data(dom, 'isCleaned', true);
4907 * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
4908 * parameter as {@link Ext.Updater#update}
4909 * @return {Ext.Element} this
4912 var um = this.getUpdater();
4913 um.update.apply(um, arguments);
4918 * Gets this element's {@link Ext.Updater Updater}
4919 * @return {Ext.Updater} The Updater
4921 getUpdater : function(){
4922 return this.updateManager || (this.updateManager = new Ext.Updater(this));
4926 * Update the innerHTML of this element, optionally searching for and processing scripts
4927 * @param {String} html The new HTML
4928 * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
4929 * @param {Function} callback (optional) For async script loading you can be notified when the update completes
4930 * @return {Ext.Element} this
4932 update : function(html, loadScripts, callback){
4938 if(loadScripts !== true){
4939 this.dom.innerHTML = html;
4940 if(typeof callback == 'function'){
4949 html += '<span id="' + id + '"></span>';
4951 Ext.lib.Event.onAvailable(id, function(){
4953 hd = DOC.getElementsByTagName("head")[0],
4954 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
4955 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
4956 typeRe = /\stype=([\'\"])(.*?)\1/i,
4964 while((match = re.exec(html))){
4966 srcMatch = attrs ? attrs.match(srcRe) : false;
4967 if(srcMatch && srcMatch[2]){
4968 s = DOC.createElement("script");
4969 s.src = srcMatch[2];
4970 typeMatch = attrs.match(typeRe);
4971 if(typeMatch && typeMatch[2]){
4972 s.type = typeMatch[2];
4975 }else if(match[2] && match[2].length > 0){
4976 if(window.execScript) {
4977 window.execScript(match[2]);
4979 window.eval(match[2]);
4983 el = DOC.getElementById(id);
4984 if(el){Ext.removeNode(el);}
4985 if(typeof callback == 'function'){
4989 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
4993 // inherit docs, overridden so we can add removeAnchor
4994 removeAllListeners : function(){
4995 this.removeAnchor();
4996 Ext.EventManager.removeAll(this.dom);
5001 * Creates a proxy element of this element
5002 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
5003 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
5004 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
5005 * @return {Ext.Element} The new proxy element
5007 createProxy : function(config, renderTo, matchBox){
5008 config = (typeof config == 'object') ? config : {tag : "div", cls: config};
5011 proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
5012 Ext.DomHelper.insertBefore(me.dom, config, true);
5014 if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded
5015 proxy.setBox(me.getBox());
5021 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
5023 * @class Ext.Element
5025 Ext.Element.addMethods({
5027 * Gets the x,y coordinates specified by the anchor position on the element.
5028 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo}
5029 * for details on supported anchor positions.
5030 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
5031 * of page coordinates
5032 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
5033 * {width: (target width), height: (target height)} (defaults to the element's current size)
5034 * @return {Array} [x, y] An array containing the element's x and y coordinates
5036 getAnchorXY : function(anchor, local, s){
5037 //Passing a different size is useful for pre-calculating anchors,
5038 //especially for anchored animations that change the el size.
5039 anchor = (anchor || "tl").toLowerCase();
5043 vp = me.dom == document.body || me.dom == document,
5044 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
5045 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),
5049 scroll = me.getScroll(),
5050 extraX = vp ? scroll.left : !local ? o[0] : 0,
5051 extraY = vp ? scroll.top : !local ? o[1] : 0,
5053 c : [r(w * 0.5), r(h * 0.5)],
5054 t : [r(w * 0.5), 0],
5055 l : [0, r(h * 0.5)],
5056 r : [w, r(h * 0.5)],
5057 b : [r(w * 0.5), h],
5065 return [xy[0] + extraX, xy[1] + extraY];
5069 * Anchors an element to another element and realigns it when the window is resized.
5070 * @param {Mixed} element The element to align to.
5071 * @param {String} position The position to align to.
5072 * @param {Array} offsets (optional) Offset the positioning by [x, y]
5073 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
5074 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
5075 * is a number, it is used as the buffer delay (defaults to 50ms).
5076 * @param {Function} callback The function to call after the animation finishes
5077 * @return {Ext.Element} this
5079 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
5082 scroll = !Ext.isEmpty(monitorScroll),
5083 action = function(){
5084 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
5085 Ext.callback(callback, Ext.fly(dom));
5087 anchor = this.getAnchor();
5089 // previous listener anchor, remove it
5090 this.removeAnchor();
5096 Ext.EventManager.onWindowResize(action, null);
5099 Ext.EventManager.on(window, 'scroll', action, null,
5100 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
5102 action.call(me); // align immediately
5107 * Remove any anchor to this element. See {@link #anchorTo}.
5108 * @return {Ext.Element} this
5110 removeAnchor : function(){
5112 anchor = this.getAnchor();
5114 if(anchor && anchor.fn){
5115 Ext.EventManager.removeResizeListener(anchor.fn);
5117 Ext.EventManager.un(window, 'scroll', anchor.fn);
5125 getAnchor : function(){
5126 var data = Ext.Element.data,
5131 var anchor = data(dom, '_anchor');
5134 anchor = data(dom, '_anchor', {});
5140 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
5141 * supported position values.
5142 * @param {Mixed} element The element to align to.
5143 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5144 * @param {Array} offsets (optional) Offset the positioning by [x, y]
5145 * @return {Array} [x, y]
5147 getAlignToXY : function(el, p, o){
5151 throw "Element.alignToXY with an element that doesn't exist";
5155 p = (!p || p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
5163 //constrain the aligned el to viewport if necessary
5167 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
5168 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
5176 docElement = doc.documentElement,
5178 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
5179 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
5180 c = false, //constrain to viewport
5183 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
5186 throw "Element.alignTo with an invalid alignment " + p;
5193 //Subtract the aligned el's internal xy from the target's offset xy
5194 //plus custom offset to get the aligned el's new offset xy
5195 a1 = me.getAnchorXY(p1, true);
5196 a2 = el.getAnchorXY(p2, false);
5198 x = a2[0] - a1[0] + o[0];
5199 y = a2[1] - a1[1] + o[1];
5205 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
5206 //perpendicular to the vp border, allow the aligned el to slide on that border,
5207 //otherwise swap the aligned el to the opposite border of the target.
5209 p1x = p1.charAt(p1.length-1);
5211 p2x = p2.charAt(p2.length-1);
5212 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
5213 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
5216 if (x + w > dw + scrollX) {
5217 x = swapX ? r.left-w : dw+scrollX-w;
5220 x = swapX ? r.right : scrollX;
5222 if (y + h > dh + scrollY) {
5223 y = swapY ? r.top-h : dh+scrollY-h;
5226 y = swapY ? r.bottom : scrollY;
5233 * Aligns this element with another element relative to the specified anchor points. If the other element is the
5234 * document it aligns it to the viewport.
5235 * The position parameter is optional, and can be specified in any one of the following formats:
5237 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
5238 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
5239 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
5240 * deprecated in favor of the newer two anchor syntax below</i>.</li>
5241 * <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
5242 * element's anchor point, and the second value is used as the target's anchor point.</li>
5244 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
5245 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
5246 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
5247 * that specified in order to enforce the viewport constraints.
5248 * Following are all of the supported anchor positions:
5251 ----- -----------------------------
5252 tl The top left corner (default)
5253 t The center of the top edge
5254 tr The top right corner
5255 l The center of the left edge
5256 c In the center of the element
5257 r The center of the right edge
5258 bl The bottom left corner
5259 b The center of the bottom edge
5260 br The bottom right corner
5264 // align el to other-el using the default positioning ("tl-bl", non-constrained)
5265 el.alignTo("other-el");
5267 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
5268 el.alignTo("other-el", "tr?");
5270 // align the bottom right corner of el with the center left edge of other-el
5271 el.alignTo("other-el", "br-l?");
5273 // align the center of el with the bottom left corner of other-el and
5274 // adjust the x position by -6 pixels (and the y position by 0)
5275 el.alignTo("other-el", "c-bl", [-6, 0]);
5277 * @param {Mixed} element The element to align to.
5278 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5279 * @param {Array} offsets (optional) Offset the positioning by [x, y]
5280 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5281 * @return {Ext.Element} this
5283 alignTo : function(element, position, offsets, animate){
5285 return me.setXY(me.getAlignToXY(element, position, offsets),
5286 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
5289 // private ==> used outside of core
5290 adjustForConstraints : function(xy, parent, offsets){
5291 return this.getConstrainToXY(parent || document, false, offsets, xy) || xy;
5294 // private ==> used outside of core
5295 getConstrainToXY : function(el, local, offsets, proposedXY){
5296 var os = {top:0, left:0, bottom:0, right: 0};
5298 return function(el, local, offsets, proposedXY){
5300 offsets = offsets ? Ext.applyIf(offsets, os) : os;
5302 var vw, vh, vx = 0, vy = 0;
5303 if(el.dom == document.body || el.dom == document){
5304 vw =Ext.lib.Dom.getViewWidth();
5305 vh = Ext.lib.Dom.getViewHeight();
5307 vw = el.dom.clientWidth;
5308 vh = el.dom.clientHeight;
5310 var vxy = el.getXY();
5316 var s = el.getScroll();
5318 vx += offsets.left + s.left;
5319 vy += offsets.top + s.top;
5321 vw -= offsets.right;
5322 vh -= offsets.bottom;
5327 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
5328 var x = xy[0], y = xy[1];
5329 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
5331 // only move it if it needs it
5334 // first validate right/bottom
5343 // then make sure top/left isn't negative
5352 return moved ? [x, y] : false;
5358 // el = Ext.get(el);
5359 // offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
5363 // s = el.getScroll(),
5364 // vxy = el.getXY(),
5365 // vx = offsets.left + s.left,
5366 // vy = offsets.top + s.top,
5367 // vw = -offsets.right,
5368 // vh = -offsets.bottom,
5371 // xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
5374 // w = me.dom.offsetWidth, h = me.dom.offsetHeight,
5375 // moved = false; // only move it if it needs it
5378 // if(el.dom == doc.body || el.dom == doc){
5379 // vw += Ext.lib.Dom.getViewWidth();
5380 // vh += Ext.lib.Dom.getViewHeight();
5382 // vw += el.dom.clientWidth;
5383 // vh += el.dom.clientHeight;
5390 // // first validate right/bottom
5391 // if(x + w > vx + vw){
5395 // if(y + h > vy + vh){
5399 // // then make sure top/left isn't negative
5408 // return moved ? [x, y] : false;
5412 * Calculates the x, y to center this element on the screen
5413 * @return {Array} The x, y values [x, y]
5415 getCenterXY : function(){
5416 return this.getAlignToXY(document, 'c-c');
5420 * Centers the Element in either the viewport, or another Element.
5421 * @param {Mixed} centerIn (optional) The element in which to center the element.
5423 center : function(centerIn){
5424 return this.alignTo(centerIn || document, 'c-c');
5428 * @class Ext.Element
5430 Ext.Element.addMethods(function(){
5431 var PARENTNODE = 'parentNode',
5432 NEXTSIBLING = 'nextSibling',
5433 PREVIOUSSIBLING = 'previousSibling',
5439 * 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)
5440 * @param {String} selector The simple selector to test
5441 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
5442 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5443 * @return {HTMLElement} The matching DOM node (or null if no match was found)
5445 findParent : function(simpleSelector, maxDepth, returnEl){
5450 if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {
5453 maxDepth = maxDepth || 50;
5454 if (isNaN(maxDepth)) {
5455 stopEl = Ext.getDom(maxDepth);
5456 maxDepth = Number.MAX_VALUE;
5458 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
5459 if(DQ.is(p, simpleSelector)){
5460 return returnEl ? GET(p) : p;
5469 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
5470 * @param {String} selector The simple selector to test
5471 * @param {Number/Mixed} maxDepth (optional) The max depth to
5472 search as a number or element (defaults to 10 || document.body)
5473 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5474 * @return {HTMLElement} The matching DOM node (or null if no match was found)
5476 findParentNode : function(simpleSelector, maxDepth, returnEl){
5477 var p = Ext.fly(this.dom.parentNode, '_internal');
5478 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
5482 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
5483 * This is a shortcut for findParentNode() that always returns an Ext.Element.
5484 * @param {String} selector The simple selector to test
5485 * @param {Number/Mixed} maxDepth (optional) The max depth to
5486 search as a number or element (defaults to 10 || document.body)
5487 * @return {Ext.Element} The matching DOM node (or null if no match was found)
5489 up : function(simpleSelector, maxDepth){
5490 return this.findParentNode(simpleSelector, maxDepth, true);
5494 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5495 * @param {String} selector The CSS selector
5496 * @return {CompositeElement/CompositeElementLite} The composite element
5498 select : function(selector){
5499 return Ext.Element.select(selector, this.dom);
5503 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
5504 * @param {String} selector The CSS selector
5505 * @return {Array} An array of the matched nodes
5507 query : function(selector){
5508 return DQ.select(selector, this.dom);
5512 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
5513 * @param {String} selector The CSS selector
5514 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5515 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5517 child : function(selector, returnDom){
5518 var n = DQ.selectNode(selector, this.dom);
5519 return returnDom ? n : GET(n);
5523 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
5524 * @param {String} selector The CSS selector
5525 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5526 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5528 down : function(selector, returnDom){
5529 var n = DQ.selectNode(" > " + selector, this.dom);
5530 return returnDom ? n : GET(n);
5534 * Gets the parent node for this element, optionally chaining up trying to match a selector
5535 * @param {String} selector (optional) Find a parent node that matches the passed simple selector
5536 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5537 * @return {Ext.Element/HTMLElement} The parent node or null
5539 parent : function(selector, returnDom){
5540 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);
5544 * Gets the next sibling, skipping text nodes
5545 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5546 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5547 * @return {Ext.Element/HTMLElement} The next sibling or null
5549 next : function(selector, returnDom){
5550 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);
5554 * Gets the previous sibling, skipping text nodes
5555 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5556 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5557 * @return {Ext.Element/HTMLElement} The previous sibling or null
5559 prev : function(selector, returnDom){
5560 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);
5565 * Gets the first child, skipping text nodes
5566 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5567 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5568 * @return {Ext.Element/HTMLElement} The first child or null
5570 first : function(selector, returnDom){
5571 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);
5575 * Gets the last child, skipping text nodes
5576 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5577 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5578 * @return {Ext.Element/HTMLElement} The last child or null
5580 last : function(selector, returnDom){
5581 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);
5584 matchNode : function(dir, start, selector, returnDom){
5585 var n = this.dom[start];
5587 if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){
5588 return !returnDom ? GET(n) : n;
5596 * @class Ext.Element
5598 Ext.Element.addMethods({
5600 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5601 * @param {String} selector The CSS selector
5602 * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)
5603 * @return {CompositeElement/CompositeElementLite} The composite element
5605 select : function(selector, unique){
5606 return Ext.Element.select(selector, unique, this.dom);
5609 * @class Ext.Element
5611 Ext.Element.addMethods(
5613 var GETDOM = Ext.getDom,
5619 * Appends the passed element(s) to this element
5620 * @param {String/HTMLElement/Array/Element/CompositeElement} el
5621 * @return {Ext.Element} this
5623 appendChild: function(el){
5624 return GET(el).appendTo(this);
5628 * Appends this element to the passed element
5629 * @param {Mixed} el The new parent element
5630 * @return {Ext.Element} this
5632 appendTo: function(el){
5633 GETDOM(el).appendChild(this.dom);
5638 * Inserts this element before the passed element in the DOM
5639 * @param {Mixed} el The element before which this element will be inserted
5640 * @return {Ext.Element} this
5642 insertBefore: function(el){
5643 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);
5648 * Inserts this element after the passed element in the DOM
5649 * @param {Mixed} el The element to insert after
5650 * @return {Ext.Element} this
5652 insertAfter: function(el){
5653 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);
5658 * Inserts (or creates) an element (or DomHelper config) as the first child of this element
5659 * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
5660 * @return {Ext.Element} The new child
5662 insertFirst: function(el, returnDom){
5664 if(el.nodeType || el.dom || typeof el == 'string'){ // element
5666 this.dom.insertBefore(el, this.dom.firstChild);
5667 return !returnDom ? GET(el) : el;
5669 return this.createChild(el, this.dom.firstChild, returnDom);
5674 * Replaces the passed element with this element
5675 * @param {Mixed} el The element to replace
5676 * @return {Ext.Element} this
5678 replace: function(el){
5680 this.insertBefore(el);
5686 * Replaces this element with the passed element
5687 * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
5688 * @return {Ext.Element} this
5690 replaceWith: function(el){
5693 if(el.nodeType || el.dom || typeof el == 'string'){
5695 me.dom.parentNode.insertBefore(el, me.dom);
5697 el = DH.insertBefore(me.dom, el);
5700 delete Ext.elCache[me.id];
5701 Ext.removeNode(me.dom);
5702 me.id = Ext.id(me.dom = el);
5703 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);
5708 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
5709 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
5710 * automatically generated with the specified attributes.
5711 * @param {HTMLElement} insertBefore (optional) a child element of this element
5712 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
5713 * @return {Ext.Element} The new child element
5715 createChild: function(config, insertBefore, returnDom){
5716 config = config || {tag:'div'};
5717 return insertBefore ?
5718 DH.insertBefore(insertBefore, config, returnDom !== true) :
5719 DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config, returnDom !== true);
5723 * Creates and wraps this element with another element
5724 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
5725 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5726 * @return {HTMLElement/Element} The newly created wrapper element
5728 wrap: function(config, returnDom){
5729 var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
5730 newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
5735 * Inserts an html fragment into this element
5736 * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
5737 * @param {String} html The HTML fragment
5738 * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
5739 * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
5741 insertHtml : function(where, html, returnEl){
5742 var el = DH.insertHtml(where, this.dom, html);
5743 return returnEl ? Ext.get(el) : el;
5747 * @class Ext.Element
5749 Ext.apply(Ext.Element.prototype, function() {
5750 var GETDOM = Ext.getDom,
5756 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
5757 * @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.
5758 * @param {String} where (optional) 'before' or 'after' defaults to before
5759 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5760 * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
5762 insertSibling: function(el, where, returnDom){
5765 isAfter = (where || 'before').toLowerCase() == 'after',
5768 if(Ext.isArray(el)){
5770 Ext.each(el, function(e) {
5771 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
5781 if(el.nodeType || el.dom){
5782 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);
5787 if (isAfter && !me.dom.nextSibling) {
5788 rt = DH.append(me.dom.parentNode, el, !returnDom);
5790 rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
5797 * @class Ext.Element
5799 Ext.Element.addMethods(function(){
5800 // local style camelizing for speed
5802 camelRe = /(-[a-z])/gi,
5804 view = document.defaultView,
5805 propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
5806 opacityRe = /alpha\(opacity=(.*)\)/i,
5807 trimRe = /^\s+|\s+$/g,
5811 PADDING = "padding",
5821 ISCLIPPED = 'isClipped',
5822 OVERFLOW = 'overflow',
5823 OVERFLOWX = 'overflow-x',
5824 OVERFLOWY = 'overflow-y',
5825 ORIGINALCLIP = 'originalClip',
5826 // special markup used throughout Ext when box wrapping elements
5827 borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
5828 paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
5829 margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
5830 data = Ext.Element.data;
5834 function camelFn(m, a) {
5835 return a.charAt(1).toUpperCase();
5838 function chkCache(prop) {
5839 return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
5843 // private ==> used by Fx
5844 adjustWidth : function(width) {
5846 var isNum = (typeof width == "number");
5847 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5848 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
5850 return (isNum && width < 0) ? 0 : width;
5853 // private ==> used by Fx
5854 adjustHeight : function(height) {
5856 var isNum = (typeof height == "number");
5857 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5858 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
5860 return (isNum && height < 0) ? 0 : height;
5865 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
5866 * @param {String/Array} className The CSS class to add, or an array of classes
5867 * @return {Ext.Element} this
5869 addClass : function(className){
5875 // Separate case is for speed
5876 if (!Ext.isArray(className)) {
5877 if (typeof className == 'string' && !this.hasClass(className)) {
5878 me.dom.className += " " + className;
5882 for (i = 0, len = className.length; i < len; i++) {
5884 if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
5889 me.dom.className += " " + cls.join(" ");
5896 * Removes one or more CSS classes from the element.
5897 * @param {String/Array} className The CSS class to remove, or an array of classes
5898 * @return {Ext.Element} this
5900 removeClass : function(className){
5907 if (!Ext.isArray(className)){
5908 className = [className];
5910 if (me.dom && me.dom.className) {
5911 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
5912 for (i = 0, len = className.length; i < len; i++) {
5914 if (typeof cls == 'string') {
5915 cls = cls.replace(trimRe, '');
5916 idx = elClasses.indexOf(cls);
5918 elClasses.splice(idx, 1);
5922 me.dom.className = elClasses.join(" ");
5928 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
5929 * @param {String/Array} className The CSS class to add, or an array of classes
5930 * @return {Ext.Element} this
5932 radioClass : function(className){
5933 var cn = this.dom.parentNode.childNodes,
5937 className = Ext.isArray(className) ? className : [className];
5938 for (i = 0, len = cn.length; i < len; i++) {
5940 if (v && v.nodeType == 1) {
5941 Ext.fly(v, '_internal').removeClass(className);
5944 return this.addClass(className);
5948 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
5949 * @param {String} className The CSS class to toggle
5950 * @return {Ext.Element} this
5952 toggleClass : function(className){
5953 return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
5957 * Checks if the specified CSS class exists on this element's DOM node.
5958 * @param {String} className The CSS class to check for
5959 * @return {Boolean} True if the class exists, else false
5961 hasClass : function(className){
5962 return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
5966 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
5967 * @param {String} oldClassName The CSS class to replace
5968 * @param {String} newClassName The replacement CSS class
5969 * @return {Ext.Element} this
5971 replaceClass : function(oldClassName, newClassName){
5972 return this.removeClass(oldClassName).addClass(newClassName);
5975 isStyle : function(style, val) {
5976 return this.getStyle(style) == val;
5980 * Normalizes currentStyle and computedStyle.
5981 * @param {String} property The style property whose value is returned.
5982 * @return {String} The current value of the style property for this element.
5984 getStyle : function(){
5985 return view && view.getComputedStyle ?
5998 prop = chkCache(prop);
5999 // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
6000 if(wk && /marginRight/.test(prop)){
6001 display = this.getStyle('display');
6002 el.style.display = 'inline-block';
6004 out = (v = el.style[prop]) ? v :
6005 (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
6007 // Webkit returns rgb values for transparent.
6009 if(out == 'rgba(0, 0, 0, 0)'){
6010 out = 'transparent';
6012 el.style.display = display;
6022 if(el == document) return null;
6023 if (prop == 'opacity') {
6024 if (el.style.filter.match) {
6025 if(m = el.style.filter.match(opacityRe)){
6026 var fv = parseFloat(m[1]);
6028 return fv ? fv / 100 : 0;
6034 prop = chkCache(prop);
6035 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
6040 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
6041 * are convert to standard 6 digit hex color.
6042 * @param {String} attr The css attribute
6043 * @param {String} defaultValue The default value to use when a valid color isn't found
6044 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
6047 getColor : function(attr, defaultValue, prefix){
6048 var v = this.getStyle(attr),
6049 color = (typeof prefix != 'undefined') ? prefix : '#',
6052 if(!v || /transparent|inherit/.test(v)){
6053 return defaultValue;
6056 Ext.each(v.slice(4, v.length -1).split(','), function(s){
6057 h = parseInt(s, 10);
6058 color += (h < 16 ? '0' : '') + h.toString(16);
6061 v = v.replace('#', '');
6062 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
6064 return(color.length > 5 ? color.toLowerCase() : defaultValue);
6068 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
6069 * @param {String/Object} property The style property to be set, or an object of multiple styles.
6070 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
6071 * @return {Ext.Element} this
6073 setStyle : function(prop, value){
6077 if (typeof prop != 'object') {
6082 for (style in prop) {
6083 value = prop[style];
6084 style == 'opacity' ?
6085 this.setOpacity(value) :
6086 this.dom.style[chkCache(style)] = value;
6092 * Set the opacity of the element
6093 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
6094 * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
6095 * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
6096 * @return {Ext.Element} this
6098 setOpacity : function(opacity, animate){
6102 if(!animate || !me.anim){
6104 var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
6105 val = s.filter.replace(opacityRe, '').replace(trimRe, '');
6108 s.filter = val + (val.length > 0 ? ' ' : '') + opac;
6110 s.opacity = opacity;
6113 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
6119 * Clears any opacity settings from this element. Required in some cases for IE.
6120 * @return {Ext.Element} this
6122 clearOpacity : function(){
6123 var style = this.dom.style;
6125 if(!Ext.isEmpty(style.filter)){
6126 style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
6129 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
6135 * Returns the offset height of the element
6136 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
6137 * @return {Number} The element's height
6139 getHeight : function(contentHeight){
6142 hidden = Ext.isIE && me.isStyle('display', 'none'),
6143 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
6145 h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
6146 return h < 0 ? 0 : h;
6150 * Returns the offset width of the element
6151 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
6152 * @return {Number} The element's width
6154 getWidth : function(contentWidth){
6157 hidden = Ext.isIE && me.isStyle('display', 'none'),
6158 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
6159 w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
6160 return w < 0 ? 0 : w;
6164 * Set the width of this Element.
6165 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6166 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6167 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6169 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6170 * @return {Ext.Element} this
6172 setWidth : function(width, animate){
6174 width = me.adjustWidth(width);
6175 !animate || !me.anim ?
6176 me.dom.style.width = me.addUnits(width) :
6177 me.anim({width : {to : width}}, me.preanim(arguments, 1));
6182 * Set the height of this Element.
6184 // change the height to 200px and animate with default configuration
6185 Ext.fly('elementId').setHeight(200, true);
6187 // change the height to 150px and animate with a custom configuration
6188 Ext.fly('elId').setHeight(150, {
6189 duration : .5, // animation will have a duration of .5 seconds
6190 // will change the content to "finished"
6191 callback: function(){ this.{@link #update}("finished"); }
6194 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6195 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
6196 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6198 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6199 * @return {Ext.Element} this
6201 setHeight : function(height, animate){
6203 height = me.adjustHeight(height);
6204 !animate || !me.anim ?
6205 me.dom.style.height = me.addUnits(height) :
6206 me.anim({height : {to : height}}, me.preanim(arguments, 1));
6211 * Gets the width of the border(s) for the specified side(s)
6212 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6213 * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
6214 * @return {Number} The width of the sides passed added together
6216 getBorderWidth : function(side){
6217 return this.addStyles(side, borders);
6221 * Gets the width of the padding(s) for the specified side(s)
6222 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6223 * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
6224 * @return {Number} The padding of the sides passed added together
6226 getPadding : function(side){
6227 return this.addStyles(side, paddings);
6231 * Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
6232 * @return {Ext.Element} this
6238 if(!data(dom, ISCLIPPED)){
6239 data(dom, ISCLIPPED, true);
6240 data(dom, ORIGINALCLIP, {
6241 o: me.getStyle(OVERFLOW),
6242 x: me.getStyle(OVERFLOWX),
6243 y: me.getStyle(OVERFLOWY)
6245 me.setStyle(OVERFLOW, HIDDEN);
6246 me.setStyle(OVERFLOWX, HIDDEN);
6247 me.setStyle(OVERFLOWY, HIDDEN);
6253 * Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
6254 * @return {Ext.Element} this
6256 unclip : function(){
6260 if(data(dom, ISCLIPPED)){
6261 data(dom, ISCLIPPED, false);
6262 var o = data(dom, ORIGINALCLIP);
6264 me.setStyle(OVERFLOW, o.o);
6267 me.setStyle(OVERFLOWX, o.x);
6270 me.setStyle(OVERFLOWY, o.y);
6277 addStyles : function(sides, styles){
6279 sidesArr = sides.match(wordsRe),
6283 len = sidesArr.length;
6284 for (i = 0; i < len; i++) {
6286 size = side && parseInt(this.getStyle(styles[side]), 10);
6288 ttlSize += MATH.abs(size);
6299 * @class Ext.Element
6302 // special markup used throughout Ext when box wrapping elements
6303 Ext.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>';
6305 Ext.Element.addMethods(function(){
6306 var INTERNAL = "_internal",
6307 pxMatch = /(\d+\.?\d+)px/;
6310 * More flexible version of {@link #setStyle} for setting style properties.
6311 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
6312 * a function which returns such a specification.
6313 * @return {Ext.Element} this
6315 applyStyles : function(style){
6316 Ext.DomHelper.applyStyles(this.dom, style);
6321 * Returns an object with properties matching the styles requested.
6322 * For example, el.getStyles('color', 'font-size', 'width') might return
6323 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
6324 * @param {String} style1 A style name
6325 * @param {String} style2 A style name
6326 * @param {String} etc.
6327 * @return {Object} The style object
6329 getStyles : function(){
6331 Ext.each(arguments, function(v) {
6332 ret[v] = this.getStyle(v);
6338 // private ==> used by ext full
6339 setOverflow : function(v){
6341 if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
6342 dom.style.overflow = 'hidden';
6343 (function(){dom.style.overflow = 'auto';}).defer(1);
6345 dom.style.overflow = v;
6350 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
6351 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
6352 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
6353 * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}). The markup
6354 * is of this form:</p>
6356 Ext.Element.boxMarkup =
6357 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
6358 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
6359 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
6361 * <p>Example usage:</p>
6364 Ext.get("foo").boxWrap();
6366 // You can also add a custom class and use CSS inheritance rules to customize the box look.
6367 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
6368 // for how to create a custom box wrap style.
6369 Ext.get("foo").boxWrap().addClass("x-box-blue");
6371 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
6372 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
6373 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
6374 * also supply all of the necessary rules.
6375 * @return {Ext.Element} The outermost wrapping element of the created box structure.
6377 boxWrap : function(cls){
6378 cls = cls || 'x-box';
6379 var el = Ext.get(this.insertHtml("beforeBegin", "<div class='" + cls + "'>" + String.format(Ext.Element.boxMarkup, cls) + "</div>")); //String.format('<div class="{0}">'+Ext.Element.boxMarkup+'</div>', cls)));
6380 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
6385 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
6386 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6387 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6388 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6389 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
6391 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6392 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6393 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6395 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6396 * @return {Ext.Element} this
6398 setSize : function(width, height, animate){
6400 if(typeof width == 'object'){ // in case of object from getSize()
6401 height = width.height;
6402 width = width.width;
6404 width = me.adjustWidth(width);
6405 height = me.adjustHeight(height);
6406 if(!animate || !me.anim){
6407 me.dom.style.width = me.addUnits(width);
6408 me.dom.style.height = me.addUnits(height);
6410 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
6416 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
6417 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
6418 * if a height has not been set using CSS.
6421 getComputedHeight : function(){
6423 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
6425 h = parseFloat(me.getStyle('height')) || 0;
6426 if(!me.isBorderBox()){
6427 h += me.getFrameWidth('tb');
6434 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
6435 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
6436 * if a width has not been set using CSS.
6439 getComputedWidth : function(){
6440 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
6442 w = parseFloat(this.getStyle('width')) || 0;
6443 if(!this.isBorderBox()){
6444 w += this.getFrameWidth('lr');
6451 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
6452 for more information about the sides.
6453 * @param {String} sides
6456 getFrameWidth : function(sides, onlyContentBox){
6457 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
6461 * Sets up event handlers to add and remove a css class when the mouse is over this element
6462 * @param {String} className
6463 * @return {Ext.Element} this
6465 addClassOnOver : function(className){
6468 Ext.fly(this, INTERNAL).addClass(className);
6471 Ext.fly(this, INTERNAL).removeClass(className);
6478 * Sets up event handlers to add and remove a css class when this element has the focus
6479 * @param {String} className
6480 * @return {Ext.Element} this
6482 addClassOnFocus : function(className){
6483 this.on("focus", function(){
6484 Ext.fly(this, INTERNAL).addClass(className);
6486 this.on("blur", function(){
6487 Ext.fly(this, INTERNAL).removeClass(className);
6493 * 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)
6494 * @param {String} className
6495 * @return {Ext.Element} this
6497 addClassOnClick : function(className){
6499 this.on("mousedown", function(){
6500 Ext.fly(dom, INTERNAL).addClass(className);
6501 var d = Ext.getDoc(),
6503 Ext.fly(dom, INTERNAL).removeClass(className);
6504 d.removeListener("mouseup", fn);
6506 d.on("mouseup", fn);
6512 * <p>Returns the dimensions of the element available to lay content out in.<p>
6513 * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
6514 * example:<pre><code>
6515 var vpSize = Ext.getBody().getViewSize();
6517 // all Windows created afterwards will have a default value of 90% height and 95% width
6518 Ext.Window.override({
6519 width: vpSize.width * 0.9,
6520 height: vpSize.height * 0.95
6522 // To handle window resizing you would have to hook onto onWindowResize.
6525 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
6526 * To obtain the size including scrollbars, use getStyleSize
6528 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6531 getViewSize : function(){
6534 isDoc = (d == doc || d == doc.body);
6536 // If the body, use Ext.lib.Dom
6538 var extdom = Ext.lib.Dom;
6540 width : extdom.getViewWidth(),
6541 height : extdom.getViewHeight()
6544 // Else use clientHeight/clientWidth
6547 width : d.clientWidth,
6548 height : d.clientHeight
6554 * <p>Returns the dimensions of the element available to lay content out in.<p>
6556 * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
6557 * To obtain the size excluding scrollbars, use getViewSize
6559 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6562 getStyleSize : function(){
6567 isDoc = (d == doc || d == doc.body),
6570 // If the body, use Ext.lib.Dom
6572 var extdom = Ext.lib.Dom;
6574 width : extdom.getViewWidth(),
6575 height : extdom.getViewHeight()
6578 // Use Styles if they are set
6579 if(s.width && s.width != 'auto'){
6580 w = parseFloat(s.width);
6581 if(me.isBorderBox()){
6582 w -= me.getFrameWidth('lr');
6585 // Use Styles if they are set
6586 if(s.height && s.height != 'auto'){
6587 h = parseFloat(s.height);
6588 if(me.isBorderBox()){
6589 h -= me.getFrameWidth('tb');
6592 // Use getWidth/getHeight if style not set.
6593 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
6597 * Returns the size of the element.
6598 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
6599 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
6601 getSize : function(contentSize){
6602 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
6606 * Forces the browser to repaint this element
6607 * @return {Ext.Element} this
6609 repaint : function(){
6611 this.addClass("x-repaint");
6612 setTimeout(function(){
6613 Ext.fly(dom).removeClass("x-repaint");
6619 * Disables text selection for this element (normalized across browsers)
6620 * @return {Ext.Element} this
6622 unselectable : function(){
6623 this.dom.unselectable = "on";
6624 return this.swallowEvent("selectstart", true).
6625 applyStyles("-moz-user-select:none;-khtml-user-select:none;").
6626 addClass("x-unselectable");
6630 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
6631 * then it returns the calculated width of the sides (see getPadding)
6632 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
6633 * @return {Object/Number}
6635 getMargins : function(side){
6638 hash = {t:"top", l:"left", r:"right", b: "bottom"},
6642 for (key in me.margins){
6643 o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
6647 return me.addStyles.call(me, side, me.margins);
6653 * @class Ext.Element
6656 var D = Ext.lib.Dom,
6661 POSITION = "position",
6663 RELATIVE = "relative",
6667 Ext.Element.addMethods({
6669 * 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).
6670 * @return {Number} The X position of the element
6673 return D.getX(this.dom);
6677 * 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).
6678 * @return {Number} The Y position of the element
6681 return D.getY(this.dom);
6685 * 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).
6686 * @return {Array} The XY position of the element
6689 return D.getXY(this.dom);
6693 * 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.
6694 * @param {Mixed} element The element to get the offsets from.
6695 * @return {Array} The XY page offsets (e.g. [100, -200])
6697 getOffsetsTo : function(el){
6698 var o = this.getXY(),
6699 e = Ext.fly(el, '_internal').getXY();
6700 return [o[0]-e[0],o[1]-e[1]];
6704 * 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).
6705 * @param {Number} The X position of the element
6706 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6707 * @return {Ext.Element} this
6709 setX : function(x, animate){
6710 return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));
6714 * 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).
6715 * @param {Number} The Y position of the element
6716 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6717 * @return {Ext.Element} this
6719 setY : function(y, animate){
6720 return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));
6724 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
6725 * @param {String} left The left CSS property value
6726 * @return {Ext.Element} this
6728 setLeft : function(left){
6729 this.setStyle(LEFT, this.addUnits(left));
6734 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
6735 * @param {String} top The top CSS property value
6736 * @return {Ext.Element} this
6738 setTop : function(top){
6739 this.setStyle(TOP, this.addUnits(top));
6744 * Sets the element's CSS right style.
6745 * @param {String} right The right CSS property value
6746 * @return {Ext.Element} this
6748 setRight : function(right){
6749 this.setStyle(RIGHT, this.addUnits(right));
6754 * Sets the element's CSS bottom style.
6755 * @param {String} bottom The bottom CSS property value
6756 * @return {Ext.Element} this
6758 setBottom : function(bottom){
6759 this.setStyle(BOTTOM, this.addUnits(bottom));
6764 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6765 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6766 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
6767 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6768 * @return {Ext.Element} this
6770 setXY : function(pos, animate){
6772 if(!animate || !me.anim){
6773 D.setXY(me.dom, pos);
6775 me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');
6781 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6782 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6783 * @param {Number} x X value for new position (coordinates are page-based)
6784 * @param {Number} y Y value for new position (coordinates are page-based)
6785 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6786 * @return {Ext.Element} this
6788 setLocation : function(x, y, animate){
6789 return this.setXY([x, y], this.animTest(arguments, animate, 2));
6793 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6794 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6795 * @param {Number} x X value for new position (coordinates are page-based)
6796 * @param {Number} y Y value for new position (coordinates are page-based)
6797 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6798 * @return {Ext.Element} this
6800 moveTo : function(x, y, animate){
6801 return this.setXY([x, y], this.animTest(arguments, animate, 2));
6805 * Gets the left X coordinate
6806 * @param {Boolean} local True to get the local css position instead of page coordinate
6809 getLeft : function(local){
6810 return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
6814 * Gets the right X coordinate of the element (element X position + element width)
6815 * @param {Boolean} local True to get the local css position instead of page coordinate
6818 getRight : function(local){
6820 return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
6824 * Gets the top Y coordinate
6825 * @param {Boolean} local True to get the local css position instead of page coordinate
6828 getTop : function(local) {
6829 return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
6833 * Gets the bottom Y coordinate of the element (element Y position + element height)
6834 * @param {Boolean} local True to get the local css position instead of page coordinate
6837 getBottom : function(local){
6839 return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
6843 * Initializes positioning on this element. If a desired position is not passed, it will make the
6844 * the element positioned relative IF it is not already positioned.
6845 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
6846 * @param {Number} zIndex (optional) The zIndex to apply
6847 * @param {Number} x (optional) Set the page X position
6848 * @param {Number} y (optional) Set the page Y position
6850 position : function(pos, zIndex, x, y){
6853 if(!pos && me.isStyle(POSITION, STATIC)){
6854 me.setStyle(POSITION, RELATIVE);
6856 me.setStyle(POSITION, pos);
6859 me.setStyle(ZINDEX, zIndex);
6861 if(x || y) me.setXY([x || false, y || false]);
6865 * Clear positioning back to the default when the document was loaded
6866 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
6867 * @return {Ext.Element} this
6869 clearPositioning : function(value){
6870 value = value || '';
6883 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
6884 * snapshot before performing an update and then restoring the element.
6887 getPositioning : function(){
6888 var l = this.getStyle(LEFT);
6889 var t = this.getStyle(TOP);
6891 "position" : this.getStyle(POSITION),
6893 "right" : l ? "" : this.getStyle(RIGHT),
6895 "bottom" : t ? "" : this.getStyle(BOTTOM),
6896 "z-index" : this.getStyle(ZINDEX)
6901 * Set positioning with an object returned by getPositioning().
6902 * @param {Object} posCfg
6903 * @return {Ext.Element} this
6905 setPositioning : function(pc){
6907 style = me.dom.style;
6911 if(pc.right == AUTO){
6914 if(pc.bottom == AUTO){
6922 * Translates the passed page coordinates into left/top css values for this element
6923 * @param {Number/Array} x The page x or an array containing [x, y]
6924 * @param {Number} y (optional) The page y, required if x is not an array
6925 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
6927 translatePoints : function(x, y){
6928 y = isNaN(x[1]) ? y : x[1];
6929 x = isNaN(x[0]) ? x : x[0];
6931 relative = me.isStyle(POSITION, RELATIVE),
6933 l = parseInt(me.getStyle(LEFT), 10),
6934 t = parseInt(me.getStyle(TOP), 10);
6936 l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
6937 t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);
6939 return {left: (x - o[0] + l), top: (y - o[1] + t)};
6942 animTest : function(args, animate, i) {
6943 return !!animate && this.preanim ? this.preanim(args, i) : false;
6947 * @class Ext.Element
6949 Ext.Element.addMethods({
6951 * 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.
6952 * @param {Object} box The box to fill {x, y, width, height}
6953 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
6954 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6955 * @return {Ext.Element} this
6957 setBox : function(box, adjust, animate){
6961 if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
6962 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
6963 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
6965 me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
6970 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
6971 * set another Element's size/location to match this element.
6972 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
6973 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
6974 * @return {Object} box An object in the format<pre><code>
6976 x: <Element's X position>,
6977 y: <Element's Y position>,
6978 width: <Element's width>,
6979 height: <Element's height>,
6980 bottom: <Element's lower bound>,
6981 right: <Element's rightmost bound>
6984 * The returned object may also be addressed as an Array where index 0 contains the X position
6985 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
6987 getBox : function(contentBox, local) {
6992 getBorderWidth = me.getBorderWidth,
6993 getPadding = me.getPadding,
7001 left = parseInt(me.getStyle("left"), 10) || 0;
7002 top = parseInt(me.getStyle("top"), 10) || 0;
7005 var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
7007 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
7009 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
7010 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
7011 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
7012 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
7013 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
7015 bx.right = bx.x + bx.width;
7016 bx.bottom = bx.y + bx.height;
7021 * Move this element relative to its current position.
7022 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
7023 * @param {Number} distance How far to move the element in pixels
7024 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7025 * @return {Ext.Element} this
7027 move : function(direction, distance, animate){
7032 left = [x - distance, y],
7033 right = [x + distance, y],
7034 top = [x, y - distance],
7035 bottom = [x, y + distance],
7049 direction = direction.toLowerCase();
7050 me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
7054 * Quick set left and top adding default units
7055 * @param {String} left The left CSS property value
7056 * @param {String} top The top CSS property value
7057 * @return {Ext.Element} this
7059 setLeftTop : function(left, top){
7061 style = me.dom.style;
7062 style.left = me.addUnits(left);
7063 style.top = me.addUnits(top);
7068 * Returns the region of the given element.
7069 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7070 * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
7072 getRegion : function(){
7073 return Ext.lib.Dom.getRegion(this.dom);
7077 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7078 * @param {Number} x X value for new position (coordinates are page-based)
7079 * @param {Number} y Y value for new position (coordinates are page-based)
7080 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
7081 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7082 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
7084 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
7085 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7086 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
7088 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7089 * @return {Ext.Element} this
7091 setBounds : function(x, y, width, height, animate){
7093 if (!animate || !me.anim) {
7094 me.setSize(width, height);
7095 me.setLocation(x, y);
7097 me.anim({points: {to: [x, y]},
7098 width: {to: me.adjustWidth(width)},
7099 height: {to: me.adjustHeight(height)}},
7100 me.preanim(arguments, 4),
7107 * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
7108 * @param {Ext.lib.Region} region The region to fill
7109 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7110 * @return {Ext.Element} this
7112 setRegion : function(region, animate) {
7113 return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
7116 * @class Ext.Element
7118 Ext.Element.addMethods({
7120 * Returns true if this element is scrollable.
7123 isScrollable : function(){
7125 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
7129 * 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().
7130 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7131 * @param {Number} value The new scroll value.
7132 * @return {Element} this
7134 scrollTo : function(side, value){
7135 this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;
7140 * Returns the current scroll position of the element.
7141 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
7143 getScroll : function(){
7147 docElement = doc.documentElement,
7152 if(d == doc || d == body){
7153 if(Ext.isIE && Ext.isStrict){
7154 l = docElement.scrollLeft;
7155 t = docElement.scrollTop;
7157 l = window.pageXOffset;
7158 t = window.pageYOffset;
7160 ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
7162 ret = {left: d.scrollLeft, top: d.scrollTop};
7167 * @class Ext.Element
7169 Ext.Element.addMethods({
7171 * 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().
7172 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7173 * @param {Number} value The new scroll value
7174 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7175 * @return {Element} this
7177 scrollTo : function(side, value, animate){
7178 var top = /top/i.test(side), //check if we're scrolling top or left
7182 if (!animate || !me.anim) {
7183 prop = 'scroll' + (top ? 'Top' : 'Left'), // just setting the value, so grab the direction
7186 prop = 'scroll' + (top ? 'Left' : 'Top'), // if scrolling top, we need to grab scrollLeft, if left, scrollTop
7187 me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}},
7188 me.preanim(arguments, 2), 'scroll');
7194 * Scrolls this element into view within the passed container.
7195 * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a
7196 * string (id), dom node, or Ext.Element.
7197 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7198 * @return {Ext.Element} this
7200 scrollIntoView : function(container, hscroll){
7201 var c = Ext.getDom(container) || Ext.getBody().dom,
7203 o = this.getOffsetsTo(c),
7204 l = o[0] + c.scrollLeft,
7205 t = o[1] + c.scrollTop,
7206 b = t + el.offsetHeight,
7207 r = l + el.offsetWidth,
7208 ch = c.clientHeight,
7209 ct = parseInt(c.scrollTop, 10),
7210 cl = parseInt(c.scrollLeft, 10),
7212 cr = cl + c.clientWidth;
7214 if (el.offsetHeight > ch || t < ct) {
7219 c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore
7221 if(hscroll !== false){
7222 if(el.offsetWidth > c.clientWidth || l < cl){
7225 c.scrollLeft = r - c.clientWidth;
7227 c.scrollLeft = c.scrollLeft;
7233 scrollChildIntoView : function(child, hscroll){
7234 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7238 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
7239 * within this element's scrollable range.
7240 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
7241 * @param {Number} distance How far to scroll the element in pixels
7242 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7243 * @return {Boolean} Returns true if a scroll was triggered or false if the element
7244 * was scrolled as far as it could go.
7246 scroll : function(direction, distance, animate){
7247 if(!this.isScrollable()){
7251 l = el.scrollLeft, t = el.scrollTop,
7252 w = el.scrollWidth, h = el.scrollHeight,
7253 cw = el.clientWidth, ch = el.clientHeight,
7254 scrolled = false, v,
7256 l: Math.min(l + distance, w-cw),
7257 r: v = Math.max(l - distance, 0),
7258 t: Math.max(t - distance, 0),
7259 b: Math.min(t + distance, h-ch)
7264 direction = direction.substr(0, 1);
7265 if((v = hash[direction]) > -1){
7267 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
7272 * @class Ext.Element
7275 * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
7279 Ext.Element.VISIBILITY = 1;
7281 * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
7285 Ext.Element.DISPLAY = 2;
7287 Ext.Element.addMethods(function(){
7288 var VISIBILITY = "visibility",
7289 DISPLAY = "display",
7291 OFFSETS = "offsets",
7293 ORIGINALDISPLAY = 'originalDisplay',
7294 VISMODE = 'visibilityMode',
7295 ELDISPLAY = Ext.Element.DISPLAY,
7296 data = Ext.Element.data,
7297 getDisplay = function(dom){
7298 var d = data(dom, ORIGINALDISPLAY);
7299 if(d === undefined){
7300 data(dom, ORIGINALDISPLAY, d = '');
7304 getVisMode = function(dom){
7305 var m = data(dom, VISMODE);
7306 if(m === undefined){
7307 data(dom, VISMODE, m = 1);
7314 * The element's default display mode (defaults to "")
7317 originalDisplay : "",
7321 * Sets the element's visibility mode. When setVisible() is called it
7322 * will use this to determine whether to set the visibility or the display property.
7323 * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
7324 * @return {Ext.Element} this
7326 setVisibilityMode : function(visMode){
7327 data(this.dom, VISMODE, visMode);
7332 * Perform custom animation on this element.
7333 * <div><ul class="mdetail-params">
7334 * <li><u>Animation Properties</u></li>
7336 * <p>The Animation Control Object enables gradual transitions for any member of an
7337 * element's style object that takes a numeric value including but not limited to
7338 * these properties:</p><div><ul class="mdetail-params">
7339 * <li><tt>bottom, top, left, right</tt></li>
7340 * <li><tt>height, width</tt></li>
7341 * <li><tt>margin, padding</tt></li>
7342 * <li><tt>borderWidth</tt></li>
7343 * <li><tt>opacity</tt></li>
7344 * <li><tt>fontSize</tt></li>
7345 * <li><tt>lineHeight</tt></li>
7349 * <li><u>Animation Property Attributes</u></li>
7351 * <p>Each Animation Property is a config object with optional properties:</p>
7352 * <div><ul class="mdetail-params">
7353 * <li><tt>by</tt>* : relative change - start at current value, change by this value</li>
7354 * <li><tt>from</tt> : ignore current value, start from this value</li>
7355 * <li><tt>to</tt>* : start at current value, go to this value</li>
7356 * <li><tt>unit</tt> : any allowable unit specification</li>
7357 * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>
7360 * <li><u>Animation Types</u></li>
7362 * <p>The supported animation types:</p><div><ul class="mdetail-params">
7363 * <li><tt>'run'</tt> : Default
7365 var el = Ext.get('complexEl');
7367 // animation control object
7369 borderWidth: {to: 3, from: 0},
7370 opacity: {to: .3, from: 1},
7371 height: {to: 50, from: el.getHeight()},
7372 width: {to: 300, from: el.getWidth()},
7373 top : {by: - 100, unit: 'px'},
7375 0.35, // animation duration
7377 'easeOut', // easing method
7378 'run' // animation type ('run','color','motion','scroll')
7382 * <li><tt>'color'</tt>
7383 * <p>Animates transition of background, text, or border colors.</p>
7386 // animation control object
7388 color: { to: '#06e' },
7389 backgroundColor: { to: '#e06' }
7391 0.35, // animation duration
7393 'easeOut', // easing method
7394 'color' // animation type ('run','color','motion','scroll')
7399 * <li><tt>'motion'</tt>
7400 * <p>Animates the motion of an element to/from specific points using optional bezier
7401 * way points during transit.</p>
7404 // animation control object
7406 borderWidth: {to: 3, from: 0},
7407 opacity: {to: .3, from: 1},
7408 height: {to: 50, from: el.getHeight()},
7409 width: {to: 300, from: el.getWidth()},
7410 top : {by: - 100, unit: 'px'},
7412 to: [50, 100], // go to this point
7413 control: [ // optional bezier way points
7419 3000, // animation duration (milliseconds!)
7421 'easeOut', // easing method
7422 'motion' // animation type ('run','color','motion','scroll')
7426 * <li><tt>'scroll'</tt>
7427 * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>
7430 // animation control object
7432 scroll: {to: [400, 300]}
7434 0.35, // animation duration
7436 'easeOut', // easing method
7437 'scroll' // animation type ('run','color','motion','scroll')
7445 * @param {Object} args The animation control args
7446 * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)
7447 * @param {Function} onComplete (optional) Function to call when animation completes
7448 * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)
7449 * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,
7450 * <tt>'motion'</tt>, or <tt>'scroll'</tt>
7451 * @return {Ext.Element} this
7453 animate : function(args, duration, onComplete, easing, animType){
7454 this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7459 * @private Internal animation call
7461 anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7462 animType = animType || 'run';
7465 anim = Ext.lib.Anim[animType](
7468 (opt.duration || defaultDur) || .35,
7469 (opt.easing || defaultEase) || 'easeOut',
7472 if(opt.callback) opt.callback.call(opt.scope || me, me, opt);
7480 // private legacy anim prep
7481 preanim : function(a, i){
7482 return !a[i] ? false : (typeof a[i] == 'object' ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7486 * Checks whether the element is currently visible using both visibility and display properties.
7487 * @return {Boolean} True if the element is currently visible, else false
7489 isVisible : function() {
7490 return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);
7494 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7495 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7496 * @param {Boolean} visible Whether the element is visible
7497 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7498 * @return {Ext.Element} this
7500 setVisible : function(visible, animate){
7501 var me = this, isDisplay, isVisible, isOffsets,
7504 // hideMode string override
7505 if (typeof animate == 'string'){
7506 isDisplay = animate == DISPLAY;
7507 isVisible = animate == VISIBILITY;
7508 isOffsets = animate == OFFSETS;
7511 isDisplay = getVisMode(this.dom) == ELDISPLAY;
7512 isVisible = !isDisplay;
7515 if (!animate || !me.anim) {
7517 me.setDisplayed(visible);
7518 } else if (isOffsets){
7520 me.hideModeStyles = {
7521 position: me.getStyle('position'),
7522 top: me.getStyle('top'),
7523 left: me.getStyle('left')
7526 me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
7528 me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
7532 dom.style.visibility = visible ? "visible" : HIDDEN;
7535 // closure for composites
7538 me.setVisible(true);
7540 me.anim({opacity: { to: (visible?1:0) }},
7541 me.preanim(arguments, 1),
7547 dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;
7548 Ext.fly(dom).setOpacity(1);
7556 * Toggles the element's visibility or display, depending on visibility mode.
7557 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7558 * @return {Ext.Element} this
7560 toggle : function(animate){
7562 me.setVisible(!me.isVisible(), me.preanim(arguments, 0));
7567 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7568 * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
7569 * @return {Ext.Element} this
7571 setDisplayed : function(value) {
7572 if(typeof value == "boolean"){
7573 value = value ? getDisplay(this.dom) : NONE;
7575 this.setStyle(DISPLAY, value);
7580 fixDisplay : function(){
7582 if(me.isStyle(DISPLAY, NONE)){
7583 me.setStyle(VISIBILITY, HIDDEN);
7584 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
7585 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
7586 me.setStyle(DISPLAY, "block");
7592 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7593 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7594 * @return {Ext.Element} this
7596 hide : function(animate){
7597 // hideMode override
7598 if (typeof animate == 'string'){
7599 this.setVisible(false, animate);
7602 this.setVisible(false, this.preanim(arguments, 0));
7607 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7608 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7609 * @return {Ext.Element} this
7611 show : function(animate){
7612 // hideMode override
7613 if (typeof animate == 'string'){
7614 this.setVisible(true, animate);
7617 this.setVisible(true, this.preanim(arguments, 0));
7623 * @class Ext.Element
7625 Ext.Element.addMethods(
7627 var VISIBILITY = "visibility",
7628 DISPLAY = "display",
7631 XMASKED = "x-masked",
7632 XMASKEDRELATIVE = "x-masked-relative",
7633 data = Ext.Element.data;
7637 * Checks whether the element is currently visible using both visibility and display properties.
7638 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7639 * @return {Boolean} True if the element is currently visible, else false
7641 isVisible : function(deep) {
7642 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),
7643 p = this.dom.parentNode;
7644 if(deep !== true || !vis){
7647 while(p && !/^body/i.test(p.tagName)){
7648 if(!Ext.fly(p, '_isVisible').isVisible()){
7657 * Returns true if display is not "none"
7660 isDisplayed : function() {
7661 return !this.isStyle(DISPLAY, NONE);
7665 * Convenience method for setVisibilityMode(Element.DISPLAY)
7666 * @param {String} display (optional) What to set display to when visible
7667 * @return {Ext.Element} this
7669 enableDisplayMode : function(display){
7670 this.setVisibilityMode(Ext.Element.DISPLAY);
7671 if(!Ext.isEmpty(display)){
7672 data(this.dom, 'originalDisplay', display);
7678 * Puts a mask over this element to disable user interaction. Requires core.css.
7679 * This method can only be applied to elements which accept child nodes.
7680 * @param {String} msg (optional) A message to display in the mask
7681 * @param {String} msgCls (optional) A css class to apply to the msg element
7682 * @return {Element} The mask element
7684 mask : function(msg, msgCls){
7688 EXTELMASKMSG = "ext-el-mask-msg",
7692 if(!/^body/i.test(dom.tagName) && me.getStyle('position') == 'static'){
7693 me.addClass(XMASKEDRELATIVE);
7695 if((el = data(dom, 'maskMsg'))){
7698 if((el = data(dom, 'mask'))){
7702 mask = dh.append(dom, {cls : "ext-el-mask"}, true);
7703 data(dom, 'mask', mask);
7705 me.addClass(XMASKED);
7706 mask.setDisplayed(true);
7707 if(typeof msg == 'string'){
7708 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
7709 data(dom, 'maskMsg', mm);
7710 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
7711 mm.dom.firstChild.innerHTML = msg;
7712 mm.setDisplayed(true);
7715 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically
7716 mask.setSize(undefined, me.getHeight());
7722 * Removes a previously applied mask.
7724 unmask : function(){
7727 mask = data(dom, 'mask'),
7728 maskMsg = data(dom, 'maskMsg');
7732 data(dom, 'maskMsg', undefined);
7735 data(dom, 'mask', undefined);
7737 me.removeClass([XMASKED, XMASKEDRELATIVE]);
7741 * Returns true if this element is masked
7744 isMasked : function(){
7745 var m = data(this.dom, 'mask');
7746 return m && m.isVisible();
7750 * Creates an iframe shim for this element to keep selects and other windowed objects from
7752 * @return {Ext.Element} The new shim element
7754 createShim : function(){
7755 var el = document.createElement('iframe'),
7757 el.frameBorder = '0';
7758 el.className = 'ext-shim';
7759 el.src = Ext.SSL_SECURE_URL;
7760 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
7761 shim.autoBoxAdjust = false;
7766 * @class Ext.Element
7768 Ext.Element.addMethods({
7770 * Convenience method for constructing a KeyMap
7771 * @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:
7772 * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
7773 * @param {Function} fn The function to call
7774 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
7775 * @return {Ext.KeyMap} The KeyMap created
7777 addKeyListener : function(key, fn, scope){
7779 if(typeof key != 'object' || Ext.isArray(key)){
7795 return new Ext.KeyMap(this, config);
7799 * Creates a KeyMap for this element
7800 * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
7801 * @return {Ext.KeyMap} The KeyMap created
7803 addKeyMap : function(config){
7804 return new Ext.KeyMap(this, config);
7810 UNDEFINED = undefined,
7824 ABSOLUTE = "absolute",
7825 VISIBLE = "visible",
7827 POSITION = "position",
7828 EASEOUT = "easeOut",
7830 * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element
7832 flyEl = new Ext.Element.Flyweight(),
7834 getObject = function(o){
7837 fly = function(dom){
7839 flyEl.id = Ext.id(dom);
7843 * Queueing now stored outside of the element due to closure issues
7845 getQueue = function(id){
7851 setQueue = function(id, value){
7855 //Notifies Element that fx methods are available
7856 Ext.enableFx = TRUE;
7860 * <p>A class to provide basic animation and visual effects support. <b>Note:</b> This class is automatically applied
7861 * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.
7862 * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be
7863 * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>
7865 * <p><b><u>Method Chaining</u></b></p>
7866 * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
7867 * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
7868 * method chain. The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
7869 * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately. For this reason,
7870 * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
7871 * expected results and should be done with care. Also see <tt>{@link #callback}</tt>.</p><br/>
7873 * <p><b><u>Anchor Options for Motion Effects</u></b></p>
7874 * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
7875 * that will serve as either the start or end point of the animation. Following are all of the supported anchor positions:</p>
7878 ----- -----------------------------
7879 tl The top left corner
7880 t The center of the top edge
7881 tr The top right corner
7882 l The center of the left edge
7883 r The center of the right edge
7884 bl The bottom left corner
7885 b The center of the bottom edge
7886 br The bottom right corner
7888 * <b>Note</b>: some Fx methods accept specific custom config parameters. The options shown in the Config Options
7889 * section below are common options that can be passed to any Fx method unless otherwise noted.</b>
7891 * @cfg {Function} callback A function called when the effect is finished. Note that effects are queued internally by the
7892 * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together
7893 * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>
7894 * el.slideIn().highlight();
7896 * The callback is intended for any additional code that should run once a particular effect has completed. The Element
7897 * being operated upon is passed as the first parameter.
7899 * @cfg {Object} scope The scope (<code>this</code> reference) in which the <tt>{@link #callback}</tt> function is executed. Defaults to the browser window.
7901 * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>
7902 * <li><b><tt>backBoth</tt></b></li>
7903 * <li><b><tt>backIn</tt></b></li>
7904 * <li><b><tt>backOut</tt></b></li>
7905 * <li><b><tt>bounceBoth</tt></b></li>
7906 * <li><b><tt>bounceIn</tt></b></li>
7907 * <li><b><tt>bounceOut</tt></b></li>
7908 * <li><b><tt>easeBoth</tt></b></li>
7909 * <li><b><tt>easeBothStrong</tt></b></li>
7910 * <li><b><tt>easeIn</tt></b></li>
7911 * <li><b><tt>easeInStrong</tt></b></li>
7912 * <li><b><tt>easeNone</tt></b></li>
7913 * <li><b><tt>easeOut</tt></b></li>
7914 * <li><b><tt>easeOutStrong</tt></b></li>
7915 * <li><b><tt>elasticBoth</tt></b></li>
7916 * <li><b><tt>elasticIn</tt></b></li>
7917 * <li><b><tt>elasticOut</tt></b></li>
7920 * @cfg {String} afterCls A css class to apply after the effect
7921 * @cfg {Number} duration The length of time (in seconds) that the effect should last
7923 * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between
7924 * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.
7926 * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
7927 * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to
7928 * effects that end with the element being visually hidden, ignored otherwise)
7929 * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object
7930 * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the
7931 * Element after the effect finishes.
7932 * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
7933 * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
7934 * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)
7938 // private - calls the function taking arguments from the argHash based on the key. Returns the return value of the function.
7939 // this is useful for replacing switch statements (for example).
7940 switchStatements : function(key, fn, argHash){
7941 return fn.apply(this, argHash[key]);
7945 * Slides the element into view. An anchor point can be optionally passed to set the point of
7946 * origin for the slide effect. This function automatically handles wrapping the element with
7947 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
7950 // default: slide the element in from the top
7953 // custom: slide the element in from the right with a 2-second duration
7954 el.slideIn('r', { duration: 2 });
7956 // common config options shown with default values
7962 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
7963 * @param {Object} options (optional) Object literal with any of the Fx config options
7964 * @return {Ext.Element} The Element
7966 slideIn : function(anchor, o){
7982 anchor = anchor || "t";
7984 me.queueFx(o, function(){
7985 xy = fly(dom).getXY();
7986 // fix display to visibility
7987 fly(dom).fixDisplay();
7989 // restore values after effect
7990 r = fly(dom).getFxRestore();
7991 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
7992 b.right = b.x + b.width;
7993 b.bottom = b.y + b.height;
7995 // fixed size for slide
7996 fly(dom).setWidth(b.width).setHeight(b.height);
7999 wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);
8001 st.visibility = VISIBLE;
8002 st.position = ABSOLUTE;
8004 // clear out temp styles after slide and unwrap
8006 fly(dom).fxUnwrap(wrap, r.pos, o);
8008 st.height = r.height;
8009 fly(dom).afterFx(o);
8012 // time to calculate the positions
8013 pt = {to: [b.x, b.y]};
8015 bh = {to: b.height};
8017 function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){
8019 fly(wrap).setWidth(ww).setHeight(wh);
8021 fly(wrap)[sXY](sXYval);
8023 style[s1] = style[s2] = "0";
8036 args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
8037 t : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],
8038 l : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],
8039 r : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],
8040 b : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],
8041 tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],
8042 bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],
8043 br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],
8044 tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]
8047 st.visibility = VISIBLE;
8050 arguments.callee.anim = fly(wrap).fxanim(args,
8061 * Slides the element out of view. An anchor point can be optionally passed to set the end point
8062 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
8063 * 'hidden') but block elements will still take up space in the document. The element must be removed
8064 * from the DOM using the 'remove' config option if desired. This function automatically handles
8065 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
8068 // default: slide the element out to the top
8071 // custom: slide the element out to the right with a 2-second duration
8072 el.slideOut('r', { duration: 2 });
8074 // common config options shown with default values
8082 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
8083 * @param {Object} options (optional) Object literal with any of the Fx config options
8084 * @return {Ext.Element} The Element
8086 slideOut : function(anchor, o){
8098 anchor = anchor || "t";
8100 me.queueFx(o, function(){
8102 // restore values after effect
8103 r = fly(dom).getFxRestore();
8104 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
8105 b.right = b.x + b.width;
8106 b.bottom = b.y + b.height;
8108 // fixed size for slide
8109 fly(dom).setWidth(b.width).setHeight(b.height);
8112 wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);
8114 st.visibility = VISIBLE;
8115 st.position = ABSOLUTE;
8116 fly(wrap).setWidth(b.width).setHeight(b.height);
8119 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8120 fly(dom).fxUnwrap(wrap, r.pos, o);
8122 st.height = r.height;
8123 fly(dom).afterFx(o);
8126 function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){
8129 style[s1] = style[s2] = "0";
8141 a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
8142 t : [st, LEFT, BOTTOM, HEIGHT, zero],
8143 l : [st, RIGHT, TOP, WIDTH, zero],
8144 r : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],
8145 b : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8146 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],
8147 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8148 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],
8149 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]
8152 arguments.callee.anim = fly(wrap).fxanim(a,
8163 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
8164 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
8165 * The element must be removed from the DOM using the 'remove' config option if desired.
8171 // common config options shown with default values
8179 * @param {Object} options (optional) Object literal with any of the Fx config options
8180 * @return {Ext.Element} The Element
8191 me.queueFx(o, function(){
8192 width = fly(dom).getWidth();
8193 height = fly(dom).getHeight();
8194 fly(dom).clearOpacity();
8197 // restore values after effect
8198 r = fly(dom).getFxRestore();
8201 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8202 fly(dom).clearOpacity();
8203 fly(dom).setPositioning(r.pos);
8205 st.height = r.height;
8207 fly(dom).afterFx(o);
8210 arguments.callee.anim = fly(dom).fxanim({
8211 width : {to : fly(dom).adjustWidth(width * 2)},
8212 height : {to : fly(dom).adjustHeight(height * 2)},
8213 points : {by : [-width * .5, -height * .5]},
8215 fontSize: {to : 200, unit: "%"}
8227 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
8228 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
8229 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
8235 // all config options shown with default values
8243 * @param {Object} options (optional) Object literal with any of the Fx config options
8244 * @return {Ext.Element} The Element
8246 switchOff : function(o){
8253 me.queueFx(o, function(){
8254 fly(dom).clearOpacity();
8257 // restore values after effect
8258 r = fly(dom).getFxRestore();
8261 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8262 fly(dom).clearOpacity();
8263 fly(dom).setPositioning(r.pos);
8265 st.height = r.height;
8266 fly(dom).afterFx(o);
8269 fly(dom).fxanim({opacity : {to : 0.3}},
8275 fly(dom).clearOpacity();
8279 points : {by : [0, fly(dom).getHeight() * .5]}
8293 * Highlights the Element by setting a color (applies to the background-color by default, but can be
8294 * changed using the "attr" config option) and then fading back to the original color. If no original
8295 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
8298 // default: highlight background to yellow
8301 // custom: highlight foreground text to blue for 2 seconds
8302 el.highlight("0000ff", { attr: 'color', duration: 2 });
8304 // common config options shown with default values
8305 el.highlight("ffff9c", {
8306 attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
8307 endColor: (current color) or "ffffff",
8312 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
8313 * @param {Object} options (optional) Object literal with any of the Fx config options
8314 * @return {Ext.Element} The Element
8316 highlight : function(color, o){
8320 attr = o.attr || "backgroundColor",
8324 me.queueFx(o, function(){
8325 fly(dom).clearOpacity();
8329 dom.style[attr] = restore;
8330 fly(dom).afterFx(o);
8332 restore = dom.style[attr];
8333 a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};
8334 arguments.callee.anim = fly(dom).fxanim(a,
8345 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
8348 // default: a single light blue ripple
8351 // custom: 3 red ripples lasting 3 seconds total
8352 el.frame("ff0000", 3, { duration: 3 });
8354 // common config options shown with default values
8355 el.frame("C3DAF9", 1, {
8356 duration: 1 //duration of each individual ripple.
8357 // Note: Easing is not configurable and will be ignored if included
8360 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
8361 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
8362 * @param {Object} options (optional) Object literal with any of the Fx config options
8363 * @return {Ext.Element} The Element
8365 frame : function(color, count, o){
8372 me.queueFx(o, function(){
8373 color = color || '#C3DAF9';
8374 if(color.length == 6){
8375 color = '#' + color;
8380 var xy = fly(dom).getXY(),
8381 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},
8383 proxy = fly(document.body || document.documentElement).createChild({
8385 position : ABSOLUTE,
8386 'z-index': 35000, // yee haw
8387 border : '0px solid ' + color
8390 return proxy.queueFx({}, animFn);
8394 arguments.callee.anim = {
8403 var scale = Ext.isBorderBox ? 2 : 1;
8404 active = proxy.anim({
8405 top : {from : b.y, to : b.y - 20},
8406 left : {from : b.x, to : b.x - 20},
8407 borderWidth : {from : 0, to : 10},
8408 opacity : {from : 1, to : 0},
8409 height : {from : b.height, to : b.height + 20 * scale},
8410 width : {from : b.width, to : b.width + 20 * scale}
8412 duration: o.duration || 1,
8413 callback: function() {
8415 --count > 0 ? queue() : fly(dom).afterFx(o);
8418 arguments.callee.anim = {
8431 * Creates a pause before any subsequent queued effects begin. If there are
8432 * no effects queued after the pause it will have no effect.
8437 * @param {Number} seconds The length of time to pause (in seconds)
8438 * @return {Ext.Element} The Element
8440 pause : function(seconds){
8444 this.queueFx({}, function(){
8445 t = setTimeout(function(){
8446 fly(dom).afterFx({});
8448 arguments.callee.anim = {
8452 fly(dom).afterFx({});
8460 * Fade an element in (from transparent to opaque). The ending opacity can be specified
8461 * using the <tt>{@link #endOpacity}</tt> config option.
8464 // default: fade in from opacity 0 to 100%
8467 // custom: fade in from opacity 0 to 75% over 2 seconds
8468 el.fadeIn({ endOpacity: .75, duration: 2});
8470 // common config options shown with default values
8472 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
8477 * @param {Object} options (optional) Object literal with any of the Fx config options
8478 * @return {Ext.Element} The Element
8480 fadeIn : function(o){
8484 to = o.endOpacity || 1;
8486 me.queueFx(o, function(){
8487 fly(dom).setOpacity(0);
8488 fly(dom).fixDisplay();
8489 dom.style.visibility = VISIBLE;
8490 arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},
8491 o, NULL, .5, EASEOUT, function(){
8493 fly(dom).clearOpacity();
8495 fly(dom).afterFx(o);
8502 * Fade an element out (from opaque to transparent). The ending opacity can be specified
8503 * using the <tt>{@link #endOpacity}</tt> config option. Note that IE may require
8504 * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
8507 // default: fade out from the element's current opacity to 0
8510 // custom: fade out from the element's current opacity to 25% over 2 seconds
8511 el.fadeOut({ endOpacity: .25, duration: 2});
8513 // common config options shown with default values
8515 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
8522 * @param {Object} options (optional) Object literal with any of the Fx config options
8523 * @return {Ext.Element} The Element
8525 fadeOut : function(o){
8530 to = o.endOpacity || 0;
8532 me.queueFx(o, function(){
8533 arguments.callee.anim = fly(dom).fxanim({
8534 opacity : {to : to}},
8541 Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ?
8542 style.display = "none" :
8543 style.visibility = HIDDEN;
8545 fly(dom).clearOpacity();
8547 fly(dom).afterFx(o);
8554 * Animates the transition of an element's dimensions from a starting height/width
8555 * to an ending height/width. This method is a convenience implementation of {@link shift}.
8558 // change height and width to 100x100 pixels
8561 // common config options shown with default values. The height and width will default to
8562 // the element's existing values if passed as null.
8564 [element's width],
8565 [element's height], {
8571 * @param {Number} width The new width (pass undefined to keep the original width)
8572 * @param {Number} height The new height (pass undefined to keep the original height)
8573 * @param {Object} options (optional) Object literal with any of the Fx config options
8574 * @return {Ext.Element} The Element
8576 scale : function(w, h, o){
8577 this.shift(Ext.apply({}, o, {
8585 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
8586 * Any of these properties not specified in the config object will not be changed. This effect
8587 * requires that at least one new dimension, position or opacity setting must be passed in on
8588 * the config object in order for the function to have any effect.
8591 // slide the element horizontally to x position 200 while changing the height and opacity
8592 el.shift({ x: 200, height: 50, opacity: .8 });
8594 // common config options shown with default values.
8596 width: [element's width],
8597 height: [element's height],
8598 x: [element's x position],
8599 y: [element's y position],
8600 opacity: [element's opacity],
8605 * @param {Object} options Object literal with any of the Fx config options
8606 * @return {Ext.Element} The Element
8608 shift : function(o){
8613 this.queueFx(o, function(){
8614 for (var prop in o) {
8615 if (o[prop] != UNDEFINED) {
8616 a[prop] = {to : o[prop]};
8620 a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;
8621 a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;
8623 if (a.x || a.y || a.xy) {
8625 {to : [ a.x ? a.x.to : fly(dom).getX(),
8626 a.y ? a.y.to : fly(dom).getY()]};
8629 arguments.callee.anim = fly(dom).fxanim(a,
8635 fly(dom).afterFx(o);
8642 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
8643 * ending point of the effect.
8646 // default: slide the element downward while fading out
8649 // custom: slide the element out to the right with a 2-second duration
8650 el.ghost('r', { duration: 2 });
8652 // common config options shown with default values
8660 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
8661 * @param {Object} options (optional) Object literal with any of the Fx config options
8662 * @return {Ext.Element} The Element
8664 ghost : function(anchor, o){
8669 a = {opacity: {to: 0}, points: {}},
8675 anchor = anchor || "b";
8677 me.queueFx(o, function(){
8678 // restore values after effect
8679 r = fly(dom).getFxRestore();
8680 w = fly(dom).getWidth();
8681 h = fly(dom).getHeight();
8684 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8685 fly(dom).clearOpacity();
8686 fly(dom).setPositioning(r.pos);
8688 st.height = r.height;
8689 fly(dom).afterFx(o);
8692 pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {
8703 arguments.callee.anim = fly(dom).fxanim(a,
8713 * Ensures that all effects queued after syncFx is called on the element are
8714 * run concurrently. This is the opposite of {@link #sequenceFx}.
8715 * @return {Ext.Element} The Element
8717 syncFx : function(){
8719 me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8728 * Ensures that all effects queued after sequenceFx is called on the element are
8729 * run in sequence. This is the opposite of {@link #syncFx}.
8730 * @return {Ext.Element} The Element
8732 sequenceFx : function(){
8734 me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8743 nextFx : function(){
8744 var ef = getQueue(this.dom.id)[0];
8751 * Returns true if the element has any effects actively running or queued, else returns false.
8752 * @return {Boolean} True if element has active effects, else false
8754 hasActiveFx : function(){
8755 return getQueue(this.dom.id)[0];
8759 * Stops any running effects and clears the element's internal effects queue if it contains
8760 * any additional effects that haven't started yet.
8761 * @return {Ext.Element} The Element
8763 stopFx : function(finish){
8766 if(me.hasActiveFx()){
8767 var cur = getQueue(id)[0];
8768 if(cur && cur.anim){
8769 if(cur.anim.isAnimated){
8770 setQueue(id, [cur]); //clear
8771 cur.anim.stop(finish !== undefined ? finish : TRUE);
8781 beforeFx : function(o){
8782 if(this.hasActiveFx() && !o.concurrent){
8793 * Returns true if the element is currently blocking so that no other effect can be queued
8794 * until this effect is finished, else returns false if blocking is not set. This is commonly
8795 * used to ensure that an effect initiated by a user action runs to completion prior to the
8796 * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
8797 * @return {Boolean} True if blocking, else false
8799 hasFxBlock : function(){
8800 var q = getQueue(this.dom.id);
8801 return q && q[0] && q[0].block;
8805 queueFx : function(o, fn){
8806 var me = fly(this.dom);
8807 if(!me.hasFxBlock()){
8808 Ext.applyIf(o, me.fxDefaults);
8810 var run = me.beforeFx(o);
8812 getQueue(me.dom.id).push(fn);
8824 fxWrap : function(pos, o, vis){
8828 if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){
8830 wrapXY = fly(dom).getXY();
8832 var div = document.createElement("div");
8833 div.style.visibility = vis;
8834 wrap = dom.parentNode.insertBefore(div, dom);
8835 fly(wrap).setPositioning(pos);
8836 if(fly(wrap).isStyle(POSITION, "static")){
8837 fly(wrap).position("relative");
8839 fly(dom).clearPositioning('auto');
8841 wrap.appendChild(dom);
8843 fly(wrap).setXY(wrapXY);
8850 fxUnwrap : function(wrap, pos, o){
8852 fly(dom).clearPositioning();
8853 fly(dom).setPositioning(pos);
8855 var pn = fly(wrap).dom.parentNode;
8856 pn.insertBefore(dom, wrap);
8862 getFxRestore : function(){
8863 var st = this.dom.style;
8864 return {pos: this.getPositioning(), width: st.width, height : st.height};
8868 afterFx : function(o){
8872 fly(dom).setStyle(o.afterStyle);
8875 fly(dom).addClass(o.afterCls);
8877 if(o.remove == TRUE){
8881 o.callback.call(o.scope, fly(dom));
8884 getQueue(id).shift();
8890 fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
8891 animType = animType || 'run';
8893 var anim = Ext.lib.Anim[animType](
8896 (opt.duration || defaultDur) || .35,
8897 (opt.easing || defaultEase) || EASEOUT,
8907 Ext.Fx.resize = Ext.Fx.scale;
8909 //When included, Ext.Fx is automatically applied to Element so that all basic
8910 //effects are available directly via the Element API
8911 Ext.Element.addMethods(Ext.Fx);
8914 * @class Ext.CompositeElementLite
8915 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
8916 * members, or to perform collective actions upon the whole set.</p>
8917 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
8918 * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
8919 * Example:<pre><code>
8920 var els = Ext.select("#some-el div.some-class");
8921 // or select directly from an existing element
8922 var el = Ext.get('some-el');
8923 el.select('div.some-class');
8925 els.setWidth(100); // all elements become 100 width
8926 els.hide(true); // all elements fade out and hide
8928 els.setWidth(100).hide(true);
8931 Ext.CompositeElementLite = function(els, root){
8933 * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
8934 * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
8935 * to augment the capabilities of the CompositeElementLite class may use it when adding
8936 * methods to the class.</p>
8937 * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
8938 * following siblings of selected elements, the code would be</p><code><pre>
8939 Ext.override(Ext.CompositeElementLite, {
8940 nextAll: function() {
8941 var els = this.elements, i, l = els.length, n, r = [], ri = -1;
8943 // Loop through all elements in this Composite, accumulating
8944 // an Array of all siblings.
8945 for (i = 0; i < l; i++) {
8946 for (n = els[i].nextSibling; n; n = n.nextSibling) {
8951 // Add all found siblings to this Composite
8956 * @property elements
8959 this.add(els, root);
8960 this.el = new Ext.Element.Flyweight();
8963 Ext.CompositeElementLite.prototype = {
8967 getElement : function(el){
8968 // Set the shared flyweight dom property to the current element
8976 transformElement : function(el){
8977 return Ext.getDom(el);
8981 * Returns the number of elements in this Composite.
8984 getCount : function(){
8985 return this.elements.length;
8988 * Adds elements to this Composite object.
8989 * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
8990 * @return {CompositeElement} This Composite object.
8992 add : function(els, root){
8994 elements = me.elements;
8998 if(typeof els == "string"){
8999 els = Ext.Element.selectorFunction(els, root);
9000 }else if(els.isComposite){
9002 }else if(!Ext.isIterable(els)){
9006 for(var i = 0, len = els.length; i < len; ++i){
9007 elements.push(me.transformElement(els[i]));
9012 invoke : function(fn, args){
9019 for(i = 0; i < len; i++) {
9022 Ext.Element.prototype[fn].apply(me.getElement(e), args);
9028 * Returns a flyweight Element of the dom element object at the specified index
9029 * @param {Number} index
9030 * @return {Ext.Element}
9032 item : function(index){
9034 el = me.elements[index],
9038 out = me.getElement(el);
9043 // fixes scope with flyweight
9044 addListener : function(eventName, handler, scope, opt){
9045 var els = this.elements,
9049 for(i = 0; i<len; i++) {
9052 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
9058 * <p>Calls the passed function for each element in this composite.</p>
9059 * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
9060 * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
9061 * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
9062 * a reference to the dom node, use el.dom.</b></div></li>
9063 * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
9064 * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
9066 * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
9067 * @return {CompositeElement} this
9069 each : function(fn, scope){
9075 for(i = 0; i<len; i++) {
9078 e = this.getElement(e);
9079 if(fn.call(scope || e, e, me, i) === false){
9088 * Clears this Composite and adds the elements passed.
9089 * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
9090 * @return {CompositeElement} this
9092 fill : function(els){
9100 * Filters this composite to only elements that match the passed selector.
9101 * @param {String/Function} selector A string CSS selector or a comparison function.
9102 * The comparison function will be called with the following arguments:<ul>
9103 * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
9104 * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
9106 * @return {CompositeElement} this
9108 filter : function(selector){
9111 elements = me.elements,
9112 fn = Ext.isFunction(selector) ? selector
9114 return el.is(selector);
9118 me.each(function(el, self, i){
9119 if(fn(el, i) !== false){
9120 els[els.length] = me.transformElement(el);
9128 * Find the index of the passed element within the composite collection.
9129 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9130 * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
9132 indexOf : function(el){
9133 return this.elements.indexOf(this.transformElement(el));
9137 * Replaces the specified element with the passed element.
9138 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9140 * @param {Mixed} replacement The id of an element or the Element itself.
9141 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
9142 * @return {CompositeElement} this
9144 replaceElement : function(el, replacement, domReplace){
9145 var index = !isNaN(el) ? el : this.indexOf(el),
9148 replacement = Ext.getDom(replacement);
9150 d = this.elements[index];
9151 d.parentNode.insertBefore(replacement, d);
9154 this.elements.splice(index, 1, replacement);
9160 * Removes all elements.
9167 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
9171 ElProto = Ext.Element.prototype,
9172 CelProto = Ext.CompositeElementLite.prototype;
9174 for(fnName in ElProto){
9175 if(Ext.isFunction(ElProto[fnName])){
9177 CelProto[fnName] = CelProto[fnName] || function(){
9178 return this.invoke(fnName, arguments);
9180 }).call(CelProto, fnName);
9187 Ext.Element.selectorFunction = Ext.DomQuery.select;
9191 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9192 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9193 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9194 * @param {String/Array} selector The CSS selector or an array of elements
9195 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9196 * @return {CompositeElementLite/CompositeElement}
9197 * @member Ext.Element
9200 Ext.Element.select = function(selector, root){
9202 if(typeof selector == "string"){
9203 els = Ext.Element.selectorFunction(selector, root);
9204 }else if(selector.length !== undefined){
9207 throw "Invalid selector";
9209 return new Ext.CompositeElementLite(els);
9212 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9213 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9214 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9215 * @param {String/Array} selector The CSS selector or an array of elements
9216 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9217 * @return {CompositeElementLite/CompositeElement}
9221 Ext.select = Ext.Element.select;
9223 * @class Ext.CompositeElementLite
9225 Ext.apply(Ext.CompositeElementLite.prototype, {
9226 addElements : function(els, root){
9230 if(typeof els == "string"){
9231 els = Ext.Element.selectorFunction(els, root);
9233 var yels = this.elements;
9234 Ext.each(els, function(e) {
9235 yels.push(Ext.get(e));
9241 * Returns the first Element
9242 * @return {Ext.Element}
9245 return this.item(0);
9249 * Returns the last Element
9250 * @return {Ext.Element}
9253 return this.item(this.getCount()-1);
9257 * Returns true if this composite contains the passed element
9258 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9261 contains : function(el){
9262 return this.indexOf(el) != -1;
9266 * Removes the specified element(s).
9267 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9268 * or an array of any of those.
9269 * @param {Boolean} removeDom (optional) True to also remove the element from the document
9270 * @return {CompositeElement} this
9272 removeElement : function(keys, removeDom){
9274 els = this.elements,
9276 Ext.each(keys, function(val){
9277 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
9292 * @class Ext.CompositeElement
9293 * @extends Ext.CompositeElementLite
9294 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
9295 * members, or to perform collective actions upon the whole set.</p>
9296 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
9297 * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
9298 * <p>All methods return <i>this</i> and can be chained.</p>
9301 var els = Ext.select("#some-el div.some-class", true);
9302 // or select directly from an existing element
9303 var el = Ext.get('some-el');
9304 el.select('div.some-class', true);
9306 els.setWidth(100); // all elements become 100 width
9307 els.hide(true); // all elements fade out and hide
9309 els.setWidth(100).hide(true);
9312 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
9314 constructor : function(els, root){
9316 this.add(els, root);
9320 getElement : function(el){
9321 // In this case just return it, since we already have a reference to it
9326 transformElement : function(el){
9331 * Adds elements to this composite.
9332 * @param {String/Array} els A string CSS selector, an array of elements or an element
9333 * @return {CompositeElement} this
9337 * Returns the Element object at the specified index
9338 * @param {Number} index
9339 * @return {Ext.Element}
9343 * Iterates each <code>element</code> in this <code>composite</code>
9344 * calling the supplied function using {@link Ext#each}.
9345 * @param {Function} fn The function to be called with each
9346 * <code>element</code>. If the supplied function returns <tt>false</tt>,
9347 * iteration stops. This function is called with the following arguments:
9348 * <div class="mdetail-params"><ul>
9349 * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>
9350 * in the <code>composite</code></div></li>
9351 * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>
9352 * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>
9354 * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.
9355 * Defaults to the <code>element</code> at the current <code>index</code>
9356 * within the composite.
9357 * @return {CompositeElement} this
9362 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9363 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9364 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9365 * @param {String/Array} selector The CSS selector or an array of elements
9366 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9367 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9368 * @return {CompositeElementLite/CompositeElement}
9369 * @member Ext.Element
9372 Ext.Element.select = function(selector, unique, root){
9374 if(typeof selector == "string"){
9375 els = Ext.Element.selectorFunction(selector, root);
9376 }else if(selector.length !== undefined){
9379 throw "Invalid selector";
9382 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
9386 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9387 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9388 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9389 * @param {String/Array} selector The CSS selector or an array of elements
9390 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9391 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9392 * @return {CompositeElementLite/CompositeElement}
9396 Ext.select = Ext.Element.select;(function(){
9397 var BEFOREREQUEST = "beforerequest",
9398 REQUESTCOMPLETE = "requestcomplete",
9399 REQUESTEXCEPTION = "requestexception",
9400 UNDEFINED = undefined,
9407 * @class Ext.data.Connection
9408 * @extends Ext.util.Observable
9409 * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made
9410 * either to a configured URL, or to a URL specified at request time.</p>
9411 * <p>Requests made by this class are asynchronous, and will return immediately. No data from
9412 * the server will be available to the statement immediately following the {@link #request} call.
9413 * To process returned data, use a
9414 * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>
9415 * in the request options object,
9416 * or an {@link #requestcomplete event listener}.</p>
9417 * <p><h3>File Uploads</h3><a href="#request-option-isUpload" ext:member="request-option-isUpload" ext:cls="Ext.data.Connection">File uploads</a> are not performed using normal "Ajax" techniques, that
9418 * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
9419 * manner with the DOM <tt><form></tt> element temporarily modified to have its
9420 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9421 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
9422 * but removed after the return data has been gathered.</p>
9423 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9424 * server is using JSON to send the return object, then the
9425 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9426 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9427 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
9428 * "<" as "&lt;", "&" as "&amp;" etc.</p>
9429 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9430 * is created containing a <tt>responseText</tt> property in order to conform to the
9431 * requirements of event handlers and callbacks.</p>
9432 * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
9433 * and some server technologies (notably JEE) may require some custom processing in order to
9434 * retrieve parameter names and parameter values from the packet content.</p>
9436 * @param {Object} config a configuration object.
9438 Ext.data.Connection = function(config){
9439 Ext.apply(this, config);
9442 * @event beforerequest
9443 * Fires before a network request is made to retrieve a data object.
9444 * @param {Connection} conn This Connection object.
9445 * @param {Object} options The options config object passed to the {@link #request} method.
9449 * @event requestcomplete
9450 * Fires if the request was successfully completed.
9451 * @param {Connection} conn This Connection object.
9452 * @param {Object} response The XHR object containing the response data.
9453 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9455 * @param {Object} options The options config object passed to the {@link #request} method.
9459 * @event requestexception
9460 * Fires if an error HTTP status was returned from the server.
9461 * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
9462 * for details of HTTP status codes.
9463 * @param {Connection} conn This Connection object.
9464 * @param {Object} response The XHR object containing the response data.
9465 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9467 * @param {Object} options The options config object passed to the {@link #request} method.
9471 Ext.data.Connection.superclass.constructor.call(this);
9474 Ext.extend(Ext.data.Connection, Ext.util.Observable, {
9476 * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>
9477 * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope
9478 * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>
9481 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9482 * extra parameters to each request made by this object. (defaults to undefined)
9485 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9486 * to each request made by this object. (defaults to undefined)
9489 * @cfg {String} method (Optional) The default HTTP method to be used for requests.
9490 * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;
9491 * otherwise, GET will be used.)
9494 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9498 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9504 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9507 disableCaching: true,
9510 * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
9511 * through a cache buster. Defaults to '_dc'
9514 disableCachingParam: '_dc',
9517 * <p>Sends an HTTP request to a remote server.</p>
9518 * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
9519 * return before the response has been received. Process any returned data
9520 * in a callback function.</p>
9523 url: 'ajax_demo/sample.json',
9524 success: function(response, opts) {
9525 var obj = Ext.decode(response.responseText);
9528 failure: function(response, opts) {
9529 console.log('server-side failure with status code ' + response.status);
9533 * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
9534 * @param {Object} options An object which may contain the following properties:<ul>
9535 * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
9536 * which to send the request, or a function to call which returns a URL string. The scope of the
9537 * function is specified by the <tt>scope</tt> option. Defaults to the configured
9538 * <tt>{@link #url}</tt>.</div></li>
9539 * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
9540 * An object containing properties which are used as parameters to the
9541 * request, a url encoded string or a function to call to get either. The scope of the function
9542 * is specified by the <tt>scope</tt> option.</div></li>
9543 * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
9544 * for the request. Defaults to the configured method, or if no method was configured,
9545 * "GET" if no parameters are being sent, and "POST" if parameters are being sent. Note that
9546 * the method name is case-sensitive and should be all caps.</div></li>
9547 * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
9548 * function to be called upon receipt of the HTTP response. The callback is
9549 * called regardless of success or failure and is passed the following
9551 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9552 * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
9553 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
9554 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
9555 * accessing elements of the response.</div></li>
9557 * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
9558 * to be called upon success of the request. The callback is passed the following
9560 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9561 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9563 * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
9564 * to be called upon failure of the request. The callback is passed the
9565 * following parameters:<ul>
9566 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9567 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9569 * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
9570 * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
9571 * specified as functions from which to draw values, then this also serves as the scope for those function calls.
9572 * Defaults to the browser window.</div></li>
9573 * <li><b>timeout</b> : Number (Optional)<div class="sub-desc">The timeout in milliseconds to be used for this request. Defaults to 30 seconds.</div></li>
9574 * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt><form></tt>
9575 * Element or the id of the <tt><form></tt> to pull parameters from.</div></li>
9576 * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
9577 * with the <tt>form</tt> option</b>.
9578 * <p>True if the form object is a file upload (will be set automatically if the form was
9579 * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
9580 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
9581 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
9582 * DOM <tt><form></tt> element temporarily modified to have its
9583 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9584 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
9585 * but removed after the return data has been gathered.</p>
9586 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9587 * server is using JSON to send the return object, then the
9588 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9589 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9590 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9591 * is created containing a <tt>responseText</tt> property in order to conform to the
9592 * requirements of event handlers and callbacks.</p>
9593 * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
9594 * and some server technologies (notably JEE) may require some custom processing in order to
9595 * retrieve parameter names and parameter values from the packet content.</p>
9597 * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
9598 * headers to set for the request.</div></li>
9599 * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
9600 * to use for the post. Note: This will be used instead of params for the post
9601 * data. Any params will be appended to the URL.</div></li>
9602 * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
9603 * data to use as the post. Note: This will be used instead of params for the post
9604 * data. Any params will be appended to the URL.</div></li>
9605 * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
9606 * to add a unique cache-buster param to GET requests.</div></li>
9608 * <p>The options object may also contain any other property which might be needed to perform
9609 * postprocessing in a callback because it is passed to callback functions.</p>
9610 * @return {Number} transactionId The id of the server transaction. This may be used
9611 * to cancel the request.
9613 request : function(o){
9615 if(me.fireEvent(BEFOREREQUEST, me, o)){
9617 if(!Ext.isEmpty(o.indicatorText)){
9618 me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";
9620 if(me.indicatorText) {
9621 Ext.getDom(o.el).innerHTML = me.indicatorText;
9623 o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {
9624 Ext.getDom(o.el).innerHTML = response.responseText;
9629 url = o.url || me.url,
9631 cb = {success: me.handleResponse,
9632 failure: me.handleFailure,
9634 argument: {options: o},
9635 timeout : o.timeout || me.timeout
9641 if (Ext.isFunction(p)) {
9642 p = p.call(o.scope||WINDOW, o);
9645 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);
9647 if (Ext.isFunction(url)) {
9648 url = url.call(o.scope || WINDOW, o);
9651 if((form = Ext.getDom(o.form))){
9652 url = url || form.action;
9653 if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) {
9654 return me.doFormUpload.call(me, o, p, url);
9656 serForm = Ext.lib.Ajax.serializeForm(form);
9657 p = p ? (p + '&' + serForm) : serForm;
9660 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);
9662 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
9663 var dcp = o.disableCachingParam || me.disableCachingParam;
9664 url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));
9667 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});
9669 if(o.autoAbort === true || me.autoAbort) {
9673 if((method == GET || o.xmlData || o.jsonData) && p){
9674 url = Ext.urlAppend(url, p);
9677 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));
9679 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;
9684 * Determine whether this object has a request outstanding.
9685 * @param {Number} transactionId (Optional) defaults to the last transaction
9686 * @return {Boolean} True if there is an outstanding request.
9688 isLoading : function(transId){
9689 return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;
9693 * Aborts any outstanding request.
9694 * @param {Number} transactionId (Optional) defaults to the last transaction
9696 abort : function(transId){
9697 if(transId || this.isLoading()){
9698 Ext.lib.Ajax.abort(transId || this.transId);
9703 handleResponse : function(response){
9704 this.transId = false;
9705 var options = response.argument.options;
9706 response.argument = options ? options.argument : null;
9707 this.fireEvent(REQUESTCOMPLETE, this, response, options);
9708 if(options.success){
9709 options.success.call(options.scope, response, options);
9711 if(options.callback){
9712 options.callback.call(options.scope, options, true, response);
9717 handleFailure : function(response, e){
9718 this.transId = false;
9719 var options = response.argument.options;
9720 response.argument = options ? options.argument : null;
9721 this.fireEvent(REQUESTEXCEPTION, this, response, options, e);
9722 if(options.failure){
9723 options.failure.call(options.scope, response, options);
9725 if(options.callback){
9726 options.callback.call(options.scope, options, false, response);
9731 doFormUpload : function(o, ps, url){
9734 frame = doc.createElement('iframe'),
9735 form = Ext.getDom(o.form),
9738 encoding = 'multipart/form-data',
9740 target: form.target,
9741 method: form.method,
9742 encoding: form.encoding,
9743 enctype: form.enctype,
9748 * Originally this behaviour was modified for Opera 10 to apply the secure URL after
9749 * the frame had been added to the document. It seems this has since been corrected in
9750 * Opera so the behaviour has been reverted, the URL will be set before being added.
9752 Ext.fly(frame).set({
9756 src: Ext.SSL_SECURE_URL
9759 doc.body.appendChild(frame);
9761 // This is required so that IE doesn't pop the response up in a new window.
9763 document.frames[id].name = id;
9772 action: url || buf.action
9775 // add dynamic params
9776 Ext.iterate(Ext.urlDecode(ps, false), function(k, v){
9777 hd = doc.createElement('input');
9783 form.appendChild(hd);
9789 // bogus response object
9790 r = {responseText : '',
9792 argument : o.argument},
9797 doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
9800 if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea
9801 r.responseText = firstChild.value;
9803 r.responseText = doc.body.innerHTML;
9806 //in IE the document may still have a body even if returns XML.
9807 r.responseXML = doc.XMLDocument || doc;
9812 Ext.EventManager.removeListener(frame, LOAD, cb, me);
9814 me.fireEvent(REQUESTCOMPLETE, me, r, o);
9816 function runCallback(fn, scope, args){
9817 if(Ext.isFunction(fn)){
9818 fn.apply(scope, args);
9822 runCallback(o.success, o.scope, [r, o]);
9823 runCallback(o.callback, o.scope, [o, true, r]);
9825 if(!me.debugUploads){
9826 setTimeout(function(){Ext.removeNode(frame);}, 100);
9830 Ext.EventManager.on(frame, LOAD, cb, this);
9833 Ext.fly(form).set(buf);
9834 Ext.each(hiddens, function(h) {
9843 * @extends Ext.data.Connection
9844 * <p>The global Ajax request class that provides a simple way to make Ajax requests
9845 * with maximum flexibility.</p>
9846 * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once
9847 * and override them at the request function level only if necessary.</p>
9848 * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>
9849 * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>
9850 * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>
9851 * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>
9854 // Default headers to pass in every request
9855 Ext.Ajax.defaultHeaders = {
9860 * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>
9861 * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>
9862 * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>
9863 * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>
9866 // Example: show a spinner during all Ajax requests
9867 Ext.Ajax.on('beforerequest', this.showSpinner, this);
9868 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
9869 Ext.Ajax.on('requestexception', this.hideSpinner, this);
9872 * <p>An example request:</p>
9875 Ext.Ajax.{@link Ext.data.Connection#request request}({
9882 params: { foo: 'bar' }
9885 // Simple ajax form submission
9886 Ext.Ajax.{@link Ext.data.Connection#request request}({
9894 Ext.Ajax = new Ext.data.Connection({
9896 * @cfg {String} url @hide
9899 * @cfg {Object} extraParams @hide
9902 * @cfg {Object} defaultHeaders @hide
9905 * @cfg {String} method (Optional) @hide
9908 * @cfg {Number} timeout (Optional) @hide
9911 * @cfg {Boolean} autoAbort (Optional) @hide
9915 * @cfg {Boolean} disableCaching (Optional) @hide
9919 * @property disableCaching
9920 * True to add a unique cache-buster param to GET requests. (defaults to true)
9925 * The default URL to be used for requests to the server. (defaults to undefined)
9926 * If the server receives all requests through one URL, setting this once is easier than
9927 * entering it on every request.
9931 * @property extraParams
9932 * An object containing properties which are used as extra parameters to each request made
9933 * by this object (defaults to undefined). Session information and other data that you need
9934 * to pass with each request are commonly put here.
9938 * @property defaultHeaders
9939 * An object containing request headers which are added to each request made by this object
9940 * (defaults to undefined).
9945 * The default HTTP method to be used for requests. Note that this is case-sensitive and
9946 * should be all caps (defaults to undefined; if not set but params are present will use
9947 * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
9952 * The timeout in milliseconds to be used for requests. (defaults to 30000)
9957 * @property autoAbort
9958 * Whether a new request should abort any pending requests. (defaults to false)
9964 * Serialize the passed form into a url encoded string
9965 * @param {String/HTMLElement} form
9968 serializeForm : function(form){
9969 return Ext.lib.Ajax.serializeForm(form);
9973 * @class Ext.Updater
9974 * @extends Ext.util.Observable
9975 * Provides AJAX-style update capabilities for Element objects. Updater can be used to {@link #update}
9976 * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9977 * {@link Ext.Element Element} on a specific interval.<br><br>
9980 * var el = Ext.get("foo"); // Get Ext.Element object
9981 * var mgr = el.getUpdater();
9983 url: "http://myserver.com/index.php",
9990 * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
9992 * // or directly (returns the same Updater instance)
9993 * var mgr = new Ext.Updater("myElementId");
9994 * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
9995 * mgr.on("update", myFcnNeedsToKnow);
9997 * // short handed call directly from the element object
9998 * Ext.get("foo").load({
10001 params: "param1=foo&param2=bar",
10002 text: "Loading Foo..."
10006 * Create new Updater directly.
10007 * @param {Mixed} el The element to update
10008 * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
10009 * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
10011 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
10013 var BEFOREUPDATE = "beforeupdate",
10015 FAILURE = "failure";
10018 function processSuccess(response){
10020 me.transaction = null;
10021 if (response.argument.form && response.argument.reset) {
10022 try { // put in try/catch since some older FF releases had problems with this
10023 response.argument.form.reset();
10026 if (me.loadScripts) {
10027 me.renderer.render(me.el, response, me,
10028 updateComplete.createDelegate(me, [response]));
10030 me.renderer.render(me.el, response, me);
10031 updateComplete.call(me, response);
10036 function updateComplete(response, type, success){
10037 this.fireEvent(type || UPDATE, this.el, response);
10038 if(Ext.isFunction(response.argument.callback)){
10039 response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
10044 function processFailure(response){
10045 updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
10049 constructor: function(el, forceNew){
10052 if(!forceNew && el.updateManager){
10053 return el.updateManager;
10056 * The Element object
10057 * @type Ext.Element
10061 * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
10064 me.defaultUrl = null;
10068 * @event beforeupdate
10069 * Fired before an update is made, return false from your handler and the update is cancelled.
10070 * @param {Ext.Element} el
10071 * @param {String/Object/Function} url
10072 * @param {String/Object} params
10077 * Fired after successful update is made.
10078 * @param {Ext.Element} el
10079 * @param {Object} oResponseObject The response Object
10084 * Fired on update failure.
10085 * @param {Ext.Element} el
10086 * @param {Object} oResponseObject The response Object
10091 Ext.apply(me, Ext.Updater.defaults);
10093 * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
10094 * @property sslBlankUrl
10098 * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
10099 * @property disableCaching
10103 * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
10104 * @property indicatorText
10108 * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
10109 * @property showLoadIndicator
10113 * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
10114 * @property timeout
10118 * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
10119 * @property loadScripts
10124 * Transaction object of the current executing transaction, or null if there is no active transaction.
10126 me.transaction = null;
10128 * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
10131 me.refreshDelegate = me.refresh.createDelegate(me);
10133 * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
10136 me.updateDelegate = me.update.createDelegate(me);
10138 * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
10141 me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
10144 * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
10146 me.renderer = me.renderer || me.getDefaultRenderer();
10148 Ext.Updater.superclass.constructor.call(me);
10152 * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10153 * @param {Object} renderer The object implementing the render() method
10155 setRenderer : function(renderer){
10156 this.renderer = renderer;
10160 * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10163 getRenderer : function(){
10164 return this.renderer;
10168 * This is an overrideable method which returns a reference to a default
10169 * renderer class if none is specified when creating the Ext.Updater.
10170 * Defaults to {@link Ext.Updater.BasicRenderer}
10172 getDefaultRenderer: function() {
10173 return new Ext.Updater.BasicRenderer();
10177 * Sets the default URL used for updates.
10178 * @param {String/Function} defaultUrl The url or a function to call to get the url
10180 setDefaultUrl : function(defaultUrl){
10181 this.defaultUrl = defaultUrl;
10185 * Get the Element this Updater is bound to
10186 * @return {Ext.Element} The element
10188 getEl : function(){
10193 * Performs an <b>asynchronous</b> request, updating this element with the response.
10194 * If params are specified it uses POST, otherwise it uses GET.<br><br>
10195 * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
10196 * will not have been fully updated when the function returns. To post-process the returned
10197 * data, use the callback option, or an <b><code>update</code></b> event handler.
10198 * @param {Object} options A config object containing any of the following options:<ul>
10199 * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
10200 * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
10201 * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
10202 * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
10203 * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
10204 * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
10205 * string, or as an object containing properties which represent parameters,
10206 * or as a function, which returns such an object.</p></li>
10207 * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
10208 * any <script> tags embedded in the response text will be extracted
10209 * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
10210 * the callback will be called <i>after</i> the execution of the scripts.</p></li>
10211 * <li>callback : <b>Function</b><p class="sub-desc">A function to
10212 * be called when the response from the server arrives. The following
10213 * parameters are passed:<ul>
10214 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10215 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10216 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
10217 * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
10219 * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
10220 * to execute the callback (The callback's <code>this</code> reference.) If the
10221 * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
10222 * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
10223 * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
10224 * calls. To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
10225 * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
10226 * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
10227 * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
10228 * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...'). To replace the entire div, not
10229 * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
10230 * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
10231 * requests, this option causes an extra, auto-generated parameter to be appended to the request
10232 * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
10237 url: "your-url.php",
10238 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
10239 callback: yourFunction,
10240 scope: yourObject, //(optional scope)
10243 text: "Loading...",
10245 scripts: false // Save time by avoiding RegExp execution.
10249 update : function(url, params, callback, discardUrl){
10254 if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
10255 if(Ext.isObject(url)){ // must be config object
10258 params = params || cfg.params;
10259 callback = callback || cfg.callback;
10260 discardUrl = discardUrl || cfg.discardUrl;
10261 callerScope = cfg.scope;
10262 if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
10263 if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
10264 if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
10265 if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
10270 me.defaultUrl = url;
10272 if(Ext.isFunction(url)){
10273 url = url.call(me);
10276 var o = Ext.apply({}, {
10278 params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
10279 success: processSuccess,
10280 failure: processFailure,
10282 callback: undefined,
10283 timeout: (me.timeout*1000),
10284 disableCaching: me.disableCaching,
10289 "callback": callback,
10290 "scope": callerScope || window,
10295 me.transaction = Ext.Ajax.request(o);
10300 * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
10301 * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
10302 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
10303 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
10304 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
10305 * DOM <code><form></code> element temporarily modified to have its
10306 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
10307 * to a dynamically generated, hidden <code><iframe></code> which is inserted into the document
10308 * but removed after the return data has been gathered.</p>
10309 * <p>Be aware that file upload packets, sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>
10310 * and some server technologies (notably JEE) may require some custom processing in order to
10311 * retrieve parameter names and parameter values from the packet content.</p>
10312 * @param {String/HTMLElement} form The form Id or form element
10313 * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
10314 * @param {Boolean} reset (optional) Whether to try to reset the form after the update
10315 * @param {Function} callback (optional) Callback when transaction is complete. The following
10316 * parameters are passed:<ul>
10317 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10318 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10319 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
10321 formUpdate : function(form, url, reset, callback){
10323 if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
10324 if(Ext.isFunction(url)){
10325 url = url.call(me);
10327 form = Ext.getDom(form);
10328 me.transaction = Ext.Ajax.request({
10331 success: processSuccess,
10332 failure: processFailure,
10334 timeout: (me.timeout*1000),
10338 "callback": callback,
10342 me.showLoading.defer(1, me);
10347 * Set this element to auto refresh. Can be canceled by calling {@link #stopAutoRefresh}.
10348 * @param {Number} interval How often to update (in seconds).
10349 * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
10350 * supported by {@link #load}, or a function to call to get the url (defaults to the last used url). Note that while
10351 * the url used in a load call can be reused by this method, other load config options will not be reused and must be
10352 * sepcified as part of a config object passed as this paramter if needed.
10353 * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
10354 * "¶m1=1¶m2=2" or as an object {param1: 1, param2: 2}
10355 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10356 * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
10358 startAutoRefresh : function(interval, url, params, callback, refreshNow){
10361 me.update(url || me.defaultUrl, params, callback, true);
10363 if(me.autoRefreshProcId){
10364 clearInterval(me.autoRefreshProcId);
10366 me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
10370 * Stop auto refresh on this element.
10372 stopAutoRefresh : function(){
10373 if(this.autoRefreshProcId){
10374 clearInterval(this.autoRefreshProcId);
10375 delete this.autoRefreshProcId;
10380 * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
10382 isAutoRefreshing : function(){
10383 return !!this.autoRefreshProcId;
10387 * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
10388 * method may be overridden to perform a custom action while this Updater is actively updating its contents.
10390 showLoading : function(){
10391 if(this.showLoadIndicator){
10392 this.el.dom.innerHTML = this.indicatorText;
10397 * Aborts the currently executing transaction, if any.
10399 abort : function(){
10400 if(this.transaction){
10401 Ext.Ajax.abort(this.transaction);
10406 * Returns true if an update is in progress, otherwise false.
10407 * @return {Boolean}
10409 isUpdating : function(){
10410 return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
10414 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
10415 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10417 refresh : function(callback){
10418 if(this.defaultUrl){
10419 this.update(this.defaultUrl, null, callback, true);
10426 * @class Ext.Updater.defaults
10427 * The defaults collection enables customizing the default properties of Updater
10429 Ext.Updater.defaults = {
10431 * Timeout for requests or form posts in seconds (defaults to 30 seconds).
10436 * True to append a unique parameter to GET requests to disable caching (defaults to false).
10439 disableCaching : false,
10441 * Whether or not to show {@link #indicatorText} during loading (defaults to true).
10444 showLoadIndicator : true,
10446 * Text for loading indicator (defaults to '<div class="loading-indicator">Loading...</div>').
10449 indicatorText : '<div class="loading-indicator">Loading...</div>',
10451 * True to process scripts by default (defaults to false).
10454 loadScripts : false,
10456 * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
10459 sslBlankUrl : Ext.SSL_SECURE_URL
10464 * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
10466 * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
10467 * @param {Mixed} el The element to update
10468 * @param {String} url The url
10469 * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
10470 * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
10471 * example: {disableCaching:true, indicatorText: "Loading data..."}
10474 * @member Ext.Updater
10476 Ext.Updater.updateElement = function(el, url, params, options){
10477 var um = Ext.get(el).getUpdater();
10478 Ext.apply(um, options);
10479 um.update(url, params, options ? options.callback : null);
10483 * @class Ext.Updater.BasicRenderer
10484 * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
10485 * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
10486 * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
10488 Ext.Updater.BasicRenderer = function(){};
10490 Ext.Updater.BasicRenderer.prototype = {
10492 * This method is called when an Ajax response is received, and an Element needs updating.
10493 * @param {Ext.Element} el The element being rendered
10494 * @param {Object} xhr The XMLHttpRequest object
10495 * @param {Updater} updateManager The calling update manager
10496 * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
10498 render : function(el, response, updateManager, callback){
10499 el.update(response.responseText, updateManager.loadScripts, callback);
10504 * The date parsing and formatting syntax contains a subset of
10505 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
10506 * supported will provide results equivalent to their PHP versions.
10508 * The following is a list of all currently supported formats:
10510 Format Description Example returned values
10511 ------ ----------------------------------------------------------------------- -----------------------
10512 d Day of the month, 2 digits with leading zeros 01 to 31
10513 D A short textual representation of the day of the week Mon to Sun
10514 j Day of the month without leading zeros 1 to 31
10515 l A full textual representation of the day of the week Sunday to Saturday
10516 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
10517 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
10518 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
10519 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
10520 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
10521 F A full textual representation of a month, such as January or March January to December
10522 m Numeric representation of a month, with leading zeros 01 to 12
10523 M A short textual representation of a month Jan to Dec
10524 n Numeric representation of a month, without leading zeros 1 to 12
10525 t Number of days in the given month 28 to 31
10526 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
10527 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
10528 belongs to the previous or next year, that year is used instead)
10529 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
10530 y A two digit representation of a year Examples: 99 or 03
10531 a Lowercase Ante meridiem and Post meridiem am or pm
10532 A Uppercase Ante meridiem and Post meridiem AM or PM
10533 g 12-hour format of an hour without leading zeros 1 to 12
10534 G 24-hour format of an hour without leading zeros 0 to 23
10535 h 12-hour format of an hour with leading zeros 01 to 12
10536 H 24-hour format of an hour with leading zeros 00 to 23
10537 i Minutes, with leading zeros 00 to 59
10538 s Seconds, with leading zeros 00 to 59
10539 u Decimal fraction of a second Examples:
10540 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
10541 100 (i.e. 0.100s) or
10542 999 (i.e. 0.999s) or
10543 999876543210 (i.e. 0.999876543210s)
10544 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
10545 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
10546 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
10547 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
10550 1) If unspecified, the month / day defaults to the current month / day, 1991 or
10551 the time defaults to midnight, while the timezone defaults to the 1992-10 or
10552 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
10553 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
10554 are optional. 1995-07-18T17:21:28-02:00 or
10555 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
10556 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
10557 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
10558 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
10559 date-time granularity which are supported, or see 2000-02-13T21:25:33
10560 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
10561 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
10562 M$ Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
10563 \/Date(1238606590509+0800)\/
10566 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
10569 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
10571 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
10572 document.write(dt.format('Y-m-d')); // 2007-01-10
10573 document.write(dt.format('F j, Y, g:i a')); // January 10, 2007, 3:05 pm
10574 document.write(dt.format('l, \\t\\he jS \\of F Y h:i:s A')); // Wednesday, the 10th of January 2007 03:05:01 PM
10577 * Here are some standard date/time patterns that you might find helpful. They
10578 * are not part of the source of Date.js, but to use them you can simply copy this
10579 * block of code into any script that is included after Date.js and they will also become
10580 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
10583 ISO8601Long:"Y-m-d H:i:s",
10584 ISO8601Short:"Y-m-d",
10585 ShortDate: "n/j/Y",
10586 LongDate: "l, F d, Y",
10587 FullDateTime: "l, F d, Y g:i:s A",
10589 ShortTime: "g:i A",
10590 LongTime: "g:i:s A",
10591 SortableDateTime: "Y-m-d\\TH:i:s",
10592 UniversalSortableDateTime: "Y-m-d H:i:sO",
10599 var dt = new Date();
10600 document.write(dt.format(Date.patterns.ShortDate));
10602 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
10603 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
10607 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
10608 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
10609 * They generate precompiled functions from format patterns instead of parsing and
10610 * processing each pattern every time a date is formatted. These functions are available
10611 * on every Date object.
10617 * Global flag which determines if strict date parsing should be used.
10618 * Strict date parsing will not roll-over invalid dates, which is the
10619 * default behaviour of javascript Date objects.
10620 * (see {@link #parseDate} for more information)
10621 * Defaults to <tt>false</tt>.
10625 Date.useStrict = false;
10628 // create private copy of Ext's String.format() method
10629 // - to remove unnecessary dependency
10630 // - to resolve namespace conflict with M$-Ajax's implementation
10631 function xf(format) {
10632 var args = Array.prototype.slice.call(arguments, 1);
10633 return format.replace(/\{(\d+)\}/g, function(m, i) {
10640 Date.formatCodeToRegex = function(character, currentGroup) {
10641 // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
10642 var p = Date.parseCodes[character];
10645 p = typeof p == 'function'? p() : p;
10646 Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
10649 return p ? Ext.applyIf({
10650 c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
10654 s:Ext.escapeRe(character) // treat unrecognised characters as literals
10658 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
10659 var $f = Date.formatCodeToRegex;
10663 * <p>An object hash in which each property is a date parsing function. The property name is the
10664 * format string which that function parses.</p>
10665 * <p>This object is automatically populated with date parsing functions as
10666 * date formats are requested for Ext standard formatting strings.</p>
10667 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
10668 * may be used as a format string to {@link #parseDate}.<p>
10669 * <p>Example:</p><pre><code>
10670 Date.parseFunctions['x-date-format'] = myDateParser;
10672 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10673 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
10674 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
10675 * (i.e. prevent javascript Date "rollover") (The default must be false).
10676 * Invalid date strings should return null when parsed.</div></li>
10678 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
10679 * formatting function must be placed into the {@link #formatFunctions} property.
10680 * @property parseFunctions
10685 "M$": function(input, strict) {
10686 // note: the timezone offset is ignored since the M$ Ajax server sends
10687 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
10688 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
10689 var r = (input || '').match(re);
10690 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
10696 * <p>An object hash in which each property is a date formatting function. The property name is the
10697 * format string which corresponds to the produced formatted date string.</p>
10698 * <p>This object is automatically populated with date formatting functions as
10699 * date formats are requested for Ext standard formatting strings.</p>
10700 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
10701 * may be used as a format string to {@link #format}. Example:</p><pre><code>
10702 Date.formatFunctions['x-date-format'] = myDateFormatter;
10704 * <p>A formatting function should return a string representation of the passed Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10705 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
10707 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
10708 * parsing function must be placed into the {@link #parseFunctions} property.
10709 * @property formatFunctions
10715 // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
10716 return '\\/Date(' + this.getTime() + ')\\/';
10723 * Date interval constant
10730 * Date interval constant
10737 * Date interval constant
10743 /** Date interval constant
10750 * Date interval constant
10757 * Date interval constant
10764 * Date interval constant
10771 * <p>An object hash containing default date values used during date parsing.</p>
10772 * <p>The following properties are available:<div class="mdetail-params"><ul>
10773 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
10774 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
10775 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
10776 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
10777 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
10778 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
10779 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
10781 * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
10782 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
10783 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
10784 * It is the responsiblity of the developer to account for this.</b></p>
10787 // set default day value to the first day of the month
10788 Date.defaults.d = 1;
10790 // parse a February date string containing only year and month values.
10791 // setting the default day value to 1 prevents weird date rollover issues
10792 // when attempting to parse the following date string on, for example, March 31st 2009.
10793 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
10795 * @property defaults
10802 * An array of textual day names.
10803 * Override these values for international dates.
10807 'SundayInYourLang',
10808 'MondayInYourLang',
10826 * An array of textual month names.
10827 * Override these values for international dates.
10830 Date.monthNames = [
10855 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
10856 * Override these values for international dates.
10859 Date.monthNumbers = {
10860 'ShortJanNameInYourLang':0,
10861 'ShortFebNameInYourLang':1,
10884 * Get the short month name for the given month number.
10885 * Override this function for international dates.
10886 * @param {Number} month A zero-based javascript month number.
10887 * @return {String} The short month name.
10890 getShortMonthName : function(month) {
10891 return Date.monthNames[month].substring(0, 3);
10895 * Get the short day name for the given day number.
10896 * Override this function for international dates.
10897 * @param {Number} day A zero-based javascript day number.
10898 * @return {String} The short day name.
10901 getShortDayName : function(day) {
10902 return Date.dayNames[day].substring(0, 3);
10906 * Get the zero-based javascript month number for the given short/full month name.
10907 * Override this function for international dates.
10908 * @param {String} name The short/full month name.
10909 * @return {Number} The zero-based javascript month number.
10912 getMonthNumber : function(name) {
10913 // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
10914 return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
10918 * The base format-code to formatting-function hashmap used by the {@link #format} method.
10919 * Formatting functions are strings (or functions which return strings) which
10920 * will return the appropriate value when evaluated in the context of the Date object
10921 * from which the {@link #format} method is called.
10922 * Add to / override these mappings for custom date formatting.
10923 * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
10926 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
10927 (new Date()).format("X"); // returns the current day of the month
10933 d: "String.leftPad(this.getDate(), 2, '0')",
10934 D: "Date.getShortDayName(this.getDay())", // get localised short day name
10935 j: "this.getDate()",
10936 l: "Date.dayNames[this.getDay()]",
10937 N: "(this.getDay() ? this.getDay() : 7)",
10938 S: "this.getSuffix()",
10939 w: "this.getDay()",
10940 z: "this.getDayOfYear()",
10941 W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10942 F: "Date.monthNames[this.getMonth()]",
10943 m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10944 M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10945 n: "(this.getMonth() + 1)",
10946 t: "this.getDaysInMonth()",
10947 L: "(this.isLeapYear() ? 1 : 0)",
10948 o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10949 Y: "this.getFullYear()",
10950 y: "('' + this.getFullYear()).substring(2, 4)",
10951 a: "(this.getHours() < 12 ? 'am' : 'pm')",
10952 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10953 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10954 G: "this.getHours()",
10955 h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10956 H: "String.leftPad(this.getHours(), 2, '0')",
10957 i: "String.leftPad(this.getMinutes(), 2, '0')",
10958 s: "String.leftPad(this.getSeconds(), 2, '0')",
10959 u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10960 O: "this.getGMTOffset()",
10961 P: "this.getGMTOffset(true)",
10962 T: "this.getTimezone()",
10963 Z: "(this.getTimezoneOffset() * -60)",
10965 c: function() { // ISO-8601 -- GMT format
10966 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10967 var e = c.charAt(i);
10968 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10970 return code.join(" + ");
10973 c: function() { // ISO-8601 -- UTC format
10975 "this.getUTCFullYear()", "'-'",
10976 "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10977 "String.leftPad(this.getUTCDate(), 2, '0')",
10979 "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10980 "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10981 "String.leftPad(this.getUTCSeconds(), 2, '0')",
10987 U: "Math.round(this.getTime() / 1000)"
10991 * Checks if the passed Date parameters will cause a javascript Date "rollover".
10992 * @param {Number} year 4-digit year
10993 * @param {Number} month 1-based month-of-year
10994 * @param {Number} day Day of month
10995 * @param {Number} hour (optional) Hour
10996 * @param {Number} minute (optional) Minute
10997 * @param {Number} second (optional) Second
10998 * @param {Number} millisecond (optional) Millisecond
10999 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
11002 isValid : function(y, m, d, h, i, s, ms) {
11009 var dt = new Date(y, m - 1, d, h, i, s, ms);
11011 return y == dt.getFullYear() &&
11012 m == dt.getMonth() + 1 &&
11013 d == dt.getDate() &&
11014 h == dt.getHours() &&
11015 i == dt.getMinutes() &&
11016 s == dt.getSeconds() &&
11017 ms == dt.getMilliseconds();
11021 * Parses the passed string using the specified date format.
11022 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
11023 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
11024 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
11025 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
11026 * Keep in mind that the input date string must precisely match the specified format string
11027 * in order for the parse operation to be successful (failed parse operations return a null value).
11028 * <p>Example:</p><pre><code>
11029 //dt = Fri May 25 2007 (current date)
11030 var dt = new Date();
11032 //dt = Thu May 25 2006 (today's month/day in 2006)
11033 dt = Date.parseDate("2006", "Y");
11035 //dt = Sun Jan 15 2006 (all date parts specified)
11036 dt = Date.parseDate("2006-01-15", "Y-m-d");
11038 //dt = Sun Jan 15 2006 15:20:01
11039 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
11041 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
11042 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
11044 * @param {String} input The raw date string.
11045 * @param {String} format The expected date string format.
11046 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
11047 (defaults to false). Invalid date strings will return null when parsed.
11048 * @return {Date} The parsed Date.
11051 parseDate : function(input, format, strict) {
11052 var p = Date.parseFunctions;
11053 if (p[format] == null) {
11054 Date.createParser(format);
11056 return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
11060 getFormatCode : function(character) {
11061 var f = Date.formatCodes[character];
11064 f = typeof f == 'function'? f() : f;
11065 Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
11068 // note: unknown characters are treated as literals
11069 return f || ("'" + String.escape(character) + "'");
11073 createFormat : function(format) {
11078 for (var i = 0; i < format.length; ++i) {
11079 ch = format.charAt(i);
11080 if (!special && ch == "\\") {
11082 } else if (special) {
11084 code.push("'" + String.escape(ch) + "'");
11086 code.push(Date.getFormatCode(ch))
11089 Date.formatFunctions[format] = new Function("return " + code.join('+'));
11093 createParser : function() {
11095 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
11096 "def = Date.defaults,",
11097 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
11102 "if(u != null){", // i.e. unix time is defined
11103 "v = new Date(u * 1000);", // give top priority to UNIX time
11105 // create Date object representing midnight of the current day;
11106 // this will provide us with our date defaults
11107 // (note: clearTime() handles Daylight Saving Time automatically)
11108 "dt = (new Date()).clearTime();",
11110 // date calculations (note: these calculations create a dependency on Ext.num())
11111 "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
11112 "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
11113 "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
11115 // time calculations (note: these calculations create a dependency on Ext.num())
11116 "h = Ext.num(h, Ext.num(def.h, dt.getHours()));",
11117 "i = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
11118 "s = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
11119 "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
11121 "if(z >= 0 && y >= 0){",
11122 // both the year and zero-based day of year are defined and >= 0.
11123 // these 2 values alone provide sufficient info to create a full date object
11125 // create Date object representing January 1st for the given year
11126 "v = new Date(y, 0, 1, h, i, s, ms);",
11128 // then add day of year, checking for Date "rollover" if necessary
11129 "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
11130 "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
11131 "v = null;", // invalid date, so return null
11133 // plain old Date object
11134 "v = new Date(y, m, d, h, i, s, ms);",
11140 // favour UTC offset over GMT offset
11142 // reset to UTC, then add offset
11143 "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
11145 // reset to GMT, then add offset
11146 "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
11153 return function(format) {
11154 var regexNum = Date.parseRegexes.length,
11161 for (var i = 0; i < format.length; ++i) {
11162 ch = format.charAt(i);
11163 if (!special && ch == "\\") {
11165 } else if (special) {
11167 regex.push(String.escape(ch));
11169 var obj = $f(ch, currentGroup);
11170 currentGroup += obj.g;
11172 if (obj.g && obj.c) {
11178 Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$");
11179 Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
11187 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
11188 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
11189 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
11193 c:"d = parseInt(results[{0}], 10);\n",
11194 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
11198 c:"d = parseInt(results[{0}], 10);\n",
11199 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
11202 for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
11206 s:"(?:" + a.join("|") +")"
11213 s:"(?:" + Date.dayNames.join("|") + ")"
11219 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
11224 s:"(?:st|nd|rd|th)"
11229 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
11233 c:"z = parseInt(results[{0}], 10);\n",
11234 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
11239 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
11244 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
11245 s:"(" + Date.monthNames.join("|") + ")"
11249 for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
11250 return Ext.applyIf({
11251 s:"(" + a.join("|") + ")"
11256 c:"m = parseInt(results[{0}], 10) - 1;\n",
11257 s:"(\\d{2})" // month number with leading zeros (01 - 12)
11261 c:"m = parseInt(results[{0}], 10) - 1;\n",
11262 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
11267 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
11279 c:"y = parseInt(results[{0}], 10);\n",
11280 s:"(\\d{4})" // 4-digit year
11284 c:"var ty = parseInt(results[{0}], 10);\n"
11285 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
11290 c:"if (results[{0}] == 'am') {\n"
11291 + "if (!h || h == 12) { h = 0; }\n"
11292 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11297 c:"if (results[{0}] == 'AM') {\n"
11298 + "if (!h || h == 12) { h = 0; }\n"
11299 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11307 c:"h = parseInt(results[{0}], 10);\n",
11308 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
11315 c:"h = parseInt(results[{0}], 10);\n",
11316 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
11320 c:"i = parseInt(results[{0}], 10);\n",
11321 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
11325 c:"s = parseInt(results[{0}], 10);\n",
11326 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
11330 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
11331 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11336 "o = results[{0}];",
11337 "var sn = o.substring(0,1),", // get + / - sign
11338 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11339 "mn = o.substring(3,5) % 60;", // get minutes
11340 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + String.leftPad(hr, 2, '0') + String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
11342 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
11347 "o = results[{0}];",
11348 "var sn = o.substring(0,1),", // get + / - sign
11349 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11350 "mn = o.substring(4,6) % 60;", // get minutes
11351 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + String.leftPad(hr, 2, '0') + String.leftPad(mn, 2, '0')) : null;\n" // -12hrs <= GMT offset <= 14hrs
11353 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
11358 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
11362 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
11363 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
11364 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
11369 $f("Y", 1), // year
11370 $f("m", 2), // month
11372 $f("h", 4), // hour
11373 $f("i", 5), // minute
11374 $f("s", 6), // second
11375 {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11376 {c:[ // allow either "Z" (i.e. UTC) or "-0530" or "+08:00" (i.e. UTC offset) timezone delimiters. assumes local timezone if no timezone is specified
11377 "if(results[8]) {", // timezone specified
11378 "if(results[8] == 'Z'){",
11380 "}else if (results[8].indexOf(':') > -1){",
11381 $f("P", 8).c, // timezone offset with colon separator
11383 $f("O", 8).c, // timezone offset without colon separator
11389 for (var i = 0, l = arr.length; i < l; ++i) {
11390 calc.push(arr[i].c);
11397 arr[0].s, // year (required)
11398 "(?:", "-", arr[1].s, // month (optional)
11399 "(?:", "-", arr[2].s, // day (optional)
11401 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
11402 arr[3].s, ":", arr[4].s, // hour AND minute, delimited by a single colon (optional). MUST be preceded by either a "T" or a single blank space
11403 "(?::", arr[5].s, ")?", // seconds (optional)
11404 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
11405 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
11414 c:"u = parseInt(results[{0}], 10);\n",
11415 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
11422 Ext.apply(Date.prototype, {
11424 dateFormat : function(format) {
11425 if (Date.formatFunctions[format] == null) {
11426 Date.createFormat(format);
11428 return Date.formatFunctions[format].call(this);
11432 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
11434 * Note: The date string returned by the javascript Date object's toString() method varies
11435 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
11436 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
11437 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
11438 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
11439 * from the GMT offset portion of the date string.
11440 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
11442 getTimezone : function() {
11443 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
11445 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
11446 // Safari : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone (same as FF)
11447 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
11448 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
11449 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
11451 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
11452 // step 1: (?:\((.*)\) -- find timezone in parentheses
11453 // step 2: ([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?) -- if nothing was found in step 1, find timezone from timezone offset portion of date string
11454 // step 3: remove all non uppercase characters found in step 1 and 2
11455 return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
11459 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
11460 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
11461 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
11463 getGMTOffset : function(colon) {
11464 return (this.getTimezoneOffset() > 0 ? "-" : "+")
11465 + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
11466 + (colon ? ":" : "")
11467 + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
11471 * Get the numeric day number of the year, adjusted for leap year.
11472 * @return {Number} 0 to 364 (365 in leap years).
11474 getDayOfYear: function() {
11477 m = this.getMonth(),
11480 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
11481 num += d.getDaysInMonth();
11483 return num + this.getDate() - 1;
11487 * Get the numeric ISO-8601 week number of the year.
11488 * (equivalent to the format specifier 'W', but without a leading zero).
11489 * @return {Number} 1 to 53
11491 getWeekOfYear : function() {
11492 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
11493 var ms1d = 864e5, // milliseconds in a day
11494 ms7d = 7 * ms1d; // milliseconds in a week
11496 return function() { // return a closure so constants get calculated only once
11497 var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
11498 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
11499 Wyr = new Date(AWN * ms7d).getUTCFullYear();
11501 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
11506 * Checks if the current date falls within a leap year.
11507 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
11509 isLeapYear : function() {
11510 var year = this.getFullYear();
11511 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
11515 * Get the first day of the current month, adjusted for leap year. The returned value
11516 * is the numeric day index within the week (0-6) which can be used in conjunction with
11517 * the {@link #monthNames} array to retrieve the textual day name.
11520 var dt = new Date('1/10/2007');
11521 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
11523 * @return {Number} The day number (0-6).
11525 getFirstDayOfMonth : function() {
11526 var day = (this.getDay() - (this.getDate() - 1)) % 7;
11527 return (day < 0) ? (day + 7) : day;
11531 * Get the last day of the current month, adjusted for leap year. The returned value
11532 * is the numeric day index within the week (0-6) which can be used in conjunction with
11533 * the {@link #monthNames} array to retrieve the textual day name.
11536 var dt = new Date('1/10/2007');
11537 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
11539 * @return {Number} The day number (0-6).
11541 getLastDayOfMonth : function() {
11542 return this.getLastDateOfMonth().getDay();
11547 * Get the date of the first day of the month in which this date resides.
11550 getFirstDateOfMonth : function() {
11551 return new Date(this.getFullYear(), this.getMonth(), 1);
11555 * Get the date of the last day of the month in which this date resides.
11558 getLastDateOfMonth : function() {
11559 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
11563 * Get the number of days in the current month, adjusted for leap year.
11564 * @return {Number} The number of days in the month.
11566 getDaysInMonth: function() {
11567 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
11569 return function() { // return a closure for efficiency
11570 var m = this.getMonth();
11572 return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
11577 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
11578 * @return {String} 'st, 'nd', 'rd' or 'th'.
11580 getSuffix : function() {
11581 switch (this.getDate()) {
11598 * Creates and returns a new Date instance with the exact same date value as the called instance.
11599 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
11600 * variable will also be changed. When the intention is to create a new variable that will not
11601 * modify the original instance, you should create a clone.
11603 * Example of correctly cloning a date:
11606 var orig = new Date('10/1/2006');
11609 document.write(orig); //returns 'Thu Oct 05 2006'!
11612 var orig = new Date('10/1/2006');
11613 var copy = orig.clone();
11615 document.write(orig); //returns 'Thu Oct 01 2006'
11617 * @return {Date} The new Date instance.
11619 clone : function() {
11620 return new Date(this.getTime());
11624 * Checks if the current date is affected by Daylight Saving Time (DST).
11625 * @return {Boolean} True if the current date is affected by DST.
11627 isDST : function() {
11628 // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
11629 // courtesy of @geoffrey.mcgill
11630 return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
11634 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
11635 * automatically adjusting for Daylight Saving Time (DST) where applicable.
11636 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
11637 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
11638 * @return {Date} this or the clone.
11640 clearTime : function(clone) {
11642 return this.clone().clearTime();
11645 // get current date before clearing time
11646 var d = this.getDate();
11650 this.setMinutes(0);
11651 this.setSeconds(0);
11652 this.setMilliseconds(0);
11654 if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
11655 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
11656 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
11658 // increment hour until cloned date == current date
11659 for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
11662 this.setHours(c.getHours());
11669 * Provides a convenient method for performing basic date arithmetic. This method
11670 * does not modify the Date instance being called - it creates and returns
11671 * a new Date instance containing the resulting date value.
11676 var dt = new Date('10/29/2006').add(Date.DAY, 5);
11677 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
11679 // Negative values will be subtracted:
11680 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
11681 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
11683 // You can even chain several calls together in one line:
11684 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
11685 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
11688 * @param {String} interval A valid date interval enum value.
11689 * @param {Number} value The amount to add to the current date.
11690 * @return {Date} The new Date instance.
11692 add : function(interval, value) {
11693 var d = this.clone();
11694 if (!interval || value === 0) return d;
11696 switch(interval.toLowerCase()) {
11698 d.setMilliseconds(this.getMilliseconds() + value);
11701 d.setSeconds(this.getSeconds() + value);
11704 d.setMinutes(this.getMinutes() + value);
11707 d.setHours(this.getHours() + value);
11710 d.setDate(this.getDate() + value);
11713 var day = this.getDate();
11715 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
11718 d.setMonth(this.getMonth() + value);
11721 d.setFullYear(this.getFullYear() + value);
11728 * Checks if this date falls on or between the given start and end dates.
11729 * @param {Date} start Start date
11730 * @param {Date} end End date
11731 * @return {Boolean} true if this date falls on or between the given start and end dates.
11733 between : function(start, end) {
11734 var t = this.getTime();
11735 return start.getTime() <= t && t <= end.getTime();
11741 * Formats a date given the supplied format string.
11742 * @param {String} format The format string.
11743 * @return {String} The formatted date.
11746 Date.prototype.format = Date.prototype.dateFormat;
11750 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
11751 Ext.apply(Date.prototype, {
11752 _xMonth : Date.prototype.setMonth,
11753 _xDate : Date.prototype.setDate,
11755 // Bug in Safari 1.3, 2.0 (WebKit build < 420)
11756 // Date.setMonth does not work consistently if iMonth is not 0-11
11757 setMonth : function(num) {
11759 var n = Math.ceil(-num),
11760 back_year = Math.ceil(n / 12),
11761 month = (n % 12) ? 12 - n % 12 : 0;
11763 this.setFullYear(this.getFullYear() - back_year);
11765 return this._xMonth(month);
11767 return this._xMonth(num);
11771 // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
11772 // The parameter for Date.setDate() is converted to a signed byte integer in Safari
11773 // http://brianary.blogspot.com/2006/03/safari-date-bug.html
11774 setDate : function(d) {
11775 // use setTime() to workaround setDate() bug
11776 // subtract current day of month in milliseconds, then add desired day of month in milliseconds
11777 return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
11784 /* Some basic Date tests... (requires Firebug)
11786 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
11787 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
11790 console.group('Standard Date.parseDate() Tests');
11791 console.log('Date.parseDate("2009-01-05T11:38:56", "c") = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
11792 console.log('Date.parseDate("2009-02-04T12:37:55.001000", "c") = %o', Date.parseDate("2009-02-04T12:37:55.001000", "c")); // assumes browser's timezone setting
11793 console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c") = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
11794 console.log('Date.parseDate("2009-04-02T14:35:53.901000-0530", "c") = %o', Date.parseDate("2009-04-02T14:35:53.901000-0530", "c")); // GMT-0530
11795 console.log('Date.parseDate("2009-05-01T15:34:52,9876000+08:00", "c") = %o', Date.parseDate("2009-05-01T15:34:52,987600+08:00", "c")); // GMT+08:00
11796 console.groupEnd();
11798 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
11799 // -- accepts ALL 6 levels of date-time granularity
11800 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
11801 console.log('Date.parseDate("1997", "c") = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
11802 console.log('Date.parseDate("1997-07", "c") = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
11803 console.log('Date.parseDate("1997-07-16", "c") = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
11804 console.log('Date.parseDate("1997-07-16T19:20+01:00", "c") = %o', Date.parseDate("1997-07-16T19:20+01:00", "c")); // YYYY-MM-DDThh:mmTZD (e.g. 1997-07-16T19:20+01:00)
11805 console.log('Date.parseDate("1997-07-16T19:20:30+01:00", "c") = %o', Date.parseDate("1997-07-16T19:20:30+01:00", "c")); // YYYY-MM-DDThh:mm:ssTZD (e.g. 1997-07-16T19:20:30+01:00)
11806 console.log('Date.parseDate("1997-07-16T19:20:30.45+01:00", "c") = %o', Date.parseDate("1997-07-16T19:20:30.45+01:00", "c")); // YYYY-MM-DDThh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45+01:00)
11807 console.log('Date.parseDate("1997-07-16 19:20:30.45+01:00", "c") = %o', Date.parseDate("1997-07-16 19:20:30.45+01:00", "c")); // YYYY-MM-DD hh:mm:ss.sTZD (e.g. 1997-07-16T19:20:30.45+01:00)
11808 console.log('Date.parseDate("1997-13-16T19:20:30.45+01:00", "c", true)= %o', Date.parseDate("1997-13-16T19:20:30.45+01:00", "c", true)); // strict date parsing with invalid month value
11809 console.groupEnd();
11813 * @class Ext.util.MixedCollection
11814 * @extends Ext.util.Observable
11815 * A Collection class that maintains both numeric indexes and keys and exposes events.
11817 * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11818 * function should add function references to the collection. Defaults to
11820 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
11821 * and return the key value for that item. This is used when available to look up the key on items that
11822 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
11823 * equivalent to providing an implementation for the {@link #getKey} method.
11825 Ext.util.MixedCollection = function(allowFunctions, keyFn){
11833 * Fires when the collection is cleared.
11838 * Fires when an item is added to the collection.
11839 * @param {Number} index The index at which the item was added.
11840 * @param {Object} o The item added.
11841 * @param {String} key The key associated with the added item.
11846 * Fires when an item is replaced in the collection.
11847 * @param {String} key he key associated with the new added.
11848 * @param {Object} old The item being replaced.
11849 * @param {Object} new The new item.
11854 * Fires when an item is removed from the collection.
11855 * @param {Object} o The item being removed.
11856 * @param {String} key (optional) The key associated with the removed item.
11861 this.allowFunctions = allowFunctions === true;
11863 this.getKey = keyFn;
11865 Ext.util.MixedCollection.superclass.constructor.call(this);
11868 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
11871 * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11872 * function should add function references to the collection. Defaults to
11875 allowFunctions : false,
11878 * Adds an item to the collection. Fires the {@link #add} event when complete.
11879 * @param {String} key <p>The key to associate with the item, or the new item.</p>
11880 * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
11881 * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
11882 * the MixedCollection will be able to <i>derive</i> the key for the new item.
11883 * In this case just pass the new item in this parameter.</p>
11884 * @param {Object} o The item to add.
11885 * @return {Object} The item added.
11887 add : function(key, o){
11888 if(arguments.length == 1){
11890 key = this.getKey(o);
11892 if(typeof key != 'undefined' && key !== null){
11893 var old = this.map[key];
11894 if(typeof old != 'undefined'){
11895 return this.replace(key, o);
11900 this.items.push(o);
11901 this.keys.push(key);
11902 this.fireEvent('add', this.length-1, o, key);
11907 * MixedCollection has a generic way to fetch keys if you implement getKey. The default implementation
11908 * simply returns <b><code>item.id</code></b> but you can provide your own implementation
11909 * to return a different value as in the following examples:<pre><code>
11911 var mc = new Ext.util.MixedCollection();
11912 mc.add(someEl.dom.id, someEl);
11913 mc.add(otherEl.dom.id, otherEl);
11917 var mc = new Ext.util.MixedCollection();
11918 mc.getKey = function(el){
11924 // or via the constructor
11925 var mc = new Ext.util.MixedCollection(false, function(el){
11931 * @param {Object} item The item for which to find the key.
11932 * @return {Object} The key for the passed item.
11934 getKey : function(o){
11939 * Replaces an item in the collection. Fires the {@link #replace} event when complete.
11940 * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
11941 * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
11942 * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
11943 * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
11944 * with one having the same key value, then just pass the replacement item in this parameter.</p>
11945 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
11947 * @return {Object} The new item.
11949 replace : function(key, o){
11950 if(arguments.length == 1){
11952 key = this.getKey(o);
11954 var old = this.map[key];
11955 if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
11956 return this.add(key, o);
11958 var index = this.indexOfKey(key);
11959 this.items[index] = o;
11961 this.fireEvent('replace', key, old, o);
11966 * Adds all elements of an Array or an Object to the collection.
11967 * @param {Object/Array} objs An Object containing properties which will be added
11968 * to the collection, or an Array of values, each of which are added to the collection.
11969 * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
11970 * has been set to <tt>true</tt>.
11972 addAll : function(objs){
11973 if(arguments.length > 1 || Ext.isArray(objs)){
11974 var args = arguments.length > 1 ? arguments : objs;
11975 for(var i = 0, len = args.length; i < len; i++){
11979 for(var key in objs){
11980 if(this.allowFunctions || typeof objs[key] != 'function'){
11981 this.add(key, objs[key]);
11988 * Executes the specified function once for every item in the collection, passing the following arguments:
11989 * <div class="mdetail-params"><ul>
11990 * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
11991 * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
11992 * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
11994 * The function should return a boolean value. Returning false from the function will stop the iteration.
11995 * @param {Function} fn The function to execute for each item.
11996 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
11998 each : function(fn, scope){
11999 var items = [].concat(this.items); // each safe for removal
12000 for(var i = 0, len = items.length; i < len; i++){
12001 if(fn.call(scope || items[i], items[i], i, len) === false){
12008 * Executes the specified function once for every key in the collection, passing each
12009 * key, and its associated item as the first two parameters.
12010 * @param {Function} fn The function to execute for each item.
12011 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
12013 eachKey : function(fn, scope){
12014 for(var i = 0, len = this.keys.length; i < len; i++){
12015 fn.call(scope || window, this.keys[i], this.items[i], i, len);
12020 * Returns the first item in the collection which elicits a true return value from the
12021 * passed selection function.
12022 * @param {Function} fn The selection function to execute for each item.
12023 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
12024 * @return {Object} The first item in the collection which returned true from the selection function.
12026 find : function(fn, scope){
12027 for(var i = 0, len = this.items.length; i < len; i++){
12028 if(fn.call(scope || window, this.items[i], this.keys[i])){
12029 return this.items[i];
12036 * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
12037 * @param {Number} index The index to insert the item at.
12038 * @param {String} key The key to associate with the new item, or the item itself.
12039 * @param {Object} o (optional) If the second parameter was a key, the new item.
12040 * @return {Object} The item inserted.
12042 insert : function(index, key, o){
12043 if(arguments.length == 2){
12045 key = this.getKey(o);
12047 if(this.containsKey(key)){
12048 this.suspendEvents();
12049 this.removeKey(key);
12050 this.resumeEvents();
12052 if(index >= this.length){
12053 return this.add(key, o);
12056 this.items.splice(index, 0, o);
12057 if(typeof key != 'undefined' && key !== null){
12060 this.keys.splice(index, 0, key);
12061 this.fireEvent('add', index, o, key);
12066 * Remove an item from the collection.
12067 * @param {Object} o The item to remove.
12068 * @return {Object} The item removed or false if no item was removed.
12070 remove : function(o){
12071 return this.removeAt(this.indexOf(o));
12075 * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
12076 * @param {Number} index The index within the collection of the item to remove.
12077 * @return {Object} The item removed or false if no item was removed.
12079 removeAt : function(index){
12080 if(index < this.length && index >= 0){
12082 var o = this.items[index];
12083 this.items.splice(index, 1);
12084 var key = this.keys[index];
12085 if(typeof key != 'undefined'){
12086 delete this.map[key];
12088 this.keys.splice(index, 1);
12089 this.fireEvent('remove', o, key);
12096 * Removed an item associated with the passed key fom the collection.
12097 * @param {String} key The key of the item to remove.
12098 * @return {Object} The item removed or false if no item was removed.
12100 removeKey : function(key){
12101 return this.removeAt(this.indexOfKey(key));
12105 * Returns the number of items in the collection.
12106 * @return {Number} the number of items in the collection.
12108 getCount : function(){
12109 return this.length;
12113 * Returns index within the collection of the passed Object.
12114 * @param {Object} o The item to find the index of.
12115 * @return {Number} index of the item. Returns -1 if not found.
12117 indexOf : function(o){
12118 return this.items.indexOf(o);
12122 * Returns index within the collection of the passed key.
12123 * @param {String} key The key to find the index of.
12124 * @return {Number} index of the key.
12126 indexOfKey : function(key){
12127 return this.keys.indexOf(key);
12131 * Returns the item associated with the passed key OR index.
12132 * Key has priority over index. This is the equivalent
12133 * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
12134 * @param {String/Number} key The key or index of the item.
12135 * @return {Object} If the item is found, returns the item. If the item was not found, returns <tt>undefined</tt>.
12136 * If an item was found, but is a Class, returns <tt>null</tt>.
12138 item : function(key){
12139 var mk = this.map[key],
12140 item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
12141 return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12145 * Returns the item at the specified index.
12146 * @param {Number} index The index of the item.
12147 * @return {Object} The item at the specified index.
12149 itemAt : function(index){
12150 return this.items[index];
12154 * Returns the item associated with the passed key.
12155 * @param {String/Number} key The key of the item.
12156 * @return {Object} The item associated with the passed key.
12158 key : function(key){
12159 return this.map[key];
12163 * Returns true if the collection contains the passed Object as an item.
12164 * @param {Object} o The Object to look for in the collection.
12165 * @return {Boolean} True if the collection contains the Object as an item.
12167 contains : function(o){
12168 return this.indexOf(o) != -1;
12172 * Returns true if the collection contains the passed Object as a key.
12173 * @param {String} key The key to look for in the collection.
12174 * @return {Boolean} True if the collection contains the Object as a key.
12176 containsKey : function(key){
12177 return typeof this.map[key] != 'undefined';
12181 * Removes all items from the collection. Fires the {@link #clear} event when complete.
12183 clear : function(){
12188 this.fireEvent('clear');
12192 * Returns the first item in the collection.
12193 * @return {Object} the first item in the collection..
12195 first : function(){
12196 return this.items[0];
12200 * Returns the last item in the collection.
12201 * @return {Object} the last item in the collection..
12204 return this.items[this.length-1];
12209 * Performs the actual sorting based on a direction and a sorting function. Internally,
12210 * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
12211 * the sorted array data back into this.items and this.keys
12212 * @param {String} property Property to sort by ('key', 'value', or 'index')
12213 * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
12214 * @param {Function} fn (optional) Comparison function that defines the sort order.
12215 * Defaults to sorting by numeric value.
12217 _sort : function(property, dir, fn){
12219 dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
12221 //this is a temporary array used to apply the sorting function
12224 items = this.items;
12226 //default to a simple sorter function if one is not provided
12227 fn = fn || function(a, b) {
12231 //copy all the items into a temporary array, which we will sort
12232 for(i = 0, len = items.length; i < len; i++){
12240 //sort the temporary array
12241 c.sort(function(a, b){
12242 var v = fn(a[property], b[property]) * dsc;
12244 v = (a.index < b.index ? -1 : 1);
12249 //copy the temporary array back into the main this.items and this.keys objects
12250 for(i = 0, len = c.length; i < len; i++){
12251 items[i] = c[i].value;
12252 keys[i] = c[i].key;
12255 this.fireEvent('sort', this);
12259 * Sorts this collection by <b>item</b> value with the passed comparison function.
12260 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12261 * @param {Function} fn (optional) Comparison function that defines the sort order.
12262 * Defaults to sorting by numeric value.
12264 sort : function(dir, fn){
12265 this._sort('value', dir, fn);
12269 * Reorders each of the items based on a mapping from old index to new index. Internally this
12270 * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
12271 * @param {Object} mapping Mapping from old item index to new item index
12273 reorder: function(mapping) {
12274 this.suspendEvents();
12276 var items = this.items,
12278 length = items.length,
12282 //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
12283 for (oldIndex in mapping) {
12284 order[mapping[oldIndex]] = items[oldIndex];
12287 for (index = 0; index < length; index++) {
12288 if (mapping[index] == undefined) {
12289 remaining.push(items[index]);
12293 for (index = 0; index < length; index++) {
12294 if (order[index] == undefined) {
12295 order[index] = remaining.shift();
12300 this.addAll(order);
12302 this.resumeEvents();
12303 this.fireEvent('sort', this);
12307 * Sorts this collection by <b>key</b>s.
12308 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12309 * @param {Function} fn (optional) Comparison function that defines the sort order.
12310 * Defaults to sorting by case insensitive string.
12312 keySort : function(dir, fn){
12313 this._sort('key', dir, fn || function(a, b){
12314 var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
12315 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12320 * Returns a range of items in this collection
12321 * @param {Number} startIndex (optional) The starting index. Defaults to 0.
12322 * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
12323 * @return {Array} An array of items
12325 getRange : function(start, end){
12326 var items = this.items;
12327 if(items.length < 1){
12330 start = start || 0;
12331 end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
12334 for(i = start; i <= end; i++) {
12335 r[r.length] = items[i];
12338 for(i = start; i >= end; i--) {
12339 r[r.length] = items[i];
12346 * Filter the <i>objects</i> in this collection by a specific property.
12347 * Returns a new collection that has been filtered.
12348 * @param {String} property A property on your objects
12349 * @param {String/RegExp} value Either string that the property values
12350 * should start with or a RegExp to test against the property
12351 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
12352 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
12353 * @return {MixedCollection} The new filtered collection
12355 filter : function(property, value, anyMatch, caseSensitive){
12356 if(Ext.isEmpty(value, false)){
12357 return this.clone();
12359 value = this.createValueMatcher(value, anyMatch, caseSensitive);
12360 return this.filterBy(function(o){
12361 return o && value.test(o[property]);
12366 * Filter by a function. Returns a <i>new</i> collection that has been filtered.
12367 * The passed function will be called with each object in the collection.
12368 * If the function returns true, the value is included otherwise it is filtered.
12369 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12370 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12371 * @return {MixedCollection} The new filtered collection
12373 filterBy : function(fn, scope){
12374 var r = new Ext.util.MixedCollection();
12375 r.getKey = this.getKey;
12376 var k = this.keys, it = this.items;
12377 for(var i = 0, len = it.length; i < len; i++){
12378 if(fn.call(scope||this, it[i], k[i])){
12379 r.add(k[i], it[i]);
12386 * Finds the index of the first matching object in this collection by a specific property/value.
12387 * @param {String} property The name of a property on your objects.
12388 * @param {String/RegExp} value A string that the property values
12389 * should start with or a RegExp to test against the property.
12390 * @param {Number} start (optional) The index to start searching at (defaults to 0).
12391 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
12392 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
12393 * @return {Number} The matched index or -1
12395 findIndex : function(property, value, start, anyMatch, caseSensitive){
12396 if(Ext.isEmpty(value, false)){
12399 value = this.createValueMatcher(value, anyMatch, caseSensitive);
12400 return this.findIndexBy(function(o){
12401 return o && value.test(o[property]);
12406 * Find the index of the first matching object in this collection by a function.
12407 * If the function returns <i>true</i> it is considered a match.
12408 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
12409 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12410 * @param {Number} start (optional) The index to start searching at (defaults to 0).
12411 * @return {Number} The matched index or -1
12413 findIndexBy : function(fn, scope, start){
12414 var k = this.keys, it = this.items;
12415 for(var i = (start||0), len = it.length; i < len; i++){
12416 if(fn.call(scope||this, it[i], k[i])){
12424 * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
12425 * and by Ext.data.Store#filter
12427 * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
12428 * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
12429 * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
12430 * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
12432 createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
12433 if (!value.exec) { // not a regex
12434 var er = Ext.escapeRe;
12435 value = String(value);
12437 if (anyMatch === true) {
12440 value = '^' + er(value);
12441 if (exactMatch === true) {
12445 value = new RegExp(value, caseSensitive ? '' : 'i');
12451 * Creates a shallow copy of this collection
12452 * @return {MixedCollection}
12454 clone : function(){
12455 var r = new Ext.util.MixedCollection();
12456 var k = this.keys, it = this.items;
12457 for(var i = 0, len = it.length; i < len; i++){
12458 r.add(k[i], it[i]);
12460 r.getKey = this.getKey;
12465 * This method calls {@link #item item()}.
12466 * Returns the item associated with the passed key OR index. Key has priority
12467 * over index. This is the equivalent of calling {@link #key} first, then if
12468 * nothing matched calling {@link #itemAt}.
12469 * @param {String/Number} key The key or index of the item.
12470 * @return {Object} If the item is found, returns the item. If the item was
12471 * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
12472 * returns <tt>null</tt>.
12474 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;
12476 * @class Ext.util.JSON
12477 * Modified version of Douglas Crockford"s json.js that doesn"t
12478 * mess with the Object prototype
12479 * http://www.json.org/js.html
12482 Ext.util.JSON = new (function(){
12483 var useHasOwn = !!{}.hasOwnProperty,
12484 isNative = function() {
12485 var useNative = null;
12487 return function() {
12488 if (useNative === null) {
12489 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
12495 pad = function(n) {
12496 return n < 10 ? "0" + n : n;
12498 doDecode = function(json){
12499 return eval("(" + json + ')');
12501 doEncode = function(o){
12502 if(!Ext.isDefined(o) || o === null){
12504 }else if(Ext.isArray(o)){
12505 return encodeArray(o);
12506 }else if(Ext.isDate(o)){
12507 return Ext.util.JSON.encodeDate(o);
12508 }else if(Ext.isString(o)){
12509 return encodeString(o);
12510 }else if(typeof o == "number"){
12511 //don't use isNumber here, since finite checks happen inside isNumber
12512 return isFinite(o) ? String(o) : "null";
12513 }else if(Ext.isBoolean(o)){
12516 var a = ["{"], b, i, v;
12518 // don't encode DOM objects
12519 if(!o.getElementsByTagName){
12520 if(!useHasOwn || o.hasOwnProperty(i)) {
12522 switch (typeof v) {
12531 a.push(doEncode(i), ":",
12532 v === null ? "null" : doEncode(v));
12551 encodeString = function(s){
12552 if (/["\\\x00-\x1f]/.test(s)) {
12553 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12558 c = b.charCodeAt();
12560 Math.floor(c / 16).toString(16) +
12561 (c % 16).toString(16);
12564 return '"' + s + '"';
12566 encodeArray = function(o){
12567 var a = ["["], b, i, l = o.length, v;
12568 for (i = 0; i < l; i += 1) {
12570 switch (typeof v) {
12579 a.push(v === null ? "null" : Ext.util.JSON.encode(v));
12588 * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
12589 * <b>The returned value includes enclosing double quotation marks.</b></p>
12590 * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
12591 * <p>To override this:</p><pre><code>
12592 Ext.util.JSON.encodeDate = function(d) {
12593 return d.format('"Y-m-d"');
12596 * @param {Date} d The Date to encode
12597 * @return {String} The string literal to use in a JSON string.
12599 this.encodeDate = function(o){
12600 return '"' + o.getFullYear() + "-" +
12601 pad(o.getMonth() + 1) + "-" +
12602 pad(o.getDate()) + "T" +
12603 pad(o.getHours()) + ":" +
12604 pad(o.getMinutes()) + ":" +
12605 pad(o.getSeconds()) + '"';
12609 * Encodes an Object, Array or other value
12610 * @param {Mixed} o The variable to encode
12611 * @return {String} The JSON string
12613 this.encode = function() {
12615 return function(o) {
12617 // setup encoding function on first access
12618 ec = isNative() ? JSON.stringify : doEncode;
12626 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
12627 * @param {String} json The JSON string
12628 * @return {Object} The resulting object
12630 this.decode = function() {
12632 return function(json) {
12634 // setup decoding function on first access
12635 dc = isNative() ? JSON.parse : doDecode;
12643 * Shorthand for {@link Ext.util.JSON#encode}
12644 * @param {Mixed} o The variable to encode
12645 * @return {String} The JSON string
12649 Ext.encode = Ext.util.JSON.encode;
12651 * Shorthand for {@link Ext.util.JSON#decode}
12652 * @param {String} json The JSON string
12653 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
12654 * @return {Object} The resulting object
12658 Ext.decode = Ext.util.JSON.decode;
12660 * @class Ext.util.Format
12661 * Reusable data formatting functions
12664 Ext.util.Format = function(){
12665 var trimRe = /^\s+|\s+$/g,
12666 stripTagsRE = /<\/?[^>]+>/gi,
12667 stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
12668 nl2brRe = /\r?\n/g;
12672 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12673 * @param {String} value The string to truncate
12674 * @param {Number} length The maximum length to allow before truncating
12675 * @param {Boolean} word True to try to find a common work break
12676 * @return {String} The converted text
12678 ellipsis : function(value, len, word){
12679 if(value && value.length > len){
12681 var vs = value.substr(0, len - 2),
12682 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
12683 if(index == -1 || index < (len - 15)){
12684 return value.substr(0, len - 3) + "...";
12686 return vs.substr(0, index) + "...";
12689 return value.substr(0, len - 3) + "...";
12696 * Checks a reference and converts it to empty string if it is undefined
12697 * @param {Mixed} value Reference to check
12698 * @return {Mixed} Empty string if converted, otherwise the original value
12700 undef : function(value){
12701 return value !== undefined ? value : "";
12705 * Checks a reference and converts it to the default value if it's empty
12706 * @param {Mixed} value Reference to check
12707 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
12710 defaultValue : function(value, defaultValue){
12711 return value !== undefined && value !== '' ? value : defaultValue;
12715 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12716 * @param {String} value The string to encode
12717 * @return {String} The encoded text
12719 htmlEncode : function(value){
12720 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
12724 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12725 * @param {String} value The string to decode
12726 * @return {String} The decoded text
12728 htmlDecode : function(value){
12729 return !value ? value : String(value).replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&");
12733 * Trims any whitespace from either side of a string
12734 * @param {String} value The text to trim
12735 * @return {String} The trimmed text
12737 trim : function(value){
12738 return String(value).replace(trimRe, "");
12742 * Returns a substring from within an original string
12743 * @param {String} value The original text
12744 * @param {Number} start The start index of the substring
12745 * @param {Number} length The length of the substring
12746 * @return {String} The substring
12748 substr : function(value, start, length){
12749 return String(value).substr(start, length);
12753 * Converts a string to all lower case letters
12754 * @param {String} value The text to convert
12755 * @return {String} The converted text
12757 lowercase : function(value){
12758 return String(value).toLowerCase();
12762 * Converts a string to all upper case letters
12763 * @param {String} value The text to convert
12764 * @return {String} The converted text
12766 uppercase : function(value){
12767 return String(value).toUpperCase();
12771 * Converts the first character only of a string to upper case
12772 * @param {String} value The text to convert
12773 * @return {String} The converted text
12775 capitalize : function(value){
12776 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12780 call : function(value, fn){
12781 if(arguments.length > 2){
12782 var args = Array.prototype.slice.call(arguments, 2);
12783 args.unshift(value);
12784 return eval(fn).apply(window, args);
12786 return eval(fn).call(window, value);
12791 * Format a number as US currency
12792 * @param {Number/String} value The numeric value to format
12793 * @return {String} The formatted currency string
12795 usMoney : function(v){
12796 v = (Math.round((v-0)*100))/100;
12797 v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12799 var ps = v.split('.'),
12801 sub = ps[1] ? '.'+ ps[1] : '.00',
12802 r = /(\d+)(\d{3})/;
12803 while (r.test(whole)) {
12804 whole = whole.replace(r, '$1' + ',' + '$2');
12807 if(v.charAt(0) == '-'){
12808 return '-$' + v.substr(1);
12814 * Parse a value into a formatted date using the specified format pattern.
12815 * @param {String/Date} value The value to format (Strings must conform to the format expected by the javascript Date object's <a href="http://www.w3schools.com/jsref/jsref_parse.asp">parse()</a> method)
12816 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12817 * @return {String} The formatted date string
12819 date : function(v, format){
12823 if(!Ext.isDate(v)){
12824 v = new Date(Date.parse(v));
12826 return v.dateFormat(format || "m/d/Y");
12830 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12831 * @param {String} format Any valid date format string
12832 * @return {Function} The date formatting function
12834 dateRenderer : function(format){
12835 return function(v){
12836 return Ext.util.Format.date(v, format);
12841 * Strips all HTML tags
12842 * @param {Mixed} value The text from which to strip tags
12843 * @return {String} The stripped text
12845 stripTags : function(v){
12846 return !v ? v : String(v).replace(stripTagsRE, "");
12850 * Strips all script tags
12851 * @param {Mixed} value The text from which to strip script tags
12852 * @return {String} The stripped text
12854 stripScripts : function(v){
12855 return !v ? v : String(v).replace(stripScriptsRe, "");
12859 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
12860 * @param {Number/String} size The numeric value to format
12861 * @return {String} The formatted file size
12863 fileSize : function(size){
12865 return size + " bytes";
12866 } else if(size < 1048576) {
12867 return (Math.round(((size*10) / 1024))/10) + " KB";
12869 return (Math.round(((size*10) / 1048576))/10) + " MB";
12874 * It does simple math for use in a template, for example:<pre><code>
12875 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
12877 * @return {Function} A function that operates on the passed value.
12881 return function(v, a){
12883 fns[a] = new Function('v', 'return v ' + a + ';');
12890 * Rounds the passed number to the required decimal precision.
12891 * @param {Number/String} value The numeric value to round.
12892 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
12893 * @return {Number} The rounded value.
12895 round : function(value, precision) {
12896 var result = Number(value);
12897 if (typeof precision == 'number') {
12898 precision = Math.pow(10, precision);
12899 result = Math.round(value * precision) / precision;
12905 * Formats the number according to the format string.
12906 * <div style="margin-left:40px">examples (123456.789):
12907 * <div style="margin-left:10px">
12908 * 0 - (123456) show only digits, no precision<br>
12909 * 0.00 - (123456.78) show only digits, 2 precision<br>
12910 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
12911 * 0,000 - (123,456) show comma and digits, no precision<br>
12912 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
12913 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
12914 * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
12915 * For example: 0.000,00/i
12917 * @param {Number} v The number to format.
12918 * @param {String} format The way you would like to format this text.
12919 * @return {String} The formatted number.
12921 number: function(v, format) {
12925 v = Ext.num(v, NaN);
12935 if(format.substr(format.length - 2) == '/i'){
12936 format = format.substr(0, format.length - 2);
12942 var hasComma = format.indexOf(comma) != -1,
12943 psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
12945 if(1 < psplit.length){
12946 v = v.toFixed(psplit[1].length);
12947 }else if(2 < psplit.length){
12948 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
12953 var fnum = v.toString();
12955 psplit = fnum.split('.');
12958 var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;
12960 for (var i = 0; i < j; i += n) {
12964 parr[parr.length] = cnum.substr(i, n);
12967 fnum = parr.join(comma);
12969 fnum += dec + psplit[1];
12973 fnum = psplit[0] + dec + psplit[1];
12977 return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
12981 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
12982 * @param {String} format Any valid number format string for {@link #number}
12983 * @return {Function} The number formatting function
12985 numberRenderer : function(format){
12986 return function(v){
12987 return Ext.util.Format.number(v, format);
12992 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
12993 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
12994 * if the value is 0 or greater than 1.
12995 * @param {Number} value The value to compare against
12996 * @param {String} singular The singular form of the word
12997 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
12999 plural : function(v, s, p){
13000 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
13004 * Converts newline characters to the HTML tag <br/>
13005 * @param {String} The string value to format.
13006 * @return {String} The string with embedded <br/> tags in place of newlines.
13008 nl2br : function(v){
13009 return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
13014 * @class Ext.XTemplate
13015 * @extends Ext.Template
13016 * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
13017 * <li>Autofilling arrays using templates and sub-templates</li>
13018 * <li>Conditional processing with basic comparison operators</li>
13019 * <li>Basic math function support</li>
13020 * <li>Execute arbitrary inline code with special built-in template variables</li>
13021 * <li>Custom member functions</li>
13022 * <li>Many special tags and built-in operators that aren't defined as part of
13023 * the API, but are supported in the templates that can be created</li>
13025 * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
13026 * <li>{@link Ext.DataView}</li>
13027 * <li>{@link Ext.ListView}</li>
13028 * <li>{@link Ext.form.ComboBox}</li>
13029 * <li>{@link Ext.grid.TemplateColumn}</li>
13030 * <li>{@link Ext.grid.GroupingView}</li>
13031 * <li>{@link Ext.menu.Item}</li>
13032 * <li>{@link Ext.layout.MenuLayout}</li>
13033 * <li>{@link Ext.ColorPalette}</li>
13036 * <p>For example usage {@link #XTemplate see the constructor}.</p>
13039 * The {@link Ext.Template#Template Ext.Template constructor} describes
13040 * the acceptable parameters to pass to the constructor. The following
13041 * examples demonstrate all of the supported features.</p>
13043 * <div class="mdetail-params"><ul>
13045 * <li><b><u>Sample Data</u></b>
13046 * <div class="sub-desc">
13047 * <p>This is the data object used for reference in each code example:</p>
13050 name: 'Jack Slocum',
13051 title: 'Lead Developer',
13052 company: 'Ext JS, LLC',
13053 email: 'jack@extjs.com',
13054 address: '4 Red Bulls Drive',
13058 drinks: ['Red Bull', 'Coffee', 'Water'],
13060 name: 'Sara Grace',
13066 name: 'John James',
13075 * <li><b><u>Auto filling of arrays</u></b>
13076 * <div class="sub-desc">
13077 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
13078 * to process the provided data object:
13080 * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
13081 * repeating the template block inside the <tt>tpl</tt> tag for each item in the
13083 * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
13084 * <li>While processing an array, the special variable <tt>{#}</tt>
13085 * will provide the current array index + 1 (starts at 1, not 0).</li>
13089 <tpl <b>for</b>=".">...</tpl> // loop through array at root node
13090 <tpl <b>for</b>="foo">...</tpl> // loop through array at foo node
13091 <tpl <b>for</b>="foo.bar">...</tpl> // loop through array at foo.bar node
13093 * Using the sample data above:
13095 var tpl = new Ext.XTemplate(
13097 '<tpl <b>for</b>=".">', // process the data.kids node
13098 '<p>{#}. {name}</p>', // use current array index to autonumber
13101 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
13103 * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
13104 * to access specified members of the provided data object to populate the template:</p>
13106 var tpl = new Ext.XTemplate(
13107 '<p>Name: {name}</p>',
13108 '<p>Title: {title}</p>',
13109 '<p>Company: {company}</p>',
13111 '<tpl <b>for="kids"</b>>', // interrogate the kids property within the data
13112 '<p>{name}</p>',
13115 tpl.overwrite(panel.body, data); // pass the root node of the data object
13117 * <p>Flat arrays that contain values (and not objects) can be auto-rendered
13118 * using the special <b><tt>{.}</tt></b> variable inside a loop. This variable
13119 * will represent the value of the array at the current index:</p>
13121 var tpl = new Ext.XTemplate(
13122 '<p>{name}\'s favorite beverages:</p>',
13123 '<tpl for="drinks">',
13124 '<div> - {.}</div>',
13127 tpl.overwrite(panel.body, data);
13129 * <p>When processing a sub-template, for example while looping through a child array,
13130 * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
13132 var tpl = new Ext.XTemplate(
13133 '<p>Name: {name}</p>',
13135 '<tpl for="kids">',
13136 '<tpl if="age > 1">',
13137 '<p>{name}</p>',
13138 '<p>Dad: {<b>parent</b>.name}</p>',
13142 tpl.overwrite(panel.body, data);
13148 * <li><b><u>Conditional processing with basic comparison operators</u></b>
13149 * <div class="sub-desc">
13150 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
13151 * to provide conditional checks for deciding whether or not to render specific
13152 * parts of the template. Notes:<div class="sub-desc"><ul>
13153 * <li>Double quotes must be encoded if used within the conditional</li>
13154 * <li>There is no <tt>else</tt> operator — if needed, two opposite
13155 * <tt>if</tt> statements should be used.</li>
13158 <tpl if="age > 1 && age < 10">Child</tpl>
13159 <tpl if="age >= 10 && age < 18">Teenager</tpl>
13160 <tpl <b>if</b>="this.isGirl(name)">...</tpl>
13161 <tpl <b>if</b>="id==\'download\'">...</tpl>
13162 <tpl <b>if</b>="needsIcon"><img src="{icon}" class="{iconCls}"/></tpl>
13164 <tpl if="name == "Jack"">Hello</tpl>
13165 // encode " if it is part of the condition, e.g.
13166 <tpl if="name == &quot;Jack&quot;">Hello</tpl>
13168 * Using the sample data above:
13170 var tpl = new Ext.XTemplate(
13171 '<p>Name: {name}</p>',
13173 '<tpl for="kids">',
13174 '<tpl if="age > 1">',
13175 '<p>{name}</p>',
13179 tpl.overwrite(panel.body, data);
13185 * <li><b><u>Basic math support</u></b>
13186 * <div class="sub-desc">
13187 * <p>The following basic math operators may be applied directly on numeric
13188 * data values:</p><pre>
13193 var tpl = new Ext.XTemplate(
13194 '<p>Name: {name}</p>',
13196 '<tpl for="kids">',
13197 '<tpl if="age &gt; 1">', // <-- Note that the > is encoded
13198 '<p>{#}: {name}</p>', // <-- Auto-number each item
13199 '<p>In 5 Years: {age+5}</p>', // <-- Basic math
13200 '<p>Dad: {parent.name}</p>',
13204 tpl.overwrite(panel.body, data);
13210 * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
13211 * <div class="sub-desc">
13212 * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
13213 * in the scope of the template. There are some special variables available in that code:
13215 * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
13216 * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
13217 * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
13218 * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
13219 * loop you are in (1-based).</li>
13220 * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
13221 * of the array you are looping.</li>
13222 * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
13224 * This example demonstrates basic row striping using an inline code block and the
13225 * <tt>xindex</tt> variable:</p>
13227 var tpl = new Ext.XTemplate(
13228 '<p>Name: {name}</p>',
13229 '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
13231 '<tpl for="kids">',
13232 '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
13237 tpl.overwrite(panel.body, data);
13242 * <li><b><u>Template member functions</u></b>
13243 * <div class="sub-desc">
13244 * <p>One or more member functions can be specified in a configuration
13245 * object passed into the XTemplate constructor for more complex processing:</p>
13247 var tpl = new Ext.XTemplate(
13248 '<p>Name: {name}</p>',
13250 '<tpl for="kids">',
13251 '<tpl if="this.isGirl(name)">',
13252 '<p>Girl: {name} - {age}</p>',
13254 // use opposite if statement to simulate 'else' processing:
13255 '<tpl if="this.isGirl(name) == false">',
13256 '<p>Boy: {name} - {age}</p>',
13258 '<tpl if="this.isBaby(age)">',
13259 '<p>{name} is a baby!</p>',
13261 '</tpl></p>',
13263 // XTemplate configuration:
13265 disableFormats: true,
13266 // member functions:
13267 isGirl: function(name){
13268 return name == 'Sara Grace';
13270 isBaby: function(age){
13275 tpl.overwrite(panel.body, data);
13282 * @param {Mixed} config
13284 Ext.XTemplate = function(){
13285 Ext.XTemplate.superclass.constructor.apply(this, arguments);
13289 re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
13290 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
13291 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
13292 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
13300 RETURN = 'return ',
13301 WITHVALUES = 'with(values){ ';
13303 s = ['<tpl>', s, '</tpl>'].join('');
13305 while((m = s.match(re))){
13306 var m2 = m[0].match(nameRe),
13307 m3 = m[0].match(ifRe),
13308 m4 = m[0].match(execRe),
13312 name = m2 && m2[1] ? m2[1] : '';
13315 exp = m3 && m3[1] ? m3[1] : null;
13317 fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
13321 exp = m4 && m4[1] ? m4[1] : null;
13323 exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
13328 case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
13329 case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
13330 default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
13340 s = s.replace(m[0], '{xtpl'+ id + '}');
13343 for(var i = tpls.length-1; i >= 0; --i){
13344 me.compileTpl(tpls[i]);
13346 me.master = tpls[tpls.length-1];
13349 Ext.extend(Ext.XTemplate, Ext.Template, {
13351 re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
13353 codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
13356 applySubTemplate : function(id, values, parent, xindex, xcount){
13362 if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
13363 (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
13366 vs = t.target ? t.target.call(me, values, parent) : values;
13368 parent = t.target ? values : parent;
13369 if(t.target && Ext.isArray(vs)){
13370 for(var i = 0, len = vs.length; i < len; i++){
13371 buf[buf.length] = t.compiled.call(me, vs[i], parent, i+1, len);
13373 return buf.join('');
13375 return t.compiled.call(me, vs, parent, xindex, xcount);
13379 compileTpl : function(tpl){
13380 var fm = Ext.util.Format,
13381 useF = this.disableFormats !== true,
13382 sep = Ext.isGecko ? "+" : ",",
13385 function fn(m, name, format, args, math){
13386 if(name.substr(0, 4) == 'xtpl'){
13387 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
13392 }else if(name === '#'){
13394 }else if(name.indexOf('.') != -1){
13397 v = "values['" + name + "']";
13400 v = '(' + v + math + ')';
13402 if (format && useF) {
13403 args = args ? ',' + args : "";
13404 if(format.substr(0, 5) != "this."){
13405 format = "fm." + format + '(';
13407 format = 'this.call("'+ format.substr(5) + '", ';
13411 args= ''; format = "("+v+" === undefined ? '' : ";
13413 return "'"+ sep + format + v + args + ")"+sep+"'";
13416 function codeFn(m, code){
13417 // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
13418 return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
13421 // branched to use + in gecko and [].join() in others
13423 body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
13424 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
13427 body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
13428 body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
13429 body.push("'].join('');};");
13430 body = body.join('');
13437 * Returns an HTML fragment of this template with the specified values applied.
13438 * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
13439 * @return {String} The HTML fragment
13441 applyTemplate : function(values){
13442 return this.master.compiled.call(this, values, {}, 1, 1);
13446 * Compile the template to a function for optimized performance. Recommended if the template will be used frequently.
13447 * @return {Function} The compiled function
13449 compile : function(){return this;}
13456 * @property disableFormats
13466 * Alias for {@link #applyTemplate}
13467 * Returns an HTML fragment of this template with the specified values applied.
13468 * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
13469 * @return {String} The HTML fragment
13470 * @member Ext.XTemplate
13473 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
13476 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
13477 * @param {String/HTMLElement} el A DOM element or its id
13478 * @return {Ext.Template} The created template
13481 Ext.XTemplate.from = function(el){
13482 el = Ext.getDom(el);
13483 return new Ext.XTemplate(el.value || el.innerHTML);
13486 * @class Ext.util.CSS
13487 * Utility class for manipulating CSS rules
13490 Ext.util.CSS = function(){
13492 var doc = document;
13494 var camelRe = /(-[a-z])/gi;
13495 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13499 * Creates a stylesheet from a text blob of rules.
13500 * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
13501 * @param {String} cssText The text containing the css rules
13502 * @param {String} id An id to add to the stylesheet for later removal
13503 * @return {StyleSheet}
13505 createStyleSheet : function(cssText, id){
13507 var head = doc.getElementsByTagName("head")[0];
13508 var rules = doc.createElement("style");
13509 rules.setAttribute("type", "text/css");
13511 rules.setAttribute("id", id);
13514 head.appendChild(rules);
13515 ss = rules.styleSheet;
13516 ss.cssText = cssText;
13519 rules.appendChild(doc.createTextNode(cssText));
13521 rules.cssText = cssText;
13523 head.appendChild(rules);
13524 ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13526 this.cacheStyleSheet(ss);
13531 * Removes a style or link tag by id
13532 * @param {String} id The id of the tag
13534 removeStyleSheet : function(id){
13535 var existing = doc.getElementById(id);
13537 existing.parentNode.removeChild(existing);
13542 * Dynamically swaps an existing stylesheet reference for a new one
13543 * @param {String} id The id of an existing link tag to remove
13544 * @param {String} url The href of the new stylesheet to include
13546 swapStyleSheet : function(id, url){
13547 this.removeStyleSheet(id);
13548 var ss = doc.createElement("link");
13549 ss.setAttribute("rel", "stylesheet");
13550 ss.setAttribute("type", "text/css");
13551 ss.setAttribute("id", id);
13552 ss.setAttribute("href", url);
13553 doc.getElementsByTagName("head")[0].appendChild(ss);
13557 * Refresh the rule cache if you have dynamically added stylesheets
13558 * @return {Object} An object (hash) of rules indexed by selector
13560 refreshCache : function(){
13561 return this.getRules(true);
13565 cacheStyleSheet : function(ss){
13569 try{// try catch for cross domain access issue
13570 var ssRules = ss.cssRules || ss.rules;
13571 for(var j = ssRules.length-1; j >= 0; --j){
13572 rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
13578 * Gets all css rules for the document
13579 * @param {Boolean} refreshCache true to refresh the internal cache
13580 * @return {Object} An object (hash) of rules indexed by selector
13582 getRules : function(refreshCache){
13583 if(rules === null || refreshCache){
13585 var ds = doc.styleSheets;
13586 for(var i =0, len = ds.length; i < len; i++){
13588 this.cacheStyleSheet(ds[i]);
13596 * Gets an an individual CSS rule by selector(s)
13597 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13598 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13599 * @return {CSSRule} The CSS rule or null if one is not found
13601 getRule : function(selector, refreshCache){
13602 var rs = this.getRules(refreshCache);
13603 if(!Ext.isArray(selector)){
13604 return rs[selector.toLowerCase()];
13606 for(var i = 0; i < selector.length; i++){
13607 if(rs[selector[i]]){
13608 return rs[selector[i].toLowerCase()];
13616 * Updates a rule property
13617 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13618 * @param {String} property The css property
13619 * @param {String} value The new value for the property
13620 * @return {Boolean} true If a rule was found and updated
13622 updateRule : function(selector, property, value){
13623 if(!Ext.isArray(selector)){
13624 var rule = this.getRule(selector);
13626 rule.style[property.replace(camelRe, camelFn)] = value;
13630 for(var i = 0; i < selector.length; i++){
13631 if(this.updateRule(selector[i], property, value)){
13640 @class Ext.util.ClickRepeater
13641 @extends Ext.util.Observable
13643 A wrapper class which can be applied to any element. Fires a "click" event while the
13644 mouse is pressed. The interval between firings may be specified in the config but
13645 defaults to 20 milliseconds.
13647 Optionally, a CSS class may be applied to the element during the time it is pressed.
13649 @cfg {Mixed} el The element to act as a button.
13650 @cfg {Number} delay The initial delay before the repeating event begins firing.
13651 Similar to an autorepeat key delay.
13652 @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
13653 @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13654 @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13655 "interval" and "delay" are ignored.
13656 @cfg {Boolean} preventDefault True to prevent the default click event
13657 @cfg {Boolean} stopDefault True to stop the default click event
13660 2007-02-02 jvs Original code contributed by Nige "Animal" White
13661 2007-02-02 jvs Renamed to ClickRepeater
13662 2007-02-03 jvs Modifications for FF Mac and Safari
13665 @param {Mixed} el The element to listen on
13666 @param {Object} config
13668 Ext.util.ClickRepeater = function(el, config)
13670 this.el = Ext.get(el);
13671 this.el.unselectable();
13673 Ext.apply(this, config);
13678 * Fires when the mouse button is depressed.
13679 * @param {Ext.util.ClickRepeater} this
13684 * Fires on a specified interval during the time the element is pressed.
13685 * @param {Ext.util.ClickRepeater} this
13690 * Fires when the mouse key is released.
13691 * @param {Ext.util.ClickRepeater} this
13696 if(!this.disabled){
13697 this.disabled = true;
13701 // allow inline handler
13703 this.on("click", this.handler, this.scope || this);
13706 Ext.util.ClickRepeater.superclass.constructor.call(this);
13709 Ext.extend(Ext.util.ClickRepeater, Ext.util.Observable, {
13712 preventDefault : true,
13713 stopDefault : false,
13717 * Enables the repeater and allows events to fire.
13719 enable: function(){
13721 this.el.on('mousedown', this.handleMouseDown, this);
13723 this.el.on('dblclick', this.handleDblClick, this);
13725 if(this.preventDefault || this.stopDefault){
13726 this.el.on('click', this.eventOptions, this);
13729 this.disabled = false;
13733 * Disables the repeater and stops events from firing.
13735 disable: function(/* private */ force){
13736 if(force || !this.disabled){
13737 clearTimeout(this.timer);
13738 if(this.pressClass){
13739 this.el.removeClass(this.pressClass);
13741 Ext.getDoc().un('mouseup', this.handleMouseUp, this);
13742 this.el.removeAllListeners();
13744 this.disabled = true;
13748 * Convenience function for setting disabled/enabled by boolean.
13749 * @param {Boolean} disabled
13751 setDisabled: function(disabled){
13752 this[disabled ? 'disable' : 'enable']();
13755 eventOptions: function(e){
13756 if(this.preventDefault){
13757 e.preventDefault();
13759 if(this.stopDefault){
13765 destroy : function() {
13766 this.disable(true);
13767 Ext.destroy(this.el);
13768 this.purgeListeners();
13771 handleDblClick : function(){
13772 clearTimeout(this.timer);
13775 this.fireEvent("mousedown", this);
13776 this.fireEvent("click", this);
13780 handleMouseDown : function(){
13781 clearTimeout(this.timer);
13783 if(this.pressClass){
13784 this.el.addClass(this.pressClass);
13786 this.mousedownTime = new Date();
13788 Ext.getDoc().on("mouseup", this.handleMouseUp, this);
13789 this.el.on("mouseout", this.handleMouseOut, this);
13791 this.fireEvent("mousedown", this);
13792 this.fireEvent("click", this);
13794 // Do not honor delay or interval if acceleration wanted.
13795 if (this.accelerate) {
13798 this.timer = this.click.defer(this.delay || this.interval, this);
13802 click : function(){
13803 this.fireEvent("click", this);
13804 this.timer = this.click.defer(this.accelerate ?
13805 this.easeOutExpo(this.mousedownTime.getElapsed(),
13809 this.interval, this);
13812 easeOutExpo : function (t, b, c, d) {
13813 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
13817 handleMouseOut : function(){
13818 clearTimeout(this.timer);
13819 if(this.pressClass){
13820 this.el.removeClass(this.pressClass);
13822 this.el.on("mouseover", this.handleMouseReturn, this);
13826 handleMouseReturn : function(){
13827 this.el.un("mouseover", this.handleMouseReturn, this);
13828 if(this.pressClass){
13829 this.el.addClass(this.pressClass);
13835 handleMouseUp : function(){
13836 clearTimeout(this.timer);
13837 this.el.un("mouseover", this.handleMouseReturn, this);
13838 this.el.un("mouseout", this.handleMouseOut, this);
13839 Ext.getDoc().un("mouseup", this.handleMouseUp, this);
13840 this.el.removeClass(this.pressClass);
13841 this.fireEvent("mouseup", this);
13844 * @class Ext.KeyNav
13845 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
13846 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13847 * way to implement custom navigation schemes for any UI component.</p>
13848 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13849 * pageUp, pageDown, del, home, end. Usage:</p>
13851 var nav = new Ext.KeyNav("my-element", {
13852 "left" : function(e){
13853 this.moveLeft(e.ctrlKey);
13855 "right" : function(e){
13856 this.moveRight(e.ctrlKey);
13858 "enter" : function(e){
13865 * @param {Mixed} el The element to bind to
13866 * @param {Object} config The config
13868 Ext.KeyNav = function(el, config){
13869 this.el = Ext.get(el);
13870 Ext.apply(this, config);
13871 if(!this.disabled){
13872 this.disabled = true;
13877 Ext.KeyNav.prototype = {
13879 * @cfg {Boolean} disabled
13880 * True to disable this KeyNav instance (defaults to false)
13884 * @cfg {String} defaultEventAction
13885 * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key. Valid values are
13886 * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
13887 * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
13889 defaultEventAction: "stopEvent",
13891 * @cfg {Boolean} forceKeyDown
13892 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
13893 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13894 * handle keydown instead of keypress.
13896 forceKeyDown : false,
13899 relay : function(e){
13900 var k = e.getKey();
13901 var h = this.keyToHandler[k];
13903 if(this.doRelay(e, this[h], h) !== true){
13904 e[this.defaultEventAction]();
13910 doRelay : function(e, h, hname){
13911 return h.call(this.scope || this, e);
13914 // possible handlers
13928 // quick lookup hash
13944 stopKeyUp: function(e) {
13945 var k = e.getKey();
13947 if (k >= 37 && k <= 40) {
13948 // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
13949 // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
13955 * Destroy this KeyNav (this is the same as calling disable).
13957 destroy: function(){
13962 * Enable this KeyNav
13964 enable: function() {
13965 if (this.disabled) {
13966 if (Ext.isSafari2) {
13967 // call stopKeyUp() on "keyup" event
13968 this.el.on('keyup', this.stopKeyUp, this);
13971 this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13972 this.disabled = false;
13977 * Disable this KeyNav
13979 disable: function() {
13980 if (!this.disabled) {
13981 if (Ext.isSafari2) {
13982 // remove "keyup" event handler
13983 this.el.un('keyup', this.stopKeyUp, this);
13986 this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13987 this.disabled = true;
13992 * Convenience function for setting disabled/enabled by boolean.
13993 * @param {Boolean} disabled
13995 setDisabled : function(disabled){
13996 this[disabled ? "disable" : "enable"]();
14000 isKeydown: function(){
14001 return this.forceKeyDown || Ext.EventManager.useKeydown;
14005 * @class Ext.KeyMap
14006 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14007 * The constructor accepts the same config object as defined by {@link #addBinding}.
14008 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14009 * combination it will call the function with this signature (if the match is a multi-key
14010 * combination the callback will still be called only once): (String key, Ext.EventObject e)
14011 * A KeyMap can also handle a string representation of keys.<br />
14014 // map one key by key code
14015 var map = new Ext.KeyMap("my-element", {
14016 key: 13, // or Ext.EventObject.ENTER
14021 // map multiple keys to one action by string
14022 var map = new Ext.KeyMap("my-element", {
14028 // map multiple keys to multiple actions by strings and array of codes
14029 var map = new Ext.KeyMap("my-element", [
14032 fn: function(){ alert("Return was pressed"); }
14035 fn: function(){ alert('a, b or c was pressed'); }
14040 fn: function(){ alert('Control + shift + tab was pressed.'); }
14044 * <b>Note: A KeyMap starts enabled</b>
14046 * @param {Mixed} el The element to bind to
14047 * @param {Object} config The config (see {@link #addBinding})
14048 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14050 Ext.KeyMap = function(el, config, eventName){
14051 this.el = Ext.get(el);
14052 this.eventName = eventName || "keydown";
14053 this.bindings = [];
14055 this.addBinding(config);
14060 Ext.KeyMap.prototype = {
14062 * True to stop the event from bubbling and prevent the default browser action if the
14063 * key was handled by the KeyMap (defaults to false)
14069 * Add a new binding to this KeyMap. The following config object properties are supported:
14071 Property Type Description
14072 ---------- --------------- ----------------------------------------------------------------------
14073 key String/Array A single keycode or an array of keycodes to handle
14074 shift Boolean True to handle key only when shift is pressed, False to handle the key only when shift is not pressed (defaults to undefined)
14075 ctrl Boolean True to handle key only when ctrl is pressed, False to handle the key only when ctrl is not pressed (defaults to undefined)
14076 alt Boolean True to handle key only when alt is pressed, False to handle the key only when alt is not pressed (defaults to undefined)
14077 handler Function The function to call when KeyMap finds the expected key combination
14078 fn Function Alias of handler (for backwards-compatibility)
14079 scope Object The scope of the callback function
14080 stopEvent Boolean True to stop the event from bubbling and prevent the default browser action if the key was handled by the KeyMap (defaults to false)
14086 var map = new Ext.KeyMap(document, {
14087 key: Ext.EventObject.ENTER,
14092 //Add a new binding to the existing KeyMap later
14100 * @param {Object/Array} config A single KeyMap config or an array of configs
14102 addBinding : function(config){
14103 if(Ext.isArray(config)){
14104 Ext.each(config, function(c){
14105 this.addBinding(c);
14109 var keyCode = config.key,
14110 fn = config.fn || config.handler,
14111 scope = config.scope;
14113 if (config.stopEvent) {
14114 this.stopEvent = config.stopEvent;
14117 if(typeof keyCode == "string"){
14119 var keyString = keyCode.toUpperCase();
14120 for(var j = 0, len = keyString.length; j < len; j++){
14121 ks.push(keyString.charCodeAt(j));
14125 var keyArray = Ext.isArray(keyCode);
14127 var handler = function(e){
14128 if(this.checkModifiers(config, e)){
14129 var k = e.getKey();
14131 for(var i = 0, len = keyCode.length; i < len; i++){
14132 if(keyCode[i] == k){
14133 if(this.stopEvent){
14136 fn.call(scope || window, k, e);
14142 if(this.stopEvent){
14145 fn.call(scope || window, k, e);
14150 this.bindings.push(handler);
14154 checkModifiers: function(config, e){
14155 var val, key, keys = ['shift', 'ctrl', 'alt'];
14156 for (var i = 0, len = keys.length; i < len; ++i){
14159 if(!(val === undefined || (val === e[key + 'Key']))){
14167 * Shorthand for adding a single key listener
14168 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14169 * following options:
14170 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14171 * @param {Function} fn The function to call
14172 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
14174 on : function(key, fn, scope){
14175 var keyCode, shift, ctrl, alt;
14176 if(typeof key == "object" && !Ext.isArray(key)){
14195 handleKeyDown : function(e){
14196 if(this.enabled){ //just in case
14197 var b = this.bindings;
14198 for(var i = 0, len = b.length; i < len; i++){
14199 b[i].call(this, e);
14205 * Returns true if this KeyMap is enabled
14206 * @return {Boolean}
14208 isEnabled : function(){
14209 return this.enabled;
14213 * Enables this KeyMap
14215 enable: function(){
14217 this.el.on(this.eventName, this.handleKeyDown, this);
14218 this.enabled = true;
14223 * Disable this KeyMap
14225 disable: function(){
14227 this.el.removeListener(this.eventName, this.handleKeyDown, this);
14228 this.enabled = false;
14233 * Convenience function for setting disabled/enabled by boolean.
14234 * @param {Boolean} disabled
14236 setDisabled : function(disabled){
14237 this[disabled ? "disable" : "enable"]();
14240 * @class Ext.util.TextMetrics
14241 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14242 * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
14243 * should not contain any HTML, otherwise it may not be measured correctly.
14246 Ext.util.TextMetrics = function(){
14250 * Measures the size of the specified text
14251 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14252 * that can affect the size of the rendered text
14253 * @param {String} text The text to measure
14254 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14255 * in order to accurately measure the text height
14256 * @return {Object} An object containing the text's size {width: (width), height: (height)}
14258 measure : function(el, text, fixedWidth){
14260 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
14263 shared.setFixedWidth(fixedWidth || 'auto');
14264 return shared.getSize(text);
14268 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
14269 * the overhead of multiple calls to initialize the style properties on each measurement.
14270 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14271 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14272 * in order to accurately measure the text height
14273 * @return {Ext.util.TextMetrics.Instance} instance The new instance
14275 createInstance : function(el, fixedWidth){
14276 return Ext.util.TextMetrics.Instance(el, fixedWidth);
14281 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14282 var ml = new Ext.Element(document.createElement('div'));
14283 document.body.appendChild(ml.dom);
14284 ml.position('absolute');
14285 ml.setLeftTop(-1000, -1000);
14289 ml.setWidth(fixedWidth);
14294 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14295 * Returns the size of the specified text based on the internal element's style and width properties
14296 * @param {String} text The text to measure
14297 * @return {Object} An object containing the text's size {width: (width), height: (height)}
14299 getSize : function(text){
14301 var s = ml.getSize();
14307 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14308 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14309 * that can affect the size of the rendered text
14310 * @param {String/HTMLElement} el The element, dom node or id
14312 bind : function(el){
14314 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
14319 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14320 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
14321 * to set a fixed width in order to accurately measure the text height.
14322 * @param {Number} width The width to set on the element
14324 setFixedWidth : function(width){
14325 ml.setWidth(width);
14329 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14330 * Returns the measured width of the specified text
14331 * @param {String} text The text to measure
14332 * @return {Number} width The width in pixels
14334 getWidth : function(text){
14335 ml.dom.style.width = 'auto';
14336 return this.getSize(text).width;
14340 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14341 * Returns the measured height of the specified text. For multiline text, be sure to call
14342 * {@link #setFixedWidth} if necessary.
14343 * @param {String} text The text to measure
14344 * @return {Number} height The height in pixels
14346 getHeight : function(text){
14347 return this.getSize(text).height;
14351 instance.bind(bindTo);
14356 Ext.Element.addMethods({
14358 * Returns the width in pixels of the passed text, or the width of the text in this Element.
14359 * @param {String} text The text to measure. Defaults to the innerHTML of the element.
14360 * @param {Number} min (Optional) The minumum value to return.
14361 * @param {Number} max (Optional) The maximum value to return.
14362 * @return {Number} The text width in pixels.
14363 * @member Ext.Element getTextWidth
14365 getTextWidth : function(text, min, max){
14366 return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
14370 * @class Ext.util.Cookies
14371 * Utility class for managing and interacting with cookies.
14374 Ext.util.Cookies = {
14376 * Create a cookie with the specified name and value. Additional settings
14377 * for the cookie may be optionally specified (for example: expiration,
14378 * access restriction, SSL).
14379 * @param {String} name The name of the cookie to set.
14380 * @param {Mixed} value The value to set for the cookie.
14381 * @param {Object} expires (Optional) Specify an expiration date the
14382 * cookie is to persist until. Note that the specified Date object will
14383 * be converted to Greenwich Mean Time (GMT).
14384 * @param {String} path (Optional) Setting a path on the cookie restricts
14385 * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>).
14386 * @param {String} domain (Optional) Setting a domain restricts access to
14387 * pages on a given domain (typically used to allow cookie access across
14388 * subdomains). For example, "extjs.com" will create a cookie that can be
14389 * accessed from any subdomain of extjs.com, including www.extjs.com,
14390 * support.extjs.com, etc.
14391 * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
14392 * should only be accessible via SSL on a page using the HTTPS protocol.
14393 * Defaults to <tt>false</tt>. Note that this will only work if the page
14394 * calling this code uses the HTTPS protocol, otherwise the cookie will be
14395 * created with default options.
14397 set : function(name, value){
14398 var argv = arguments;
14399 var argc = arguments.length;
14400 var expires = (argc > 2) ? argv[2] : null;
14401 var path = (argc > 3) ? argv[3] : '/';
14402 var domain = (argc > 4) ? argv[4] : null;
14403 var secure = (argc > 5) ? argv[5] : false;
14404 document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
14408 * Retrieves cookies that are accessible by the current page. If a cookie
14409 * does not exist, <code>get()</code> returns <tt>null</tt>. The following
14410 * example retrieves the cookie called "valid" and stores the String value
14411 * in the variable <tt>validStatus</tt>.
14413 * var validStatus = Ext.util.Cookies.get("valid");
14415 * @param {String} name The name of the cookie to get
14416 * @return {Mixed} Returns the cookie value for the specified name;
14417 * null if the cookie name does not exist.
14419 get : function(name){
14420 var arg = name + "=";
14421 var alen = arg.length;
14422 var clen = document.cookie.length;
14427 if(document.cookie.substring(i, j) == arg){
14428 return Ext.util.Cookies.getCookieVal(j);
14430 i = document.cookie.indexOf(" ", i) + 1;
14439 * Removes a cookie with the provided name from the browser
14440 * if found by setting its expiration date to sometime in the past.
14441 * @param {String} name The name of the cookie to remove
14443 clear : function(name){
14444 if(Ext.util.Cookies.get(name)){
14445 document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
14451 getCookieVal : function(offset){
14452 var endstr = document.cookie.indexOf(";", offset);
14454 endstr = document.cookie.length;
14456 return unescape(document.cookie.substring(offset, endstr));
14459 * Framework-wide error-handler. Developers can override this method to provide
14460 * custom exception-handling. Framework errors will often extend from the base
14462 * @param {Object/Error} e The thrown exception object.
14464 Ext.handleError = function(e) {
14471 * <p>A base error class. Future implementations are intended to provide more
14472 * robust error handling throughout the framework (<b>in the debug build only</b>)
14473 * to check for common errors and problems. The messages issued by this class
14474 * will aid error checking. Error checks will be automatically removed in the
14475 * production build so that performance is not negatively impacted.</p>
14476 * <p>Some sample messages currently implemented:</p><pre>
14477 "DataProxy attempted to execute an API-action but found an undefined
14478 url / function. Please review your Proxy url/api-configuration."
14480 "Could not locate your "root" property in your server response.
14481 Please review your JsonReader config to ensure the config-property
14482 "root" matches the property your server-response. See the JsonReader
14483 docs for additional assistance."
14485 * <p>An example of the code used for generating error messages:</p><pre><code>
14494 function generateError(data) {
14495 throw new Ext.Error('foo-error', data);
14498 * @param {String} message
14500 Ext.Error = function(message) {
14501 // Try to read the message from Ext.Error.lang
14502 this.message = (this.lang[message]) ? this.lang[message] : message;
14505 Ext.Error.prototype = new Error();
14506 Ext.apply(Ext.Error.prototype, {
14507 // protected. Extensions place their error-strings here.
14515 getName : function() {
14522 getMessage : function() {
14523 return this.message;
14529 toJson : function() {
14530 return Ext.encode(this);