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){
170 if(typeof o == "string"){
172 } else if (Ext.isArray(o)) {
173 for (var i=0; i < o.length; i++) {
175 b += createHtml(o[i]);
179 b += '<' + (o.tag = o.tag || 'div');
182 if(!confRe.test(attr)){
183 if (typeof val == "object") {
184 b += ' ' + attr + '="';
186 b += key + ':' + val[key] + ';';
190 b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
194 // Now either just close the tag or try to add children and close the tag.
195 if (emptyTags.test(o.tag)) {
199 if ((cn = o.children || o.cn)) {
204 b += '</' + o.tag + '>';
210 function ieTable(depth, s, h, e){
211 tempTableEl.innerHTML = [s, h, e].join('');
218 // If the result is multiple siblings, then encapsulate them into one fragment.
219 if(ns = el.nextSibling){
220 var df = document.createDocumentFragment();
233 * Nasty code for IE's broken table implementation
235 function insertIntoTable(tag, where, el, html) {
239 tempTableEl = tempTableEl || document.createElement('div');
241 if(tag == 'td' && (where == afterbegin || where == beforeend) ||
242 !tableElRe.test(tag) && (where == beforebegin || where == afterend)) {
245 before = where == beforebegin ? el :
246 where == afterend ? el.nextSibling :
247 where == afterbegin ? el.firstChild : null;
249 if (where == beforebegin || where == afterend) {
253 if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
254 node = ieTable(4, trs, html, tre);
255 } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
256 (tag == 'tr' && (where == beforebegin || where == afterend))) {
257 node = ieTable(3, tbs, html, tbe);
259 node = ieTable(2, ts, html, te);
261 el.insertBefore(node, before);
268 * Returns the markup for the passed Element(s) config.
269 * @param {Object} o The DOM object spec (and children)
272 markup : function(o){
273 return createHtml(o);
277 * Applies a style specification to an element.
278 * @param {String/HTMLElement} el The element to apply styles to
279 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
280 * a function which returns such a specification.
282 applyStyles : function(el, styles){
287 if (typeof styles == "function") {
288 styles = styles.call();
290 if (typeof styles == "string") {
291 while ((matches = cssRe.exec(styles))) {
292 el.setStyle(matches[1], matches[2]);
294 } else if (typeof styles == "object") {
301 * Inserts an HTML fragment into the DOM.
302 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
303 * @param {HTMLElement} el The context element
304 * @param {String} html The HTML fragment
305 * @return {HTMLElement} The new node
307 insertHtml : function(where, el, html){
316 where = where.toLowerCase();
317 // add these here because they are used in both branches of the condition.
318 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
319 hash[afterend] = ['AfterEnd', 'nextSibling'];
321 if (el.insertAdjacentHTML) {
322 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
325 // add these two to the hash.
326 hash[afterbegin] = ['AfterBegin', 'firstChild'];
327 hash[beforeend] = ['BeforeEnd', 'lastChild'];
328 if ((hashVal = hash[where])) {
329 el.insertAdjacentHTML(hashVal[0], html);
330 return el[hashVal[1]];
333 range = el.ownerDocument.createRange();
334 setStart = 'setStart' + (endRe.test(where) ? 'After' : 'Before');
337 frag = range.createContextualFragment(html);
338 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
339 return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
341 rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
343 range[setStart](el[rangeEl]);
344 frag = range.createContextualFragment(html);
345 if(where == afterbegin){
346 el.insertBefore(frag, el.firstChild);
348 el.appendChild(frag);
356 throw 'Illegal insertion point -> "' + where + '"';
360 * Creates new DOM element(s) and inserts them before el.
361 * @param {Mixed} el The context element
362 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
363 * @param {Boolean} returnElement (optional) true to return a Ext.Element
364 * @return {HTMLElement/Ext.Element} The new node
366 insertBefore : function(el, o, returnElement){
367 return doInsert(el, o, returnElement, beforebegin);
371 * Creates new DOM element(s) and inserts them after el.
372 * @param {Mixed} el The context element
373 * @param {Object} o The DOM object spec (and children)
374 * @param {Boolean} returnElement (optional) true to return a Ext.Element
375 * @return {HTMLElement/Ext.Element} The new node
377 insertAfter : function(el, o, returnElement){
378 return doInsert(el, o, returnElement, afterend, 'nextSibling');
382 * Creates new DOM element(s) and inserts them as the first child of el.
383 * @param {Mixed} el The context element
384 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
385 * @param {Boolean} returnElement (optional) true to return a Ext.Element
386 * @return {HTMLElement/Ext.Element} The new node
388 insertFirst : function(el, o, returnElement){
389 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
393 * Creates new DOM element(s) and appends them to el.
394 * @param {Mixed} el The context element
395 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
396 * @param {Boolean} returnElement (optional) true to return a Ext.Element
397 * @return {HTMLElement/Ext.Element} The new node
399 append : function(el, o, returnElement){
400 return doInsert(el, o, returnElement, beforeend, '', true);
404 * Creates new DOM element(s) and overwrites the contents of el with them.
405 * @param {Mixed} el The context element
406 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
407 * @param {Boolean} returnElement (optional) true to return a Ext.Element
408 * @return {HTMLElement/Ext.Element} The new node
410 overwrite : function(el, o, returnElement){
412 el.innerHTML = createHtml(o);
413 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
416 createHtml : createHtml
421 * @class Ext.DomHelper
423 Ext.apply(Ext.DomHelper,
426 afterbegin = 'afterbegin',
427 afterend = 'afterend',
428 beforebegin = 'beforebegin',
429 beforeend = 'beforeend',
430 confRe = /tag|children|cn|html$/i;
433 function doInsert(el, o, returnElement, pos, sibling, append){
437 newNode = createDom(o, null);
439 el.appendChild(newNode);
441 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
444 newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
446 return returnElement ? Ext.get(newNode, true) : newNode;
451 function createDom(o, parentNode){
459 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
460 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
461 for (var i = 0, l = o.length; i < l; i++) {
464 } else if (typeof o == 'string') { // Allow a string as a child spec.
465 el = doc.createTextNode(o);
467 el = doc.createElement( o.tag || 'div' );
468 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
469 for (var attr in o) {
470 if(!confRe.test(attr)){
476 el.setAttribute(attr, val);
483 Ext.DomHelper.applyStyles(el, o.style);
485 if ((cn = o.children || o.cn)) {
488 el.innerHTML = o.html;
492 parentNode.appendChild(el);
499 * Creates a new Ext.Template from the DOM object spec.
500 * @param {Object} o The DOM object spec (and children)
501 * @return {Ext.Template} The new template
503 createTemplate : function(o){
504 var html = Ext.DomHelper.createHtml(o);
505 return new Ext.Template(html);
508 /** True to force the use of DOM instead of html fragments @type Boolean */
512 * Creates new DOM element(s) and inserts them before el.
513 * @param {Mixed} el The context element
514 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
515 * @param {Boolean} returnElement (optional) true to return a Ext.Element
516 * @return {HTMLElement/Ext.Element} The new node
519 insertBefore : function(el, o, returnElement){
520 return doInsert(el, o, returnElement, beforebegin);
524 * Creates new DOM element(s) and inserts them after el.
525 * @param {Mixed} el The context element
526 * @param {Object} o The DOM object spec (and children)
527 * @param {Boolean} returnElement (optional) true to return a Ext.Element
528 * @return {HTMLElement/Ext.Element} The new node
531 insertAfter : function(el, o, returnElement){
532 return doInsert(el, o, returnElement, afterend, 'nextSibling');
536 * Creates new DOM element(s) and inserts them as the first child of el.
537 * @param {Mixed} el The context element
538 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
539 * @param {Boolean} returnElement (optional) true to return a Ext.Element
540 * @return {HTMLElement/Ext.Element} The new node
543 insertFirst : function(el, o, returnElement){
544 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
548 * Creates new DOM element(s) and appends them to el.
549 * @param {Mixed} el The context element
550 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
551 * @param {Boolean} returnElement (optional) true to return a Ext.Element
552 * @return {HTMLElement/Ext.Element} The new node
555 append: function(el, o, returnElement){
556 return doInsert(el, o, returnElement, beforeend, '', true);
560 * Creates new DOM element(s) without inserting them to the document.
561 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
562 * @return {HTMLElement} The new uninserted node
569 * @class Ext.Template
570 * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
571 * for greater performance.</p>
572 * <p>For example usage {@link #Template see the constructor}.</p>
575 * An instance of this class may be created by passing to the constructor either
576 * a single argument, or multiple arguments:
577 * <div class="mdetail-params"><ul>
578 * <li><b>single argument</b> : String/Array
579 * <div class="sub-desc">
580 * The single argument may be either a String or an Array:<ul>
581 * <li><tt>String</tt> : </li><pre><code>
582 var t = new Ext.Template("<div>Hello {0}.</div>");
583 t.{@link #append}('some-element', ['foo']);
585 * <li><tt>Array</tt> : </li>
586 * An Array will be combined with <code>join('')</code>.
588 var t = new Ext.Template([
589 '<div name="{id}">',
590 '<span class="{cls}">{name:trim} {value:ellipsis(10)}</span>',
593 t.{@link #compile}();
594 t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
597 * <li><b>multiple arguments</b> : String, Object, Array, ...
598 * <div class="sub-desc">
599 * Multiple arguments will be combined with <code>join('')</code>.
601 var t = new Ext.Template(
602 '<div name="{id}">',
603 '<span class="{cls}">{name} {value}</span>',
605 // a configuration object:
607 compiled: true, // {@link #compile} immediately
608 disableFormats: true // See Notes below.
612 * <p><b>Notes</b>:</p>
613 * <div class="mdetail-params"><ul>
614 * <li>Formatting and <code>disableFormats</code> are not applicable for Ext Core.</li>
615 * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
616 * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
617 * when no formatting is required.</li>
621 * @param {Mixed} config
623 Ext.Template = function(html){
629 if (Ext.isArray(html)) {
630 html = html.join("");
631 } else if (a.length > 1) {
632 for(var i = 0, len = a.length; i < len; i++){
634 if(typeof v == 'object'){
646 * @cfg {Boolean} compiled Specify <tt>true</tt> to compile the template
647 * immediately (see <code>{@link #compile}</code>).
648 * Defaults to <tt>false</tt>.
654 Ext.Template.prototype = {
656 * @cfg {RegExp} re The regular expression used to match template variables.
657 * Defaults to:<pre><code>
658 * re : /\{([\w-]+)\}/g // for Ext Core
659 * re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g // for Ext JS
662 re : /\{([\w-]+)\}/g,
664 * See <code>{@link #re}</code>.
670 * Returns an HTML fragment of this template with the specified <code>values</code> applied.
671 * @param {Object/Array} values
672 * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
673 * or an object (i.e. <code>{foo: 'bar'}</code>).
674 * @return {String} The HTML fragment
676 applyTemplate : function(values){
680 me.compiled(values) :
681 me.html.replace(me.re, function(m, name){
682 return values[name] !== undefined ? values[name] : "";
687 * Sets the HTML used as the template and optionally compiles it.
688 * @param {String} html
689 * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
690 * @return {Ext.Template} this
692 set : function(html, compile){
696 return compile ? me.compile() : me;
700 * Compiles the template into an internal function, eliminating the RegEx overhead.
701 * @return {Ext.Template} this
703 compile : function(){
705 sep = Ext.isGecko ? "+" : ",";
707 function fn(m, name){
708 name = "values['" + name + "']";
709 return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
712 eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
713 me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
714 (Ext.isGecko ? "';};" : "'].join('');};"));
719 * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
720 * @param {Mixed} el The context element
721 * @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'})
722 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
723 * @return {HTMLElement/Ext.Element} The new node or Element
725 insertFirst: function(el, values, returnElement){
726 return this.doInsert('afterBegin', el, values, returnElement);
730 * Applies the supplied values to the template and inserts the new node(s) before el.
731 * @param {Mixed} el The context element
732 * @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'})
733 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
734 * @return {HTMLElement/Ext.Element} The new node or Element
736 insertBefore: function(el, values, returnElement){
737 return this.doInsert('beforeBegin', el, values, returnElement);
741 * Applies the supplied values to the template and inserts the new node(s) after el.
742 * @param {Mixed} el The context element
743 * @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'})
744 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
745 * @return {HTMLElement/Ext.Element} The new node or Element
747 insertAfter : function(el, values, returnElement){
748 return this.doInsert('afterEnd', el, values, returnElement);
752 * Applies the supplied <code>values</code> to the template and appends
753 * the new node(s) to the specified <code>el</code>.
754 * <p>For example usage {@link #Template see the constructor}.</p>
755 * @param {Mixed} el The context element
756 * @param {Object/Array} values
757 * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
758 * or an object (i.e. <code>{foo: 'bar'}</code>).
759 * @param {Boolean} returnElement (optional) true to return an Ext.Element (defaults to undefined)
760 * @return {HTMLElement/Ext.Element} The new node or Element
762 append : function(el, values, returnElement){
763 return this.doInsert('beforeEnd', el, values, returnElement);
766 doInsert : function(where, el, values, returnEl){
768 var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
769 return returnEl ? Ext.get(newNode, true) : newNode;
773 * Applies the supplied values to the template and overwrites the content of el with the new node(s).
774 * @param {Mixed} el The context element
775 * @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'})
776 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
777 * @return {HTMLElement/Ext.Element} The new node or Element
779 overwrite : function(el, values, returnElement){
781 el.innerHTML = this.applyTemplate(values);
782 return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
786 * Alias for {@link #applyTemplate}
787 * Returns an HTML fragment of this template with the specified <code>values</code> applied.
788 * @param {Object/Array} values
789 * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
790 * or an object (i.e. <code>{foo: 'bar'}</code>).
791 * @return {String} The HTML fragment
792 * @member Ext.Template
795 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
798 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
799 * @param {String/HTMLElement} el A DOM element or its id
800 * @param {Object} config A configuration object
801 * @return {Ext.Template} The created template
804 Ext.Template.from = function(el, config){
806 return new Ext.Template(el.value || el.innerHTML, config || '');
809 * @class Ext.Template
811 Ext.apply(Ext.Template.prototype, {
813 * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format
814 * functions in the template. If the template does not contain
815 * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>
816 * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.
818 var t = new Ext.Template(
819 '<div name="{id}">',
820 '<span class="{cls}">{name} {value}</span>',
823 compiled: true, // {@link #compile} immediately
824 disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting
828 * For a list of available format functions, see {@link Ext.util.Format}.
830 disableFormats : false,
832 * See <code>{@link #disableFormats}</code>.
834 * @property disableFormats
838 * The regular expression used to match template variables
843 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
844 argsRe : /^\s*['"](.*)["']\s*$/,
846 compileBRe : /(\r\n|\n)/g,
850 * Returns an HTML fragment of this template with the specified values applied.
851 * @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'})
852 * @return {String} The HTML fragment
855 applyTemplate : function(values){
857 useF = me.disableFormats !== true,
858 fm = Ext.util.Format,
862 return me.compiled(values);
864 function fn(m, name, format, args){
865 if (format && useF) {
866 if (format.substr(0, 5) == "this.") {
867 return tpl.call(format.substr(5), values[name], values);
870 // quoted values are required for strings in compiled templates,
871 // but for non compiled we need to strip them
872 // quoted reversed for jsmin
874 args = args.split(',');
875 for(var i = 0, len = args.length; i < len; i++){
876 args[i] = args[i].replace(re, "$1");
878 args = [values[name]].concat(args);
880 args = [values[name]];
882 return fm[format].apply(fm, args);
885 return values[name] !== undefined ? values[name] : "";
888 return me.html.replace(me.re, fn);
892 * Compiles the template into an internal function, eliminating the RegEx overhead.
893 * @return {Ext.Template} this
896 compile : function(){
898 fm = Ext.util.Format,
899 useF = me.disableFormats !== true,
900 sep = Ext.isGecko ? "+" : ",",
903 function fn(m, name, format, args){
905 args = args ? ',' + args : "";
906 if(format.substr(0, 5) != "this."){
907 format = "fm." + format + '(';
909 format = 'this.call("'+ format.substr(5) + '", ';
913 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
915 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
918 // branched to use + in gecko and [].join() in others
920 body = "this.compiled = function(values){ return '" +
921 me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn) +
924 body = ["this.compiled = function(values){ return ['"];
925 body.push(me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn));
926 body.push("'].join('');};");
927 body = body.join('');
933 // private function used to call members
934 call : function(fnName, value, allValues){
935 return this[fnName](value, allValues);
938 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
940 * This is code is also distributed under MIT license for use
941 * with jQuery and prototype JavaScript libraries.
944 * @class Ext.DomQuery
945 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).
947 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>
950 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.
952 <h4>Element Selectors:</h4>
954 <li> <b>*</b> any element</li>
955 <li> <b>E</b> an element with the tag E</li>
956 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
957 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
958 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
959 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
961 <h4>Attribute Selectors:</h4>
962 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
964 <li> <b>E[foo]</b> has an attribute "foo"</li>
965 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
966 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
967 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
968 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
969 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
970 <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
972 <h4>Pseudo Classes:</h4>
974 <li> <b>E:first-child</b> E is the first child of its parent</li>
975 <li> <b>E:last-child</b> E is the last child of its parent</li>
976 <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>
977 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
978 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
979 <li> <b>E:only-child</b> E is the only child of its parent</li>
980 <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>
981 <li> <b>E:first</b> the first E in the resultset</li>
982 <li> <b>E:last</b> the last E in the resultset</li>
983 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
984 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
985 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
986 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
987 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
988 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
989 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
990 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
991 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
992 <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
994 <h4>CSS Value Selectors:</h4>
996 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
997 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
998 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
999 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
1000 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
1001 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
1005 Ext.DomQuery = function(){
1010 trimRe = /^\s+|\s+$/g,
1011 tplRe = /\{(\d+)\}/g,
1012 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
1013 tagTokenRe = /^(#)?([\w-\*]+)/,
1014 nthRe = /(\d*)n\+?(\d*)/,
1016 // This is for IE MSXML which does not support expandos.
1017 // IE runs the same speed using setAttribute, however FF slows way down
1018 // and Safari completely fails so they need to continue to use expandos.
1019 isIE = window.ActiveXObject ? true : false,
1022 // this eval is stop the compressor from
1023 // renaming the variable to something shorter
1024 eval("var batch = 30803;");
1026 // Retrieve the child node from a particular
1027 // parent at the specified index.
1028 function child(parent, index){
1030 n = parent.firstChild;
1032 if(n.nodeType == 1){
1042 // retrieve the next element node
1044 while((n = n.nextSibling) && n.nodeType != 1);
1048 // retrieve the previous element node
1050 while((n = n.previousSibling) && n.nodeType != 1);
1054 // Mark each child node with a nodeIndex skipping and
1055 // removing empty text nodes.
1056 function children(parent){
1057 var n = parent.firstChild,
1061 nextNode = n.nextSibling;
1062 // clean worthless empty nodes.
1063 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
1064 parent.removeChild(n);
1066 // add an expando nodeIndex
1067 n.nodeIndex = ++nodeIndex;
1075 // nodeSet - array of nodes
1077 function byClassName(nodeSet, cls){
1081 var result = [], ri = -1;
1082 for(var i = 0, ci; ci = nodeSet[i]; i++){
1083 if((' '+ci.className+' ').indexOf(cls) != -1){
1090 function attrValue(n, attr){
1091 // if its an array, use the first node.
1092 if(!n.tagName && typeof n.length != "undefined"){
1102 if(attr == "class" || attr == "className"){
1105 return n.getAttribute(attr) || n[attr];
1111 // mode - false, /, >, +, ~
1112 // tagName - defaults to "*"
1113 function getNodes(ns, mode, tagName){
1114 var result = [], ri = -1, cs;
1118 tagName = tagName || "*";
1120 if(typeof ns.getElementsByTagName != "undefined"){
1124 // no mode specified, grab all elements by tagName
1127 for(var i = 0, ni; ni = ns[i]; i++){
1128 cs = ni.getElementsByTagName(tagName);
1129 for(var j = 0, ci; ci = cs[j]; j++){
1133 // Direct Child mode (/ or >)
1134 // E > F or E/F all direct children elements of E that have the tag
1135 } else if(mode == "/" || mode == ">"){
1136 var utag = tagName.toUpperCase();
1137 for(var i = 0, ni, cn; ni = ns[i]; i++){
1139 for(var j = 0, cj; cj = cn[j]; j++){
1140 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
1145 // Immediately Preceding mode (+)
1146 // E + F all elements with the tag F that are immediately preceded by an element with the tag E
1147 }else if(mode == "+"){
1148 var utag = tagName.toUpperCase();
1149 for(var i = 0, n; n = ns[i]; i++){
1150 while((n = n.nextSibling) && n.nodeType != 1);
1151 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
1156 // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
1157 }else if(mode == "~"){
1158 var utag = tagName.toUpperCase();
1159 for(var i = 0, n; n = ns[i]; i++){
1160 while((n = n.nextSibling)){
1161 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
1170 function concat(a, b){
1174 for(var i = 0, l = b.length; i < l; i++){
1180 function byTag(cs, tagName){
1181 if(cs.tagName || cs == document){
1187 var result = [], ri = -1;
1188 tagName = tagName.toLowerCase();
1189 for(var i = 0, ci; ci = cs[i]; i++){
1190 if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
1197 function byId(cs, id){
1198 if(cs.tagName || cs == document){
1204 var result = [], ri = -1;
1205 for(var i = 0, ci; ci = cs[i]; i++){
1206 if(ci && ci.id == id){
1214 // operators are =, !=, ^=, $=, *=, %=, |= and ~=
1215 // custom can be "{"
1216 function byAttribute(cs, attr, value, op, custom){
1219 useGetStyle = custom == "{",
1220 fn = Ext.DomQuery.operators[op],
1225 for(var i = 0, ci; ci = cs[i]; i++){
1226 // skip non-element nodes.
1227 if(ci.nodeType != 1){
1230 // only need to do this for the first node
1232 xml = Ext.DomQuery.isXml(ci);
1236 // we only need to change the property names if we're dealing with html nodes, not XML
1239 a = Ext.DomQuery.getStyle(ci, attr);
1240 } else if (attr == "class" || attr == "className"){
1242 } else if (attr == "for"){
1244 } else if (attr == "href"){
1245 // getAttribute href bug
1246 // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
1247 a = ci.getAttribute("href", 2);
1249 a = ci.getAttribute(attr);
1252 a = ci.getAttribute(attr);
1254 if((fn && fn(a, value)) || (!fn && a)){
1261 function byPseudo(cs, name, value){
1262 return Ext.DomQuery.pseudos[name](cs, value);
1265 function nodupIEXml(cs){
1268 cs[0].setAttribute("_nodup", d);
1270 for(var i = 1, len = cs.length; i < len; i++){
1272 if(!c.getAttribute("_nodup") != d){
1273 c.setAttribute("_nodup", d);
1277 for(var i = 0, len = cs.length; i < len; i++){
1278 cs[i].removeAttribute("_nodup");
1287 var len = cs.length, c, i, r = cs, cj, ri = -1;
1288 if(!len || typeof cs.nodeType != "undefined" || len == 1){
1291 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
1292 return nodupIEXml(cs);
1296 for(i = 1; c = cs[i]; i++){
1301 for(var j = 0; j < i; j++){
1304 for(j = i+1; cj = cs[j]; j++){
1316 function quickDiffIEXml(c1, c2){
1319 for(var i = 0, len = c1.length; i < len; i++){
1320 c1[i].setAttribute("_qdiff", d);
1322 for(var i = 0, len = c2.length; i < len; i++){
1323 if(c2[i].getAttribute("_qdiff") != d){
1324 r[r.length] = c2[i];
1327 for(var i = 0, len = c1.length; i < len; i++){
1328 c1[i].removeAttribute("_qdiff");
1333 function quickDiff(c1, c2){
1334 var len1 = c1.length,
1340 if(isIE && typeof c1[0].selectSingleNode != "undefined"){
1341 return quickDiffIEXml(c1, c2);
1343 for(var i = 0; i < len1; i++){
1346 for(var i = 0, len = c2.length; i < len; i++){
1347 if(c2[i]._qdiff != d){
1348 r[r.length] = c2[i];
1354 function quickId(ns, mode, root, id){
1356 var d = root.ownerDocument || root;
1357 return d.getElementById(id);
1359 ns = getNodes(ns, mode, "*");
1360 return byId(ns, id);
1364 getStyle : function(el, name){
1365 return Ext.fly(el).getStyle(name);
1368 * Compiles a selector/xpath query into a reusable function. The returned function
1369 * takes one parameter "root" (optional), which is the context node from where the query should start.
1370 * @param {String} selector The selector/xpath query
1371 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
1372 * @return {Function}
1374 compile : function(path, type){
1375 type = type || "select";
1377 // setup fn preamble
1378 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
1381 matchers = Ext.DomQuery.matchers,
1382 matchersLn = matchers.length,
1384 // accept leading mode switch
1385 lmode = path.match(modeRe);
1387 if(lmode && lmode[1]){
1388 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
1389 path = path.replace(lmode[1], "");
1392 // strip leading slashes
1393 while(path.substr(0, 1)=="/"){
1394 path = path.substr(1);
1397 while(path && lastPath != path){
1399 var tokenMatch = path.match(tagTokenRe);
1400 if(type == "select"){
1403 if(tokenMatch[1] == "#"){
1404 fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
1406 fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1408 path = path.replace(tokenMatch[0], "");
1409 }else if(path.substr(0, 1) != '@'){
1410 fn[fn.length] = 'n = getNodes(n, mode, "*");';
1415 if(tokenMatch[1] == "#"){
1416 fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1418 fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1420 path = path.replace(tokenMatch[0], "");
1423 while(!(modeMatch = path.match(modeRe))){
1424 var matched = false;
1425 for(var j = 0; j < matchersLn; j++){
1426 var t = matchers[j];
1427 var m = path.match(t.re);
1429 fn[fn.length] = t.select.replace(tplRe, function(x, i){
1432 path = path.replace(m[0], "");
1437 // prevent infinite loop on bad selector
1439 throw 'Error parsing selector, parsing failed at "' + path + '"';
1443 fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1444 path = path.replace(modeMatch[1], "");
1448 fn[fn.length] = "return nodup(n);\n}";
1450 // eval fn and return it
1456 * Selects a group of elements.
1457 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1458 * @param {Node/String} root (optional) The start of the query (defaults to document).
1459 * @return {Array} An Array of DOM elements which match the selector. If there are
1460 * no matches, and empty Array is returned.
1462 jsSelect: function(path, root, type){
1463 // set root to doc if not specified.
1464 root = root || document;
1466 if(typeof root == "string"){
1467 root = document.getElementById(root);
1469 var paths = path.split(","),
1472 // loop over each selector
1473 for(var i = 0, len = paths.length; i < len; i++){
1474 var subPath = paths[i].replace(trimRe, "");
1475 // compile and place in cache
1476 if(!cache[subPath]){
1477 cache[subPath] = Ext.DomQuery.compile(subPath);
1478 if(!cache[subPath]){
1479 throw subPath + " is not a valid selector";
1482 var result = cache[subPath](root);
1483 if(result && result != document){
1484 results = results.concat(result);
1488 // if there were multiple selectors, make sure dups
1490 if(paths.length > 1){
1491 return nodup(results);
1495 isXml: function(el) {
1496 var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1497 return docEl ? docEl.nodeName !== "HTML" : false;
1499 select : document.querySelectorAll ? function(path, root, type) {
1500 root = root || document;
1501 if (!Ext.DomQuery.isXml(root)) {
1503 var cs = root.querySelectorAll(path);
1504 return Ext.toArray(cs);
1508 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1509 } : function(path, root, type) {
1510 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1514 * Selects a single element.
1515 * @param {String} selector The selector/xpath query
1516 * @param {Node} root (optional) The start of the query (defaults to document).
1517 * @return {Element} The DOM element which matched the selector.
1519 selectNode : function(path, root){
1520 return Ext.DomQuery.select(path, root)[0];
1524 * Selects the value of a node, optionally replacing null with the defaultValue.
1525 * @param {String} selector The selector/xpath query
1526 * @param {Node} root (optional) The start of the query (defaults to document).
1527 * @param {String} defaultValue
1530 selectValue : function(path, root, defaultValue){
1531 path = path.replace(trimRe, "");
1532 if(!valueCache[path]){
1533 valueCache[path] = Ext.DomQuery.compile(path, "select");
1535 var n = valueCache[path](root), v;
1536 n = n[0] ? n[0] : n;
1538 // overcome a limitation of maximum textnode size
1539 // Rumored to potentially crash IE6 but has not been confirmed.
1540 // http://reference.sitepoint.com/javascript/Node/normalize
1541 // https://developer.mozilla.org/En/DOM/Node.normalize
1542 if (typeof n.normalize == 'function') n.normalize();
1544 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1545 return ((v === null||v === undefined||v==='') ? defaultValue : v);
1549 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1550 * @param {String} selector The selector/xpath query
1551 * @param {Node} root (optional) The start of the query (defaults to document).
1552 * @param {Number} defaultValue
1555 selectNumber : function(path, root, defaultValue){
1556 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1557 return parseFloat(v);
1561 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1562 * @param {String/HTMLElement/Array} el An element id, element or array of elements
1563 * @param {String} selector The simple selector to test
1566 is : function(el, ss){
1567 if(typeof el == "string"){
1568 el = document.getElementById(el);
1570 var isArray = Ext.isArray(el),
1571 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1572 return isArray ? (result.length == el.length) : (result.length > 0);
1576 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1577 * @param {Array} el An array of elements to filter
1578 * @param {String} selector The simple selector to test
1579 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1580 * the selector instead of the ones that match
1581 * @return {Array} An Array of DOM elements which match the selector. If there are
1582 * no matches, and empty Array is returned.
1584 filter : function(els, ss, nonMatches){
1585 ss = ss.replace(trimRe, "");
1586 if(!simpleCache[ss]){
1587 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1589 var result = simpleCache[ss](els);
1590 return nonMatches ? quickDiff(result, els) : result;
1594 * Collection of matching regular expressions and code snippets.
1595 * Each capture group within () will be replace the {} in the select
1596 * statement as specified by their index.
1600 select: 'n = byClassName(n, " {1} ");'
1602 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1603 select: 'n = byPseudo(n, "{1}", "{2}");'
1605 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1606 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1609 select: 'n = byId(n, "{1}");'
1612 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1617 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1618 * 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, > <.
1621 "=" : function(a, v){
1624 "!=" : function(a, v){
1627 "^=" : function(a, v){
1628 return a && a.substr(0, v.length) == v;
1630 "$=" : function(a, v){
1631 return a && a.substr(a.length-v.length) == v;
1633 "*=" : function(a, v){
1634 return a && a.indexOf(v) !== -1;
1636 "%=" : function(a, v){
1637 return (a % v) == 0;
1639 "|=" : function(a, v){
1640 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1642 "~=" : function(a, v){
1643 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1648 * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed
1649 * two parameters:</p><div class="mdetail-params"><ul>
1650 * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>
1651 * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>
1653 * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>
1654 * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,
1655 * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>
1656 * <p>For example, to filter <code><a></code> elements to only return links to <i>external</i> resources:</p>
1658 Ext.DomQuery.pseudos.external = function(c, v){
1659 var r = [], ri = -1;
1660 for(var i = 0, ci; ci = c[i]; i++){
1661 // Include in result set only if it's a link to an external resource
1662 if(ci.hostname != location.hostname){
1668 * Then external links could be gathered with the following statement:<code><pre>
1669 var externalLinks = Ext.select("a:external");
1673 "first-child" : function(c){
1674 var r = [], ri = -1, n;
1675 for(var i = 0, ci; ci = n = c[i]; i++){
1676 while((n = n.previousSibling) && n.nodeType != 1);
1684 "last-child" : function(c){
1685 var r = [], ri = -1, n;
1686 for(var i = 0, ci; ci = n = c[i]; i++){
1687 while((n = n.nextSibling) && n.nodeType != 1);
1695 "nth-child" : function(c, a) {
1696 var r = [], ri = -1,
1697 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1698 f = (m[1] || 1) - 0, l = m[2] - 0;
1699 for(var i = 0, n; n = c[i]; i++){
1700 var pn = n.parentNode;
1701 if (batch != pn._batch) {
1703 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1704 if(cn.nodeType == 1){
1711 if (l == 0 || n.nodeIndex == l){
1714 } else if ((n.nodeIndex + l) % f == 0){
1722 "only-child" : function(c){
1723 var r = [], ri = -1;;
1724 for(var i = 0, ci; ci = c[i]; i++){
1725 if(!prev(ci) && !next(ci)){
1732 "empty" : function(c){
1733 var r = [], ri = -1;
1734 for(var i = 0, ci; ci = c[i]; i++){
1735 var cns = ci.childNodes, j = 0, cn, empty = true;
1738 if(cn.nodeType == 1 || cn.nodeType == 3){
1750 "contains" : function(c, v){
1751 var r = [], ri = -1;
1752 for(var i = 0, ci; ci = c[i]; i++){
1753 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
1760 "nodeValue" : function(c, v){
1761 var r = [], ri = -1;
1762 for(var i = 0, ci; ci = c[i]; i++){
1763 if(ci.firstChild && ci.firstChild.nodeValue == v){
1770 "checked" : function(c){
1771 var r = [], ri = -1;
1772 for(var i = 0, ci; ci = c[i]; i++){
1773 if(ci.checked == true){
1780 "not" : function(c, ss){
1781 return Ext.DomQuery.filter(c, ss, true);
1784 "any" : function(c, selectors){
1785 var ss = selectors.split('|'),
1787 for(var i = 0, ci; ci = c[i]; i++){
1788 for(var j = 0; s = ss[j]; j++){
1789 if(Ext.DomQuery.is(ci, s)){
1798 "odd" : function(c){
1799 return this["nth-child"](c, "odd");
1802 "even" : function(c){
1803 return this["nth-child"](c, "even");
1806 "nth" : function(c, a){
1807 return c[a-1] || [];
1810 "first" : function(c){
1814 "last" : function(c){
1815 return c[c.length-1] || [];
1818 "has" : function(c, ss){
1819 var s = Ext.DomQuery.select,
1821 for(var i = 0, ci; ci = c[i]; i++){
1822 if(s(ss, ci).length > 0){
1829 "next" : function(c, ss){
1830 var is = Ext.DomQuery.is,
1832 for(var i = 0, ci; ci = c[i]; i++){
1841 "prev" : function(c, ss){
1842 var is = Ext.DomQuery.is,
1844 for(var i = 0, ci; ci = c[i]; i++){
1857 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
1858 * @param {String} path The selector/xpath query
1859 * @param {Node} root (optional) The start of the query (defaults to document).
1864 Ext.query = Ext.DomQuery.select;
1866 * @class Ext.util.DelayedTask
1867 * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
1868 * performing setTimeout where a new timeout cancels the old timeout. When called, the
1869 * task will wait the specified time period before executing. If durng that time period,
1870 * the task is called again, the original call will be cancelled. This continues so that
1871 * the function is only called a single time for each iteration.</p>
1872 * <p>This method is especially useful for things like detecting whether a user has finished
1873 * typing in a text field. An example would be performing validation on a keypress. You can
1874 * use this class to buffer the keypress events for a certain number of milliseconds, and
1875 * perform only if they stop for that amount of time. Usage:</p><pre><code>
1876 var task = new Ext.util.DelayedTask(function(){
1877 alert(Ext.getDom('myInputField').value.length);
1879 // Wait 500ms before calling our function. If the user presses another key
1880 // during that 500ms, it will be cancelled and we'll wait another 500ms.
1881 Ext.get('myInputField').on('keypress', function(){
1882 task.{@link #delay}(500);
1885 * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
1886 * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
1887 * also setup a delayed task for you to buffer events.</p>
1888 * @constructor The parameters to this constructor serve as defaults and are not required.
1889 * @param {Function} fn (optional) The default function to call.
1890 * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
1891 * function is called. If not specified, <code>this</code> will refer to the browser window.
1892 * @param {Array} args (optional) The default Array of arguments.
1894 Ext.util.DelayedTask = function(fn, scope, args){
1900 fn.apply(scope, args || []);
1904 * Cancels any pending timeout and queues a new one
1905 * @param {Number} delay The milliseconds to delay
1906 * @param {Function} newFn (optional) Overrides function passed to constructor
1907 * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
1908 * is specified, <code>this</code> will refer to the browser window.
1909 * @param {Array} newArgs (optional) Overrides args passed to constructor
1911 me.delay = function(delay, newFn, newScope, newArgs){
1914 scope = newScope || scope;
1915 args = newArgs || args;
1916 id = setInterval(call, delay);
1920 * Cancel the last queued timeout
1922 me.cancel = function(){
1930 var EXTUTIL = Ext.util,
1935 * @class Ext.util.Observable
1936 * Base class that provides a common interface for publishing events. Subclasses are expected to
1937 * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1938 * with configured listeners defined.<br>
1941 Employee = Ext.extend(Ext.util.Observable, {
1942 constructor: function(config){
1943 this.name = config.name;
1949 // Copy configured listeners into *this* object so that the base class's
1950 // constructor will add them.
1951 this.listeners = config.listeners;
1953 // Call our superclass constructor to complete construction process.
1954 Employee.superclass.constructor.call(this, config)
1958 * This could then be used like this:<pre><code>
1959 var newEmployee = new Employee({
1963 // By default, "this" will be the object that fired the event.
1964 alert(this.name + " has quit!");
1970 EXTUTIL.Observable = function(){
1972 * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1973 * object during initialization. This should be a valid listeners config object as specified in the
1974 * {@link #addListener} example for attaching multiple handlers at once.</p>
1975 * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1976 * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1977 * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1978 * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1979 * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1980 * has been rendered. A plugin can simplify this step:<pre><code>
1981 // Plugin is configured with a listeners config object.
1982 // The Component is appended to the argument list of all handler functions.
1983 Ext.DomObserver = Ext.extend(Object, {
1984 constructor: function(config) {
1985 this.listeners = config.listeners ? config.listeners : config;
1988 // Component passes itself into plugin's init method
1990 var p, l = this.listeners;
1992 if (Ext.isFunction(l[p])) {
1993 l[p] = this.createHandler(l[p], c);
1995 l[p].fn = this.createHandler(l[p].fn, c);
1999 // Add the listeners to the Element immediately following the render call
2000 c.render = c.render.{@link Function#createSequence createSequence}(function() {
2008 createHandler: function(fn, c) {
2009 return function(e) {
2010 fn.call(this, e, c);
2015 var combo = new Ext.form.ComboBox({
2017 // Collapse combo when its element is clicked on
2018 plugins: [ new Ext.DomObserver({
2019 click: function(evt, comp) {
2026 triggerAction: 'all'
2030 var me = this, e = me.events;
2032 me.on(me.listeners);
2033 delete me.listeners;
2035 me.events = e || {};
2038 EXTUTIL.Observable.prototype = {
2040 filterOptRe : /^(?:scope|delay|buffer|single)$/,
2043 * <p>Fires the specified event with the passed parameters (minus the event name).</p>
2044 * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
2045 * by calling {@link #enableBubble}.</p>
2046 * @param {String} eventName The name of the event to fire.
2047 * @param {Object...} args Variable number of parameters are passed to handlers.
2048 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
2050 fireEvent : function(){
2051 var a = Array.prototype.slice.call(arguments, 0),
2052 ename = a[0].toLowerCase(),
2055 ce = me.events[ename],
2059 if (me.eventsSuspended === TRUE) {
2060 if (q = me.eventQueue) {
2064 else if(typeof ce == 'object') {
2066 if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
2069 c = me.getBubbleTarget && me.getBubbleTarget();
2070 if(c && c.enableBubble) {
2071 cc = c.events[ename];
2072 if(!cc || typeof cc != 'object' || !cc.bubble) {
2073 c.enableBubble(ename);
2075 return c.fireEvent.apply(c, a);
2080 ret = ce.fire.apply(ce, a);
2087 * Appends an event handler to this object.
2088 * @param {String} eventName The name of the event to listen for.
2089 * @param {Function} handler The method the event invokes.
2090 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2091 * <b>If omitted, defaults to the object which fired the event.</b>
2092 * @param {Object} options (optional) An object containing handler configuration.
2093 * properties. This may contain any of the following properties:<ul>
2094 * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2095 * <b>If omitted, defaults to the object which fired the event.</b></div></li>
2096 * <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>
2097 * <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>
2098 * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2099 * by the specified number of milliseconds. If the event fires again within that time, the original
2100 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2101 * <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>
2102 * if the event was bubbled up from a child Observable.</div></li>
2105 * <b>Combining Options</b><br>
2106 * Using the options argument, it is possible to combine different types of listeners:<br>
2108 * A delayed, one-time listener.
2110 myDataView.on('click', this.onClick, this, {
2115 * <b>Attaching multiple handlers in 1 call</b><br>
2116 * The method also allows for a single argument to be passed which is a config object containing properties
2117 * which specify multiple handlers.
2127 fn: this.onMouseOver,
2131 fn: this.onMouseOut,
2136 * Or a shorthand syntax:<br>
2139 'click' : this.onClick,
2140 'mouseover' : this.onMouseOver,
2141 'mouseout' : this.onMouseOut,
2145 addListener : function(eventName, fn, scope, o){
2151 if (typeof eventName == 'object') {
2155 if (!me.filterOptRe.test(e)) {
2156 me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
2160 eventName = eventName.toLowerCase();
2161 ce = me.events[eventName] || TRUE;
2162 if (typeof ce == 'boolean') {
2163 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
2165 ce.addListener(fn, scope, typeof o == 'object' ? o : {});
2170 * Removes an event handler.
2171 * @param {String} eventName The type of event the handler was associated with.
2172 * @param {Function} handler The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2173 * @param {Object} scope (optional) The scope originally specified for the handler.
2175 removeListener : function(eventName, fn, scope){
2176 var ce = this.events[eventName.toLowerCase()];
2177 if (typeof ce == 'object') {
2178 ce.removeListener(fn, scope);
2183 * Removes all listeners for this object
2185 purgeListeners : function(){
2186 var events = this.events,
2191 if(typeof evt == 'object'){
2192 evt.clearListeners();
2198 * Adds the specified events to the list of events which this Observable may fire.
2199 * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
2200 * or the first event name string if multiple event names are being passed as separate parameters.
2201 * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
2203 this.addEvents('storeloaded', 'storecleared');
2206 addEvents : function(o){
2208 me.events = me.events || {};
2209 if (typeof o == 'string') {
2213 me.events[a[i]] = me.events[a[i]] || TRUE;
2216 Ext.applyIf(me.events, o);
2221 * Checks to see if this object has any listeners for a specified event
2222 * @param {String} eventName The name of the event to check for
2223 * @return {Boolean} True if the event is being listened for, else false
2225 hasListener : function(eventName){
2226 var e = this.events[eventName.toLowerCase()];
2227 return typeof e == 'object' && e.listeners.length > 0;
2231 * Suspend the firing of all events. (see {@link #resumeEvents})
2232 * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
2233 * after the {@link #resumeEvents} call instead of discarding all suspended events;
2235 suspendEvents : function(queueSuspended){
2236 this.eventsSuspended = TRUE;
2237 if(queueSuspended && !this.eventQueue){
2238 this.eventQueue = [];
2243 * Resume firing events. (see {@link #suspendEvents})
2244 * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
2245 * events fired during event suspension will be sent to any listeners now.
2247 resumeEvents : function(){
2249 queued = me.eventQueue || [];
2250 me.eventsSuspended = FALSE;
2251 delete me.eventQueue;
2252 EACH(queued, function(e) {
2253 me.fireEvent.apply(me, e);
2258 var OBSERVABLE = EXTUTIL.Observable.prototype;
2260 * Appends an event handler to this object (shorthand for {@link #addListener}.)
2261 * @param {String} eventName The type of event to listen for
2262 * @param {Function} handler The method the event invokes
2263 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2264 * <b>If omitted, defaults to the object which fired the event.</b>
2265 * @param {Object} options (optional) An object containing handler configuration.
2268 OBSERVABLE.on = OBSERVABLE.addListener;
2270 * Removes an event handler (shorthand for {@link #removeListener}.)
2271 * @param {String} eventName The type of event the handler was associated with.
2272 * @param {Function} handler The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2273 * @param {Object} scope (optional) The scope originally specified for the handler.
2276 OBSERVABLE.un = OBSERVABLE.removeListener;
2279 * Removes <b>all</b> added captures from the Observable.
2280 * @param {Observable} o The Observable to release
2283 EXTUTIL.Observable.releaseCapture = function(o){
2284 o.fireEvent = OBSERVABLE.fireEvent;
2287 function createTargeted(h, o, scope){
2289 if(o.target == arguments[0]){
2290 h.apply(scope, Array.prototype.slice.call(arguments, 0));
2295 function createBuffered(h, o, l, scope){
2296 l.task = new EXTUTIL.DelayedTask();
2298 l.task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
2302 function createSingle(h, e, fn, scope){
2304 e.removeListener(fn, scope);
2305 return h.apply(scope, arguments);
2309 function createDelayed(h, o, l, scope){
2311 var task = new EXTUTIL.DelayedTask();
2316 task.delay(o.delay || 10, h, scope, Array.prototype.slice.call(arguments, 0));
2320 EXTUTIL.Event = function(obj, name){
2323 this.listeners = [];
2326 EXTUTIL.Event.prototype = {
2327 addListener : function(fn, scope, options){
2330 scope = scope || me.obj;
2331 if(!me.isListening(fn, scope)){
2332 l = me.createListener(fn, scope, options);
2333 if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2334 me.listeners = me.listeners.slice(0);
2336 me.listeners.push(l);
2340 createListener: function(fn, scope, o){
2342 scope = scope || this.obj;
2349 h = createTargeted(h, o, scope);
2352 h = createDelayed(h, o, l, scope);
2355 h = createSingle(h, this, fn, scope);
2358 h = createBuffered(h, o, l, scope);
2364 findListener : function(fn, scope){
2365 var list = this.listeners,
2369 scope = scope || this.obj;
2373 if(l.fn == fn && l.scope == scope){
2381 isListening : function(fn, scope){
2382 return this.findListener(fn, scope) != -1;
2385 removeListener : function(fn, scope){
2391 if((index = me.findListener(fn, scope)) != -1){
2393 me.listeners = me.listeners.slice(0);
2395 l = me.listeners[index];
2400 k = l.tasks && l.tasks.length;
2403 l.tasks[k].cancel();
2407 me.listeners.splice(index, 1);
2413 // Iterate to stop any buffered/delayed events
2414 clearListeners : function(){
2419 me.removeListener(l[i].fn, l[i].scope);
2425 listeners = me.listeners,
2426 len = listeners.length,
2432 var args = Array.prototype.slice.call(arguments, 0);
2433 for (; i < len; i++) {
2435 if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2436 return (me.firing = FALSE);
2447 * @class Ext.util.Observable
2449 Ext.apply(Ext.util.Observable.prototype, function(){
2450 // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
2451 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2453 function getMethodEvent(method){
2454 var e = (this.methodEvents = this.methodEvents ||
2455 {})[method], returnValue, v, cancel, obj = this;
2458 this.methodEvents[method] = e = {};
2459 e.originalFn = this[method];
2460 e.methodName = method;
2464 var makeCall = function(fn, scope, args){
2465 if((v = fn.apply(scope || obj, args)) !== undefined){
2466 if (typeof v == 'object') {
2467 if(v.returnValue !== undefined){
2468 returnValue = v.returnValue;
2472 cancel = !!v.cancel;
2484 this[method] = function(){
2485 var args = Array.prototype.slice.call(arguments, 0),
2487 returnValue = v = undefined;
2490 for(var i = 0, len = e.before.length; i < len; i++){
2492 makeCall(b.fn, b.scope, args);
2498 if((v = e.originalFn.apply(obj, args)) !== undefined){
2502 for(var i = 0, len = e.after.length; i < len; i++){
2504 makeCall(b.fn, b.scope, args);
2516 // these are considered experimental
2517 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2518 // adds an 'interceptor' called before the original method
2519 beforeMethod : function(method, fn, scope){
2520 getMethodEvent.call(this, method).before.push({
2526 // adds a 'sequence' called after the original method
2527 afterMethod : function(method, fn, scope){
2528 getMethodEvent.call(this, method).after.push({
2534 removeMethodListener: function(method, fn, scope){
2535 var e = this.getMethodEvent(method);
2536 for(var i = 0, len = e.before.length; i < len; i++){
2537 if(e.before[i].fn == fn && e.before[i].scope == scope){
2538 e.before.splice(i, 1);
2542 for(var i = 0, len = e.after.length; i < len; i++){
2543 if(e.after[i].fn == fn && e.after[i].scope == scope){
2544 e.after.splice(i, 1);
2551 * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
2552 * @param {Object} o The Observable whose events this object is to relay.
2553 * @param {Array} events Array of event names to relay.
2555 relayEvents : function(o, events){
2557 function createHandler(ename){
2559 return me.fireEvent.apply(me, [ename].concat(Array.prototype.slice.call(arguments, 0)));
2562 for(var i = 0, len = events.length; i < len; i++){
2563 var ename = events[i];
2564 me.events[ename] = me.events[ename] || true;
2565 o.on(ename, createHandler(ename), me);
2570 * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
2571 * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
2572 * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default
2573 * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
2574 * access the required target more quickly.</p>
2575 * <p>Example:</p><pre><code>
2576 Ext.override(Ext.form.Field, {
2577 // Add functionality to Field's initComponent to enable the change event to bubble
2578 initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
2579 this.enableBubble('change');
2582 // We know that we want Field's events to bubble directly to the FormPanel.
2583 getBubbleTarget : function() {
2584 if (!this.formPanel) {
2585 this.formPanel = this.findParentByType('form');
2587 return this.formPanel;
2591 var myForm = new Ext.formPanel({
2592 title: 'User Details',
2597 change: function() {
2598 // Title goes red if form has been modified.
2599 myForm.header.setStyle('color', 'red');
2604 * @param {String/Array} events The event name to bubble, or an Array of event names.
2606 enableBubble : function(events){
2608 if(!Ext.isEmpty(events)){
2609 events = Ext.isArray(events) ? events : Array.prototype.slice.call(arguments, 0);
2610 for(var i = 0, len = events.length; i < len; i++){
2611 var ename = events[i];
2612 ename = ename.toLowerCase();
2613 var ce = me.events[ename] || true;
2614 if (typeof ce == 'boolean') {
2615 ce = new Ext.util.Event(me, ename);
2616 me.events[ename] = ce;
2627 * Starts capture on the specified Observable. All events will be passed
2628 * to the supplied function with the event name + standard signature of the event
2629 * <b>before</b> the event is fired. If the supplied function returns false,
2630 * the event will not fire.
2631 * @param {Observable} o The Observable to capture events from.
2632 * @param {Function} fn The function to call when an event is fired.
2633 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
2636 Ext.util.Observable.capture = function(o, fn, scope){
2637 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
2642 * Sets observability on the passed class constructor.<p>
2643 * <p>This makes any event fired on any instance of the passed class also fire a single event through
2644 * the <i>class</i> allowing for central handling of events on many instances at once.</p>
2645 * <p>Usage:</p><pre><code>
2646 Ext.util.Observable.observeClass(Ext.data.Connection);
2647 Ext.data.Connection.on('beforerequest', function(con, options) {
2648 console.log('Ajax request made to ' + options.url);
2650 * @param {Function} c The class constructor to make observable.
2651 * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
2654 Ext.util.Observable.observeClass = function(c, listeners){
2657 Ext.apply(c, new Ext.util.Observable());
2658 Ext.util.Observable.capture(c.prototype, c.fireEvent, c);
2660 if(typeof listeners == 'object'){
2667 * @class Ext.EventManager
2668 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
2669 * several useful events directly.
2670 * See {@link Ext.EventObject} for more details on normalized event objects.
2674 Ext.EventManager = function(){
2677 docReadyState = false,
2678 DETECT_NATIVE = Ext.isGecko || Ext.isWebKit || Ext.isSafari,
2683 DOMCONTENTLOADED = "DOMContentLoaded",
2684 COMPLETE = 'complete',
2685 propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
2687 * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep
2688 * a reference to them so we can look them up at a later point.
2690 specialElCache = [];
2695 len = specialElCache.length,
2700 if(el.getElementById || el.navigator){
2702 for(; i < len; ++i){
2703 o = specialElCache[i];
2710 // for browsers that support it, ensure that give the el the same id
2712 specialElCache.push({
2721 if(!Ext.elCache[id]){
2722 Ext.Element.addToCache(new Ext.Element(el), id);
2724 Ext.elCache[id].skipGC = true;
2731 /// There is some jquery work around stuff here that isn't needed in Ext Core.
2732 function addListener(el, ename, fn, task, wrap, scope){
2733 el = Ext.getDom(el);
2735 es = Ext.elCache[id].events,
2738 wfn = E.on(el, ename, wrap);
2739 es[ename] = es[ename] || [];
2741 /* 0 = Original Function,
2742 1 = Event Manager Wrapped Function,
2744 3 = Adapter Wrapped Function,
2747 es[ename].push([fn, wrap, scope, wfn, task]);
2749 // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
2750 // without breaking ExtJS.
2752 // workaround for jQuery
2753 if(el.addEventListener && ename == "mousewheel"){
2754 var args = ["DOMMouseScroll", wrap, false];
2755 el.addEventListener.apply(el, args);
2756 Ext.EventManager.addListener(WINDOW, 'unload', function(){
2757 el.removeEventListener.apply(el, args);
2761 // fix stopped mousedowns on the document
2762 if(el == DOC && ename == "mousedown"){
2763 Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
2767 function doScrollChk(){
2769 'doScroll' will NOT work in a IFRAME/FRAMESET.
2770 The method succeeds but, a DOM query done immediately after -- FAILS.
2777 DOC.documentElement.doScroll('left');
2786 * @return {Boolean} True if the document is in a 'complete' state (or was determined to
2787 * be true by other means). If false, the state is evaluated again until canceled.
2789 function checkReadyState(e){
2791 if(Ext.isIE && doScrollChk()){
2794 if(DOC.readyState == COMPLETE){
2798 docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2803 function checkStyleSheets(e){
2804 styles || (styles = Ext.query('style, link[rel=stylesheet]'));
2805 if(styles.length == DOC.styleSheets.length){
2809 docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2813 function OperaDOMContentLoaded(e){
2814 DOC.removeEventListener(DOMCONTENTLOADED, arguments.callee, false);
2818 function fireDocReady(e){
2820 docReadyState = true; //only attempt listener removal once
2823 clearTimeout(docReadyProcId);
2826 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
2828 if(Ext.isIE && checkReadyState.bindIE){ //was this was actually set ??
2829 DOC.detachEvent('onreadystatechange', checkReadyState);
2831 E.un(WINDOW, "load", arguments.callee);
2833 if(docReadyEvent && !Ext.isReady){
2835 docReadyEvent.fire();
2836 docReadyEvent.listeners = [];
2841 function initDocReady(){
2842 docReadyEvent || (docReadyEvent = new Ext.util.Event());
2843 if (DETECT_NATIVE) {
2844 DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
2847 * Handle additional (exceptional) detection strategies here
2850 //Use readystatechange as a backup AND primary detection mechanism for a FRAME/IFRAME
2851 //See if page is already loaded
2852 if(!checkReadyState()){
2853 checkReadyState.bindIE = true;
2854 DOC.attachEvent('onreadystatechange', checkReadyState);
2857 }else if(Ext.isOpera ){
2859 Opera needs special treatment needed here because CSS rules are NOT QUITE
2860 available after DOMContentLoaded is raised.
2863 //See if page is already loaded and all styleSheets are in place
2864 (DOC.readyState == COMPLETE && checkStyleSheets()) ||
2865 DOC.addEventListener(DOMCONTENTLOADED, OperaDOMContentLoaded, false);
2867 }else if (Ext.isWebKit){
2868 //Fallback for older Webkits without DOMCONTENTLOADED support
2871 // no matter what, make sure it fires on load
2872 E.on(WINDOW, "load", fireDocReady);
2875 function createTargeted(h, o){
2877 var args = Ext.toArray(arguments);
2878 if(o.target == Ext.EventObject.setEvent(args[0]).target){
2879 h.apply(this, args);
2884 function createBuffered(h, o, task){
2886 // create new event object impl so new events don't wipe out properties
2887 task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
2891 function createSingle(h, el, ename, fn, scope){
2893 Ext.EventManager.removeListener(el, ename, fn, scope);
2898 function createDelayed(h, o, fn){
2900 var task = new Ext.util.DelayedTask(h);
2904 fn.tasks.push(task);
2905 task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);
2909 function listen(element, ename, opt, fn, scope){
2910 var o = (!opt || typeof opt == "boolean") ? {} : opt,
2911 el = Ext.getDom(element), task;
2914 scope = scope || o.scope;
2917 throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
2920 // prevent errors while unload occurring
2921 if(!Ext){// !window[xname]){ ==> can't we do this?
2924 e = Ext.EventObject.setEvent(e);
2927 if(!(t = e.getTarget(o.delegate, el))){
2936 if (o.preventDefault) {
2939 if (o.stopPropagation) {
2940 e.stopPropagation();
2946 fn.call(scope || el, e, t, o);
2949 h = createTargeted(h, o);
2952 h = createDelayed(h, o, fn);
2955 h = createSingle(h, el, ename, fn, scope);
2958 task = new Ext.util.DelayedTask(h);
2959 h = createBuffered(h, o, task);
2962 addListener(el, ename, fn, task, h, scope);
2968 * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
2969 * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
2970 * @param {String/HTMLElement} el The html element or id to assign the event handler to.
2971 * @param {String} eventName The name of the event to listen for.
2972 * @param {Function} handler The handler function the event invokes. This function is passed
2973 * the following parameters:<ul>
2974 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
2975 * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
2976 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
2977 * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
2979 * @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>.
2980 * @param {Object} options (optional) An object containing handler configuration properties.
2981 * This may contain any of the following properties:<ul>
2982 * <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>
2983 * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
2984 * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
2985 * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
2986 * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
2987 * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
2988 * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
2989 * <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>
2990 * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2991 * by the specified number of milliseconds. If the event fires again within that time, the original
2992 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2993 * <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>
2995 * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
2997 addListener : function(element, eventName, fn, scope, options){
2998 if(typeof eventName == 'object'){
2999 var o = eventName, e, val;
3002 if(!propRe.test(e)){
3003 if(Ext.isFunction(val)){
3005 listen(element, e, o, val, o.scope);
3007 // individual options
3008 listen(element, e, val);
3013 listen(element, eventName, options, fn, scope);
3018 * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
3019 * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
3020 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
3021 * @param {String} eventName The name of the event.
3022 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
3023 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
3024 * then this must refer to the same object.
3026 removeListener : function(el, eventName, fn, scope){
3027 el = Ext.getDom(el);
3029 f = el && (Ext.elCache[id].events)[eventName] || [],
3030 wrap, i, l, k, len, fnc;
3032 for (i = 0, len = f.length; i < len; i++) {
3034 /* 0 = Original Function,
3035 1 = Event Manager Wrapped Function,
3037 3 = Adapter Wrapped Function,
3040 if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) {
3044 k = fn.tasks && fn.tasks.length;
3047 fn.tasks[k].cancel();
3052 E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);
3054 // jQuery workaround that should be removed from Ext Core
3055 if(wrap && el.addEventListener && eventName == "mousewheel"){
3056 el.removeEventListener("DOMMouseScroll", wrap, false);
3059 // fix stopped mousedowns on the document
3060 if(wrap && el == DOC && eventName == "mousedown"){
3061 Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3065 if (f.length === 0) {
3066 delete Ext.elCache[id].events[eventName];
3068 for (k in Ext.elCache[id].events) {
3071 Ext.elCache[id].events = {};
3078 * Removes all event handers from an element. Typically you will use {@link Ext.Element#removeAllListeners}
3079 * directly on an Element in favor of calling this version.
3080 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
3082 removeAll : function(el){
3083 el = Ext.getDom(el);
3085 ec = Ext.elCache[id] || {},
3086 es = ec.events || {},
3087 f, i, len, ename, fn, k, wrap;
3090 if(es.hasOwnProperty(ename)){
3092 /* 0 = Original Function,
3093 1 = Event Manager Wrapped Function,
3095 3 = Adapter Wrapped Function,
3098 for (i = 0, len = f.length; i < len; i++) {
3103 if(fn[0].tasks && (k = fn[0].tasks.length)) {
3105 fn[0].tasks[k].cancel();
3110 E.un(el, ename, E.extAdapter ? fn[3] : wrap);
3112 // jQuery workaround that should be removed from Ext Core
3113 if(el.addEventListener && wrap && ename == "mousewheel"){
3114 el.removeEventListener("DOMMouseScroll", wrap, false);
3117 // fix stopped mousedowns on the document
3118 if(wrap && el == DOC && ename == "mousedown"){
3119 Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3124 if (Ext.elCache[id]) {
3125 Ext.elCache[id].events = {};
3129 getListeners : function(el, eventName) {
3130 el = Ext.getDom(el);
3132 ec = Ext.elCache[id] || {},
3133 es = ec.events || {},
3135 if (es && es[eventName]) {
3136 return es[eventName];
3142 purgeElement : function(el, recurse, eventName) {
3143 el = Ext.getDom(el);
3145 ec = Ext.elCache[id] || {},
3146 es = ec.events || {},
3149 if (es && es.hasOwnProperty(eventName)) {
3151 for (i = 0, len = f.length; i < len; i++) {
3152 Ext.EventManager.removeListener(el, eventName, f[i][0]);
3156 Ext.EventManager.removeAll(el);
3158 if (recurse && el && el.childNodes) {
3159 for (i = 0, len = el.childNodes.length; i < len; i++) {
3160 Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);
3165 _unload : function() {
3167 for (el in Ext.elCache) {
3168 Ext.EventManager.removeAll(el);
3171 delete Ext.Element._flyweights;
3173 // Abort any outstanding Ajax requests
3177 ajax = Ext.lib.Ajax;
3178 (typeof ajax.conn == 'object') ? conn = ajax.conn : conn = {};
3182 ajax.abort({conn: c, tId: tid});
3187 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
3188 * accessed shorthanded as Ext.onReady().
3189 * @param {Function} fn The method the event invokes.
3190 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3191 * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3192 * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3194 onDocumentReady : function(fn, scope, options){
3195 if(Ext.isReady){ // if it already fired or document.body is present
3196 docReadyEvent || (docReadyEvent = new Ext.util.Event());
3197 docReadyEvent.addListener(fn, scope, options);
3198 docReadyEvent.fire();
3199 docReadyEvent.listeners = [];
3204 options = options || {};
3205 options.delay = options.delay || 1;
3206 docReadyEvent.addListener(fn, scope, options);
3211 * Forces a document ready state transition for the framework. Used when Ext is loaded
3212 * into a DOM structure AFTER initial page load (Google API or other dynamic load scenario.
3213 * Any pending 'onDocumentReady' handlers will be fired (if not already handled).
3215 fireDocReady : fireDocReady
3218 * Appends an event handler to an element. Shorthand for {@link #addListener}.
3219 * @param {String/HTMLElement} el The html element or id to assign the event handler to
3220 * @param {String} eventName The name of the event to listen for.
3221 * @param {Function} handler The handler function the event invokes.
3222 * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.
3223 * @param {Object} options (optional) An object containing standard {@link #addListener} options
3224 * @member Ext.EventManager
3227 pub.on = pub.addListener;
3229 * Removes an event handler from an element. Shorthand for {@link #removeListener}.
3230 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
3231 * @param {String} eventName The name of the event.
3232 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #on} call.</b>
3233 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
3234 * then this must refer to the same object.
3235 * @member Ext.EventManager
3238 pub.un = pub.removeListener;
3240 pub.stoppedMouseDownEvent = new Ext.util.Event();
3244 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
3245 * @param {Function} fn The method the event invokes.
3246 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3247 * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3248 * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3252 Ext.onReady = Ext.EventManager.onDocumentReady;
3255 //Initialize doc classes
3258 var initExtCss = function(){
3259 // find the body element
3260 var bd = document.body || document.getElementsByTagName('body')[0];
3261 if(!bd){ return false; }
3263 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
3264 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
3265 : Ext.isOpera ? "ext-opera"
3266 : Ext.isWebKit ? "ext-webkit" : ""];
3269 cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
3270 }else if(Ext.isChrome){
3271 cls.push("ext-chrome");
3275 cls.push("ext-mac");
3278 cls.push("ext-linux");
3281 if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
3282 var p = bd.parentNode;
3284 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';
3287 bd.className += cls.join(' ');
3292 Ext.onReady(initExtCss);
3298 * @class Ext.EventObject
3299 * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
3300 * wraps the browser's native event-object normalizing cross-browser differences,
3301 * such as which mouse button is clicked, keys pressed, mechanisms to stop
3302 * event-propagation along with a method to prevent default actions from taking place.
3303 * <p>For example:</p>
3305 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
3307 var target = e.getTarget(); // same as t (the target HTMLElement)
3310 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.Element}
3311 myDiv.on( // 'on' is shorthand for addListener
3312 "click", // perform an action on click of myDiv
3313 handleClick // reference to the action handler
3315 // other methods to do the same:
3316 Ext.EventManager.on("myDiv", 'click', handleClick);
3317 Ext.EventManager.addListener("myDiv", 'click', handleClick);
3321 Ext.EventObject = function(){
3322 var E = Ext.lib.Event,
3323 clickRe = /(dbl)?click/,
3324 // safari keypress events for special keys return bad keycodes
3328 63235 : 39, // right
3331 63276 : 33, // page up
3332 63277 : 34, // page down
3333 63272 : 46, // delete
3337 // normalize button clicks
3338 btnMap = Ext.isIE ? {1:0,4:1,2:2} :
3339 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
3341 Ext.EventObjectImpl = function(e){
3343 this.setEvent(e.browserEvent || e);
3347 Ext.EventObjectImpl.prototype = {
3349 setEvent : function(e){
3351 if(e == me || (e && e.browserEvent)){ // already wrapped
3354 me.browserEvent = e;
3356 // normalize buttons
3357 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
3358 if(clickRe.test(e.type) && me.button == -1){
3362 me.shiftKey = e.shiftKey;
3363 // mac metaKey behaves like ctrlKey
3364 me.ctrlKey = e.ctrlKey || e.metaKey || false;
3365 me.altKey = e.altKey;
3366 // in getKey these will be normalized for the mac
3367 me.keyCode = e.keyCode;
3368 me.charCode = e.charCode;
3369 // cache the target for the delayed and or buffered events
3370 me.target = E.getTarget(e);
3375 me.shiftKey = false;
3387 * Stop the event (preventDefault and stopPropagation)
3389 stopEvent : function(){
3391 if(me.browserEvent){
3392 if(me.browserEvent.type == 'mousedown'){
3393 Ext.EventManager.stoppedMouseDownEvent.fire(me);
3395 E.stopEvent(me.browserEvent);
3400 * Prevents the browsers default handling of the event.
3402 preventDefault : function(){
3403 if(this.browserEvent){
3404 E.preventDefault(this.browserEvent);
3409 * Cancels bubbling of the event.
3411 stopPropagation : function(){
3413 if(me.browserEvent){
3414 if(me.browserEvent.type == 'mousedown'){
3415 Ext.EventManager.stoppedMouseDownEvent.fire(me);
3417 E.stopPropagation(me.browserEvent);
3422 * Gets the character code for the event.
3425 getCharCode : function(){
3426 return this.charCode || this.keyCode;
3430 * Returns a normalized keyCode for the event.
3431 * @return {Number} The key code
3433 getKey : function(){
3434 return this.normalizeKey(this.keyCode || this.charCode)
3438 normalizeKey: function(k){
3439 return Ext.isSafari ? (safariKeys[k] || k) : k;
3443 * Gets the x coordinate of the event.
3446 getPageX : function(){
3451 * Gets the y coordinate of the event.
3454 getPageY : function(){
3459 * Gets the page coordinates of the event.
3460 * @return {Array} The xy values like [x, y]
3467 * Gets the target for the event.
3468 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
3469 * @param {Number/Mixed} maxDepth (optional) The max depth to
3470 search as a number or element (defaults to 10 || document.body)
3471 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
3472 * @return {HTMLelement}
3474 getTarget : function(selector, maxDepth, returnEl){
3475 return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
3479 * Gets the related target.
3480 * @return {HTMLElement}
3482 getRelatedTarget : function(){
3483 return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
3487 * Normalizes mouse wheel delta across browsers
3488 * @return {Number} The delta
3490 getWheelDelta : function(){
3491 var e = this.browserEvent;
3493 if(e.wheelDelta){ /* IE/Opera. */
3494 delta = e.wheelDelta/120;
3495 }else if(e.detail){ /* Mozilla case. */
3496 delta = -e.detail/3;
3502 * 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.
3503 * Example usage:<pre><code>
3504 // Handle click on any child of an element
3505 Ext.getBody().on('click', function(e){
3506 if(e.within('some-el')){
3507 alert('Clicked on a child of some-el!');
3511 // Handle click directly on an element, ignoring clicks on child nodes
3512 Ext.getBody().on('click', function(e,t){
3513 if((t.id == 'some-el') && !e.within(t, true)){
3514 alert('Clicked directly on some-el!');
3518 * @param {Mixed} el The id, DOM element or Ext.Element to check
3519 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
3520 * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
3523 within : function(el, related, allowEl){
3525 var t = this[related ? "getRelatedTarget" : "getTarget"]();
3526 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
3532 return new Ext.EventObjectImpl();
3535 * @class Ext.EventManager
3537 Ext.apply(Ext.EventManager, function(){
3543 propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
3546 // note 1: IE fires ONLY the keydown event on specialkey autorepeat
3547 // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
3548 // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
3549 useKeydown = Ext.isWebKit ?
3550 Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
3551 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
3555 doResizeEvent: function(){
3556 var h = D.getViewHeight(),
3557 w = D.getViewWidth();
3559 //whacky problem in IE where the resize event will fire even though the w/h are the same.
3560 if(curHeight != h || curWidth != w){
3561 resizeEvent.fire(curWidth = w, curHeight = h);
3566 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
3567 * passes new viewport width and height to handlers.
3568 * @param {Function} fn The handler function the window resize event invokes.
3569 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3570 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
3572 onWindowResize : function(fn, scope, options){
3574 resizeEvent = new Ext.util.Event();
3575 resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
3576 Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
3578 resizeEvent.addListener(fn, scope, options);
3581 // exposed only to allow manual firing
3582 fireWindowResize : function(){
3584 resizeTask.delay(100);
3589 * 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.
3590 * @param {Function} fn The function the event invokes.
3591 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3592 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
3594 onTextResize : function(fn, scope, options){
3596 textEvent = new Ext.util.Event();
3597 var textEl = new Ext.Element(document.createElement('div'));
3598 textEl.dom.className = 'x-text-resize';
3599 textEl.dom.innerHTML = 'X';
3600 textEl.appendTo(document.body);
3601 textSize = textEl.dom.offsetHeight;
3602 setInterval(function(){
3603 if(textEl.dom.offsetHeight != textSize){
3604 textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
3606 }, this.textResizeInterval);
3608 textEvent.addListener(fn, scope, options);
3612 * Removes the passed window resize listener.
3613 * @param {Function} fn The method the event invokes
3614 * @param {Object} scope The scope of handler
3616 removeResizeListener : function(fn, scope){
3618 resizeEvent.removeListener(fn, scope);
3623 fireResize : function(){
3625 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
3630 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
3632 textResizeInterval : 50,
3635 * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
3639 // protected for use inside the framework
3640 // detects whether we should use keydown or keypress based on the browser.
3641 useKeydown: useKeydown
3645 Ext.EventManager.on = Ext.EventManager.addListener;
3648 Ext.apply(Ext.EventObjectImpl.prototype, {
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 /** Key constant @type Number */
3661 /** Key constant @type Number */
3663 CONTROL : 17, // legacy
3664 /** Key constant @type Number */
3666 /** Key constant @type Number */
3668 /** Key constant @type Number */
3670 /** Key constant @type Number */
3672 /** Key constant @type Number */
3674 /** Key constant @type Number */
3676 PAGEUP : 33, // legacy
3677 /** Key constant @type Number */
3679 PAGEDOWN : 34, // legacy
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 */
3822 /** Key constant @type Number */
3824 /** Key constant @type Number */
3828 isNavKeyPress : function(){
3830 k = this.normalizeKey(me.keyCode);
3831 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
3837 isSpecialKey : function(){
3838 var k = this.normalizeKey(this.keyCode);
3839 return (this.type == 'keypress' && this.ctrlKey) ||
3840 this.isNavKeyPress() ||
3841 (k == this.BACKSPACE) || // Backspace
3842 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
3843 (k >= 44 && k <= 46); // Print Screen, Insert, Delete
3846 getPoint : function(){
3847 return new Ext.lib.Point(this.xy[0], this.xy[1]);
3851 * Returns true if the control, meta, shift or alt key was pressed during this event.
3854 hasModifier : function(){
3855 return ((this.ctrlKey || this.altKey) || this.shiftKey);
3858 * @class Ext.Element
3859 * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
3860 * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
3861 * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
3862 * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
3863 * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
3867 var el = Ext.get("my-div");
3869 // by DOM element reference
3870 var el = Ext.get(myDivElement);
3872 * <b>Animations</b><br />
3873 * <p>When an element is manipulated, by default there is no animation.</p>
3875 var el = Ext.get("my-div");
3880 * <p>Many of the functions for manipulating an element have an optional "animate" parameter. This
3881 * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
3883 // default animation
3884 el.setWidth(100, true);
3887 * <p>To configure the effects, an object literal with animation options to use as the Element animation
3888 * configuration object can also be specified. Note that the supported Element animation configuration
3889 * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects. The supported
3890 * Element animation configuration options are:</p>
3892 Option Default Description
3893 --------- -------- ---------------------------------------------
3894 {@link Ext.Fx#duration duration} .35 The duration of the animation in seconds
3895 {@link Ext.Fx#easing easing} easeOut The easing method
3896 {@link Ext.Fx#callback callback} none A function to execute when the anim completes
3897 {@link Ext.Fx#scope scope} this The scope (this) of the callback function
3901 // Element animation options object
3903 {@link Ext.Fx#duration duration}: 1,
3904 {@link Ext.Fx#easing easing}: 'elasticIn',
3905 {@link Ext.Fx#callback callback}: this.foo,
3906 {@link Ext.Fx#scope scope}: this
3908 // animation with some options set
3909 el.setWidth(100, opt);
3911 * <p>The Element animation object being used for the animation will be set on the options
3912 * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
3914 // using the "anim" property to get the Anim object
3915 if(opt.anim.isAnimated()){
3919 * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
3920 * <p><b> Composite (Collections of) Elements</b></p>
3921 * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
3922 * @constructor Create a new Element directly.
3923 * @param {String/HTMLElement} element
3924 * @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).
3929 Ext.Element = function(element, forceNew){
3930 var dom = typeof element == "string" ?
3931 DOC.getElementById(element) : element,
3934 if(!dom) return null;
3938 if(!forceNew && id && Ext.elCache[id]){ // element object already exists
3939 return Ext.elCache[id].el;
3949 * The DOM element ID
3952 this.id = id || Ext.id(dom);
3955 var DH = Ext.DomHelper,
3961 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
3962 * @param {Object} o The object with the attributes
3963 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
3964 * @return {Ext.Element} this
3966 set : function(o, useSet){
3970 useSet = (useSet !== false) && !!el.setAttribute;
3973 if (o.hasOwnProperty(attr)) {
3975 if (attr == 'style') {
3976 DH.applyStyles(el, val);
3977 } else if (attr == 'cls') {
3979 } else if (useSet) {
3980 el.setAttribute(attr, val);
3992 * Fires when a mouse click is detected within the element.
3993 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3994 * @param {HtmlElement} t The target of the event.
3995 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3998 * @event contextmenu
3999 * Fires when a right click is detected within the element.
4000 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4001 * @param {HtmlElement} t The target of the event.
4002 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4006 * Fires when a mouse double click is detected within the element.
4007 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4008 * @param {HtmlElement} t The target of the event.
4009 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4013 * Fires when a mousedown is detected within the element.
4014 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4015 * @param {HtmlElement} t The target of the event.
4016 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4020 * Fires when a mouseup is detected within the element.
4021 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4022 * @param {HtmlElement} t The target of the event.
4023 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4027 * Fires when a mouseover is detected within the element.
4028 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4029 * @param {HtmlElement} t The target of the event.
4030 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4034 * Fires when a mousemove is detected with the element.
4035 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4036 * @param {HtmlElement} t The target of the event.
4037 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4041 * Fires when a mouseout is detected with the element.
4042 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4043 * @param {HtmlElement} t The target of the event.
4044 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4048 * Fires when the mouse enters the element.
4049 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4050 * @param {HtmlElement} t The target of the event.
4051 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4055 * Fires when the mouse leaves the element.
4056 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4057 * @param {HtmlElement} t The target of the event.
4058 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4064 * Fires when a keypress is detected within the element.
4065 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4066 * @param {HtmlElement} t The target of the event.
4067 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4071 * Fires when a keydown is detected within the element.
4072 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4073 * @param {HtmlElement} t The target of the event.
4074 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4078 * Fires when a keyup is detected within the element.
4079 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4080 * @param {HtmlElement} t The target of the event.
4081 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4085 // HTML frame/object events
4088 * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
4089 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4090 * @param {HtmlElement} t The target of the event.
4091 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4095 * 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.
4096 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4097 * @param {HtmlElement} t The target of the event.
4098 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4102 * Fires when an object/image is stopped from loading before completely loaded.
4103 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4104 * @param {HtmlElement} t The target of the event.
4105 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4109 * Fires when an object/image/frame cannot be loaded properly.
4110 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4111 * @param {HtmlElement} t The target of the event.
4112 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4116 * Fires when a document view is resized.
4117 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4118 * @param {HtmlElement} t The target of the event.
4119 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4123 * Fires when a document view is scrolled.
4124 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4125 * @param {HtmlElement} t The target of the event.
4126 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4132 * Fires when a user selects some text in a text field, including input and textarea.
4133 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4134 * @param {HtmlElement} t The target of the event.
4135 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4139 * Fires when a control loses the input focus and its value has been modified since gaining focus.
4140 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4141 * @param {HtmlElement} t The target of the event.
4142 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4146 * Fires when a form is submitted.
4147 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4148 * @param {HtmlElement} t The target of the event.
4149 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4153 * Fires when a form is reset.
4154 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4155 * @param {HtmlElement} t The target of the event.
4156 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4160 * Fires when an element receives focus either via the pointing device or by tab navigation.
4161 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4162 * @param {HtmlElement} t The target of the event.
4163 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4167 * Fires when an element loses focus either via the pointing device or by tabbing navigation.
4168 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4169 * @param {HtmlElement} t The target of the event.
4170 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4173 // User Interface events
4176 * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
4177 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4178 * @param {HtmlElement} t The target of the event.
4179 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4182 * @event DOMFocusOut
4183 * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
4184 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4185 * @param {HtmlElement} t The target of the event.
4186 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4189 * @event DOMActivate
4190 * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
4191 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4192 * @param {HtmlElement} t The target of the event.
4193 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4196 // DOM Mutation events
4198 * @event DOMSubtreeModified
4199 * Where supported. Fires when the subtree is modified.
4200 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4201 * @param {HtmlElement} t The target of the event.
4202 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4205 * @event DOMNodeInserted
4206 * Where supported. Fires when a node has been added as a child of another node.
4207 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4208 * @param {HtmlElement} t The target of the event.
4209 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4212 * @event DOMNodeRemoved
4213 * Where supported. Fires when a descendant node of the element is removed.
4214 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4215 * @param {HtmlElement} t The target of the event.
4216 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4219 * @event DOMNodeRemovedFromDocument
4220 * Where supported. Fires when a node is being removed from a document.
4221 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4222 * @param {HtmlElement} t The target of the event.
4223 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4226 * @event DOMNodeInsertedIntoDocument
4227 * Where supported. Fires when a node is being inserted into a document.
4228 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4229 * @param {HtmlElement} t The target of the event.
4230 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4233 * @event DOMAttrModified
4234 * Where supported. Fires when an attribute has been modified.
4235 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4236 * @param {HtmlElement} t The target of the event.
4237 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4240 * @event DOMCharacterDataModified
4241 * Where supported. Fires when the character data has been modified.
4242 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4243 * @param {HtmlElement} t The target of the event.
4244 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4248 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
4254 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
4255 * @param {String} selector The simple selector to test
4256 * @return {Boolean} True if this element matches the selector, else false
4258 is : function(simpleSelector){
4259 return Ext.DomQuery.is(this.dom, simpleSelector);
4263 * Tries to focus the element. Any exceptions are caught and ignored.
4264 * @param {Number} defer (optional) Milliseconds to defer the focus
4265 * @return {Ext.Element} this
4267 focus : function(defer, /* private */ dom) {
4269 dom = dom || me.dom;
4272 me.focus.defer(defer, null, [null, dom]);
4281 * Tries to blur the element. Any exceptions are caught and ignored.
4282 * @return {Ext.Element} this
4292 * Returns the value of the "value" attribute
4293 * @param {Boolean} asNumber true to parse the value as a number
4294 * @return {String/Number}
4296 getValue : function(asNumber){
4297 var val = this.dom.value;
4298 return asNumber ? parseInt(val, 10) : val;
4302 * Appends an event handler to this element. The shorthand version {@link #on} is equivalent.
4303 * @param {String} eventName The name of event to handle.
4304 * @param {Function} fn The handler function the event invokes. This function is passed
4305 * the following parameters:<ul>
4306 * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
4307 * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
4308 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
4309 * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
4311 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4312 * <b>If omitted, defaults to this Element.</b>.
4313 * @param {Object} options (optional) An object containing handler configuration properties.
4314 * This may contain any of the following properties:<ul>
4315 * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4316 * <b>If omitted, defaults to this Element.</b></div></li>
4317 * <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>
4318 * <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>
4319 * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
4320 * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
4321 * <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>
4322 * <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>
4323 * <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>
4324 * <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>
4325 * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
4326 * by the specified number of milliseconds. If the event fires again within that time, the original
4327 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
4330 * <b>Combining Options</b><br>
4331 * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
4332 * addListener. The two are equivalent. Using the options argument, it is possible to combine different
4333 * types of listeners:<br>
4335 * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
4336 * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
4338 el.on('click', this.onClick, this, {
4343 });</code></pre></p>
4345 * <b>Attaching multiple handlers in 1 call</b><br>
4346 * The method also allows for a single argument to be passed which is a config object containing properties
4347 * which specify multiple handlers.</p>
4357 fn: this.onMouseOver,
4361 fn: this.onMouseOut,
4366 * Or a shorthand syntax:<br>
4367 * Code:<pre><code></p>
4369 'click' : this.onClick,
4370 'mouseover' : this.onMouseOver,
4371 'mouseout' : this.onMouseOut,
4375 * <p><b>delegate</b></p>
4376 * <p>This is a configuration option that you can pass along when registering a handler for
4377 * an event to assist with event delegation. Event delegation is a technique that is used to
4378 * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
4379 * for a container element as opposed to each element within a container. By setting this
4380 * configuration option to a simple selector, the target element will be filtered to look for
4381 * a descendant of the target.
4382 * For example:<pre><code>
4383 // using this markup:
4385 <p id='p1'>paragraph one</p>
4386 <p id='p2' class='clickable'>paragraph two</p>
4387 <p id='p3'>paragraph three</p>
4389 // utilize event delegation to registering just one handler on the container element:
4390 el = Ext.get('elId');
4395 console.info(t.id); // 'p2'
4399 // filter the target element to be a descendant with the class 'clickable'
4400 delegate: '.clickable'
4404 * @return {Ext.Element} this
4406 addListener : function(eventName, fn, scope, options){
4407 Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
4412 * Removes an event handler from this element. The shorthand version {@link #un} is equivalent.
4413 * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
4414 * listener, the same scope must be specified here.
4417 el.removeListener('click', this.handlerFn);
4419 el.un('click', this.handlerFn);
4421 * @param {String} eventName The name of the event from which to remove the handler.
4422 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4423 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4424 * then this must refer to the same object.
4425 * @return {Ext.Element} this
4427 removeListener : function(eventName, fn, scope){
4428 Ext.EventManager.removeListener(this.dom, eventName, fn, scope || this);
4433 * Removes all previous added listeners from this element
4434 * @return {Ext.Element} this
4436 removeAllListeners : function(){
4437 Ext.EventManager.removeAll(this.dom);
4442 * Recursively removes all previous added listeners from this element and its children
4443 * @return {Ext.Element} this
4445 purgeAllListeners : function() {
4446 Ext.EventManager.purgeElement(this, true);
4450 * @private Test if size has a unit, otherwise appends the default
4452 addUnits : function(size){
4453 if(size === "" || size == "auto" || size === undefined){
4455 } else if(!isNaN(size) || !unitPattern.test(size)){
4456 size = size + (this.defaultUnit || 'px');
4462 * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
4463 * 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>
4464 * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt><script></tt> elements. This is a browser restriction.</p>
4465 * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
4466 * exactly how to request the HTML.
4467 * @return {Ext.Element} this
4469 load : function(url, params, cb){
4470 Ext.Ajax.request(Ext.apply({
4472 url: url.url || url,
4475 indicatorText: url.indicatorText || ''
4476 }, Ext.isObject(url) ? url : {}));
4481 * Tests various css rules/browsers to determine if this element uses a border box
4484 isBorderBox : function(){
4485 return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
4489 * <p>Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode}</p>
4491 remove : function(){
4497 Ext.removeNode(dom);
4502 * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
4503 * @param {Function} overFn The function to call when the mouse enters the Element.
4504 * @param {Function} outFn The function to call when the mouse leaves the Element.
4505 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
4506 * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
4507 * @return {Ext.Element} this
4509 hover : function(overFn, outFn, scope, options){
4511 me.on('mouseenter', overFn, scope || me.dom, options);
4512 me.on('mouseleave', outFn, scope || me.dom, options);
4517 * Returns true if this element is an ancestor of the passed element
4518 * @param {HTMLElement/String} el The element to check
4519 * @return {Boolean} True if this element is an ancestor of el, else false
4521 contains : function(el){
4522 return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
4526 * Returns the value of a namespaced attribute from the element's underlying DOM node.
4527 * @param {String} namespace The namespace in which to look for the attribute
4528 * @param {String} name The attribute name
4529 * @return {String} The attribute value
4532 getAttributeNS : function(ns, name){
4533 return this.getAttribute(name, ns);
4537 * Returns the value of an attribute from the element's underlying DOM node.
4538 * @param {String} name The attribute name
4539 * @param {String} namespace (optional) The namespace in which to look for the attribute
4540 * @return {String} The attribute value
4542 getAttribute : Ext.isIE ? function(name, ns){
4544 type = typeof d[ns + ":" + name];
4546 if(['undefined', 'unknown'].indexOf(type) == -1){
4547 return d[ns + ":" + name];
4550 } : function(name, ns){
4552 return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
4556 * Update the innerHTML of this element
4557 * @param {String} html The new HTML
4558 * @return {Ext.Element} this
4560 update : function(html) {
4562 this.dom.innerHTML = html;
4568 var ep = El.prototype;
4570 El.addMethods = function(o){
4575 * Appends an event handler (shorthand for {@link #addListener}).
4576 * @param {String} eventName The name of event to handle.
4577 * @param {Function} fn The handler function the event invokes.
4578 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
4579 * @param {Object} options (optional) An object containing standard {@link #addListener} options
4580 * @member Ext.Element
4583 ep.on = ep.addListener;
4586 * Removes an event handler from this element (see {@link #removeListener} for additional notes).
4587 * @param {String} eventName The name of the event from which to remove the handler.
4588 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4589 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4590 * then this must refer to the same object.
4591 * @return {Ext.Element} this
4592 * @member Ext.Element
4595 ep.un = ep.removeListener;
4598 * true to automatically adjust width and height settings for box-model issues (default to true)
4600 ep.autoBoxAdjust = true;
4603 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
4611 * Retrieves Ext.Element objects.
4612 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4613 * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4614 * its ID, use {@link Ext.ComponentMgr#get}.</p>
4615 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4616 * object was recreated with the same id via AJAX or DOM.</p>
4617 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4618 * @return {Element} The Element object (or null if no matching element was found)
4620 * @member Ext.Element
4623 El.get = function(el){
4627 if(!el){ return null; }
4628 if (typeof el == "string") { // element id
4629 if (!(elm = DOC.getElementById(el))) {
4632 if (EC[el] && EC[el].el) {
4636 ex = El.addToCache(new El(elm));
4639 } else if (el.tagName) { // dom element
4643 if (EC[id] && EC[id].el) {
4647 ex = El.addToCache(new El(el));
4650 } else if (el instanceof El) {
4652 // refresh dom element in case no longer valid,
4653 // catch case where it hasn't been appended
4655 // If an el instance is passed, don't pass to getElementById without some kind of id
4656 if (Ext.isIE && (el.id == undefined || el.id == '')) {
4659 el.dom = DOC.getElementById(el.id) || el.dom;
4663 } else if(el.isComposite) {
4665 } else if(Ext.isArray(el)) {
4666 return El.select(el);
4667 } else if(el == DOC) {
4668 // create a bogus element object representing the document object
4670 var f = function(){};
4671 f.prototype = El.prototype;
4680 El.addToCache = function(el, id){
4690 // private method for getting and setting element data
4691 El.data = function(el, key, value){
4696 var c = EC[el.id].data;
4697 if(arguments.length == 2){
4700 return (c[key] = value);
4705 // Garbage collection - uncache elements/purge listeners on orphaned elements
4706 // so we don't hold a reference and cause the browser to retain them
4707 function garbageCollect(){
4708 if(!Ext.enableGarbageCollector){
4709 clearInterval(El.collectorThreadId);
4723 // -------------------------------------------------------
4724 // Determining what is garbage:
4725 // -------------------------------------------------------
4727 // dom node is null, definitely garbage
4728 // -------------------------------------------------------
4730 // no parentNode == direct orphan, definitely garbage
4731 // -------------------------------------------------------
4732 // !d.offsetParent && !document.getElementById(eid)
4733 // display none elements have no offsetParent so we will
4734 // also try to look it up by it's id. However, check
4735 // offsetParent first so we don't do unneeded lookups.
4736 // This enables collection of elements that are not orphans
4737 // directly, but somewhere up the line they have an orphan
4739 // -------------------------------------------------------
4740 if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
4741 if(Ext.enableListenerCollection){
4742 Ext.EventManager.removeAll(d);
4747 // Cleanup IE Object leaks
4753 EC = Ext.elCache = t;
4757 El.collectorThreadId = setInterval(garbageCollect, 30000);
4759 var flyFn = function(){};
4760 flyFn.prototype = El.prototype;
4763 El.Flyweight = function(dom){
4767 El.Flyweight.prototype = new flyFn();
4768 El.Flyweight.prototype.isFlyweight = true;
4769 El._flyweights = {};
4772 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4773 * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4774 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4775 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4776 * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4777 * @param {String/HTMLElement} el The dom node or id
4778 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4779 * (e.g. internally Ext uses "_global")
4780 * @return {Element} The shared Element object (or null if no matching element was found)
4781 * @member Ext.Element
4784 El.fly = function(el, named){
4786 named = named || '_global';
4788 if (el = Ext.getDom(el)) {
4789 (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
4790 ret = El._flyweights[named];
4796 * Retrieves Ext.Element objects.
4797 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4798 * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4799 * its ID, use {@link Ext.ComponentMgr#get}.</p>
4800 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4801 * object was recreated with the same id via AJAX or DOM.</p>
4802 * Shorthand of {@link Ext.Element#get}
4803 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4804 * @return {Element} The Element object (or null if no matching element was found)
4811 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4812 * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4813 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4814 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4815 * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4816 * @param {String/HTMLElement} el The dom node or id
4817 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4818 * (e.g. internally Ext uses "_global")
4819 * @return {Element} The shared Element object (or null if no matching element was found)
4825 // speedy lookup for elements never to box adjust
4826 var noBoxAdjust = Ext.isStrict ? {
4829 input:1, select:1, textarea:1
4831 if(Ext.isIE || Ext.isGecko){
4832 noBoxAdjust['button'] = 1;
4837 * @class Ext.Element
4839 Ext.Element.addMethods({
4841 * Stops the specified event(s) from bubbling and optionally prevents the default action
4842 * @param {String/Array} eventName an event / array of events to stop from bubbling
4843 * @param {Boolean} preventDefault (optional) true to prevent the default action too
4844 * @return {Ext.Element} this
4846 swallowEvent : function(eventName, preventDefault){
4849 e.stopPropagation();
4854 if(Ext.isArray(eventName)){
4855 Ext.each(eventName, function(e) {
4860 me.on(eventName, fn);
4865 * Create an event handler on this element such that when the event fires and is handled by this element,
4866 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
4867 * @param {String} eventName The type of event to relay
4868 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
4869 * for firing the relayed event
4871 relayEvent : function(eventName, observable){
4872 this.on(eventName, function(e){
4873 observable.fireEvent(eventName, e);
4878 * Removes worthless text nodes
4879 * @param {Boolean} forceReclean (optional) By default the element
4880 * keeps track if it has been cleaned already so
4881 * you can call this over and over. However, if you update the element and
4882 * need to force a reclean, you can pass true.
4884 clean : function(forceReclean){
4890 if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){
4895 var nx = n.nextSibling;
4896 if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){
4903 Ext.Element.data(dom, 'isCleaned', true);
4908 * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
4909 * parameter as {@link Ext.Updater#update}
4910 * @return {Ext.Element} this
4913 var um = this.getUpdater();
4914 um.update.apply(um, arguments);
4919 * Gets this element's {@link Ext.Updater Updater}
4920 * @return {Ext.Updater} The Updater
4922 getUpdater : function(){
4923 return this.updateManager || (this.updateManager = new Ext.Updater(this));
4927 * Update the innerHTML of this element, optionally searching for and processing scripts
4928 * @param {String} html The new HTML
4929 * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
4930 * @param {Function} callback (optional) For async script loading you can be notified when the update completes
4931 * @return {Ext.Element} this
4933 update : function(html, loadScripts, callback){
4939 if(loadScripts !== true){
4940 this.dom.innerHTML = html;
4941 if(typeof callback == 'function'){
4950 html += '<span id="' + id + '"></span>';
4952 Ext.lib.Event.onAvailable(id, function(){
4954 hd = DOC.getElementsByTagName("head")[0],
4955 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
4956 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
4957 typeRe = /\stype=([\'\"])(.*?)\1/i,
4965 while((match = re.exec(html))){
4967 srcMatch = attrs ? attrs.match(srcRe) : false;
4968 if(srcMatch && srcMatch[2]){
4969 s = DOC.createElement("script");
4970 s.src = srcMatch[2];
4971 typeMatch = attrs.match(typeRe);
4972 if(typeMatch && typeMatch[2]){
4973 s.type = typeMatch[2];
4976 }else if(match[2] && match[2].length > 0){
4977 if(window.execScript) {
4978 window.execScript(match[2]);
4980 window.eval(match[2]);
4984 el = DOC.getElementById(id);
4985 if(el){Ext.removeNode(el);}
4986 if(typeof callback == 'function'){
4990 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
4994 // inherit docs, overridden so we can add removeAnchor
4995 removeAllListeners : function(){
4996 this.removeAnchor();
4997 Ext.EventManager.removeAll(this.dom);
5002 * Creates a proxy element of this element
5003 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
5004 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
5005 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
5006 * @return {Ext.Element} The new proxy element
5008 createProxy : function(config, renderTo, matchBox){
5009 config = (typeof config == 'object') ? config : {tag : "div", cls: config};
5012 proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
5013 Ext.DomHelper.insertBefore(me.dom, config, true);
5015 if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded
5016 proxy.setBox(me.getBox());
5022 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
5024 * @class Ext.Element
5026 Ext.Element.addMethods({
5028 * Gets the x,y coordinates specified by the anchor position on the element.
5029 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo}
5030 * for details on supported anchor positions.
5031 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
5032 * of page coordinates
5033 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
5034 * {width: (target width), height: (target height)} (defaults to the element's current size)
5035 * @return {Array} [x, y] An array containing the element's x and y coordinates
5037 getAnchorXY : function(anchor, local, s){
5038 //Passing a different size is useful for pre-calculating anchors,
5039 //especially for anchored animations that change the el size.
5040 anchor = (anchor || "tl").toLowerCase();
5044 vp = me.dom == document.body || me.dom == document,
5045 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
5046 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),
5050 scroll = me.getScroll(),
5051 extraX = vp ? scroll.left : !local ? o[0] : 0,
5052 extraY = vp ? scroll.top : !local ? o[1] : 0,
5054 c : [r(w * 0.5), r(h * 0.5)],
5055 t : [r(w * 0.5), 0],
5056 l : [0, r(h * 0.5)],
5057 r : [w, r(h * 0.5)],
5058 b : [r(w * 0.5), h],
5066 return [xy[0] + extraX, xy[1] + extraY];
5070 * Anchors an element to another element and realigns it when the window is resized.
5071 * @param {Mixed} element The element to align to.
5072 * @param {String} position The position to align to.
5073 * @param {Array} offsets (optional) Offset the positioning by [x, y]
5074 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
5075 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
5076 * is a number, it is used as the buffer delay (defaults to 50ms).
5077 * @param {Function} callback The function to call after the animation finishes
5078 * @return {Ext.Element} this
5080 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
5083 scroll = !Ext.isEmpty(monitorScroll),
5084 action = function(){
5085 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
5086 Ext.callback(callback, Ext.fly(dom));
5088 anchor = this.getAnchor();
5090 // previous listener anchor, remove it
5091 this.removeAnchor();
5097 Ext.EventManager.onWindowResize(action, null);
5100 Ext.EventManager.on(window, 'scroll', action, null,
5101 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
5103 action.call(me); // align immediately
5108 * Remove any anchor to this element. See {@link #anchorTo}.
5109 * @return {Ext.Element} this
5111 removeAnchor : function(){
5113 anchor = this.getAnchor();
5115 if(anchor && anchor.fn){
5116 Ext.EventManager.removeResizeListener(anchor.fn);
5118 Ext.EventManager.un(window, 'scroll', anchor.fn);
5126 getAnchor : function(){
5127 var data = Ext.Element.data,
5132 var anchor = data(dom, '_anchor');
5135 anchor = data(dom, '_anchor', {});
5141 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
5142 * supported position values.
5143 * @param {Mixed} element The element to align to.
5144 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5145 * @param {Array} offsets (optional) Offset the positioning by [x, y]
5146 * @return {Array} [x, y]
5148 getAlignToXY : function(el, p, o){
5152 throw "Element.alignToXY with an element that doesn't exist";
5156 p = (!p || p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
5164 //constrain the aligned el to viewport if necessary
5168 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
5169 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
5177 docElement = doc.documentElement,
5179 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
5180 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
5181 c = false, //constrain to viewport
5184 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
5187 throw "Element.alignTo with an invalid alignment " + p;
5194 //Subtract the aligned el's internal xy from the target's offset xy
5195 //plus custom offset to get the aligned el's new offset xy
5196 a1 = me.getAnchorXY(p1, true);
5197 a2 = el.getAnchorXY(p2, false);
5199 x = a2[0] - a1[0] + o[0];
5200 y = a2[1] - a1[1] + o[1];
5206 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
5207 //perpendicular to the vp border, allow the aligned el to slide on that border,
5208 //otherwise swap the aligned el to the opposite border of the target.
5210 p1x = p1.charAt(p1.length-1);
5212 p2x = p2.charAt(p2.length-1);
5213 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
5214 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
5217 if (x + w > dw + scrollX) {
5218 x = swapX ? r.left-w : dw+scrollX-w;
5221 x = swapX ? r.right : scrollX;
5223 if (y + h > dh + scrollY) {
5224 y = swapY ? r.top-h : dh+scrollY-h;
5227 y = swapY ? r.bottom : scrollY;
5234 * Aligns this element with another element relative to the specified anchor points. If the other element is the
5235 * document it aligns it to the viewport.
5236 * The position parameter is optional, and can be specified in any one of the following formats:
5238 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
5239 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
5240 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
5241 * deprecated in favor of the newer two anchor syntax below</i>.</li>
5242 * <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
5243 * element's anchor point, and the second value is used as the target's anchor point.</li>
5245 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
5246 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
5247 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
5248 * that specified in order to enforce the viewport constraints.
5249 * Following are all of the supported anchor positions:
5252 ----- -----------------------------
5253 tl The top left corner (default)
5254 t The center of the top edge
5255 tr The top right corner
5256 l The center of the left edge
5257 c In the center of the element
5258 r The center of the right edge
5259 bl The bottom left corner
5260 b The center of the bottom edge
5261 br The bottom right corner
5265 // align el to other-el using the default positioning ("tl-bl", non-constrained)
5266 el.alignTo("other-el");
5268 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
5269 el.alignTo("other-el", "tr?");
5271 // align the bottom right corner of el with the center left edge of other-el
5272 el.alignTo("other-el", "br-l?");
5274 // align the center of el with the bottom left corner of other-el and
5275 // adjust the x position by -6 pixels (and the y position by 0)
5276 el.alignTo("other-el", "c-bl", [-6, 0]);
5278 * @param {Mixed} element The element to align to.
5279 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5280 * @param {Array} offsets (optional) Offset the positioning by [x, y]
5281 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5282 * @return {Ext.Element} this
5284 alignTo : function(element, position, offsets, animate){
5286 return me.setXY(me.getAlignToXY(element, position, offsets),
5287 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
5290 // private ==> used outside of core
5291 adjustForConstraints : function(xy, parent, offsets){
5292 return this.getConstrainToXY(parent || document, false, offsets, xy) || xy;
5295 // private ==> used outside of core
5296 getConstrainToXY : function(el, local, offsets, proposedXY){
5297 var os = {top:0, left:0, bottom:0, right: 0};
5299 return function(el, local, offsets, proposedXY){
5301 offsets = offsets ? Ext.applyIf(offsets, os) : os;
5303 var vw, vh, vx = 0, vy = 0;
5304 if(el.dom == document.body || el.dom == document){
5305 vw =Ext.lib.Dom.getViewWidth();
5306 vh = Ext.lib.Dom.getViewHeight();
5308 vw = el.dom.clientWidth;
5309 vh = el.dom.clientHeight;
5311 var vxy = el.getXY();
5317 var s = el.getScroll();
5319 vx += offsets.left + s.left;
5320 vy += offsets.top + s.top;
5322 vw -= offsets.right;
5323 vh -= offsets.bottom;
5327 xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]),
5328 x = xy[0], y = xy[1],
5329 offset = this.getConstrainOffset(),
5330 w = this.dom.offsetWidth + offset,
5331 h = this.dom.offsetHeight + offset;
5333 // only move it if it needs it
5336 // first validate right/bottom
5345 // then make sure top/left isn't negative
5354 return moved ? [x, y] : false;
5360 // el = Ext.get(el);
5361 // offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
5365 // s = el.getScroll(),
5366 // vxy = el.getXY(),
5367 // vx = offsets.left + s.left,
5368 // vy = offsets.top + s.top,
5369 // vw = -offsets.right,
5370 // vh = -offsets.bottom,
5373 // xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
5376 // w = me.dom.offsetWidth, h = me.dom.offsetHeight,
5377 // moved = false; // only move it if it needs it
5380 // if(el.dom == doc.body || el.dom == doc){
5381 // vw += Ext.lib.Dom.getViewWidth();
5382 // vh += Ext.lib.Dom.getViewHeight();
5384 // vw += el.dom.clientWidth;
5385 // vh += el.dom.clientHeight;
5392 // // first validate right/bottom
5393 // if(x + w > vx + vw){
5397 // if(y + h > vy + vh){
5401 // // then make sure top/left isn't negative
5410 // return moved ? [x, y] : false;
5413 // private, used internally
5414 getConstrainOffset : function(){
5419 * Calculates the x, y to center this element on the screen
5420 * @return {Array} The x, y values [x, y]
5422 getCenterXY : function(){
5423 return this.getAlignToXY(document, 'c-c');
5427 * Centers the Element in either the viewport, or another Element.
5428 * @param {Mixed} centerIn (optional) The element in which to center the element.
5430 center : function(centerIn){
5431 return this.alignTo(centerIn || document, 'c-c');
5435 * @class Ext.Element
5437 Ext.Element.addMethods(function(){
5438 var PARENTNODE = 'parentNode',
5439 NEXTSIBLING = 'nextSibling',
5440 PREVIOUSSIBLING = 'previousSibling',
5446 * 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)
5447 * @param {String} selector The simple selector to test
5448 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
5449 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5450 * @return {HTMLElement} The matching DOM node (or null if no match was found)
5452 findParent : function(simpleSelector, maxDepth, returnEl){
5457 if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {
5460 maxDepth = maxDepth || 50;
5461 if (isNaN(maxDepth)) {
5462 stopEl = Ext.getDom(maxDepth);
5463 maxDepth = Number.MAX_VALUE;
5465 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
5466 if(DQ.is(p, simpleSelector)){
5467 return returnEl ? GET(p) : p;
5476 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
5477 * @param {String} selector The simple selector to test
5478 * @param {Number/Mixed} maxDepth (optional) The max depth to
5479 search as a number or element (defaults to 10 || document.body)
5480 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5481 * @return {HTMLElement} The matching DOM node (or null if no match was found)
5483 findParentNode : function(simpleSelector, maxDepth, returnEl){
5484 var p = Ext.fly(this.dom.parentNode, '_internal');
5485 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
5489 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
5490 * This is a shortcut for findParentNode() that always returns an Ext.Element.
5491 * @param {String} selector The simple selector to test
5492 * @param {Number/Mixed} maxDepth (optional) The max depth to
5493 search as a number or element (defaults to 10 || document.body)
5494 * @return {Ext.Element} The matching DOM node (or null if no match was found)
5496 up : function(simpleSelector, maxDepth){
5497 return this.findParentNode(simpleSelector, maxDepth, true);
5501 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5502 * @param {String} selector The CSS selector
5503 * @return {CompositeElement/CompositeElementLite} The composite element
5505 select : function(selector){
5506 return Ext.Element.select(selector, this.dom);
5510 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
5511 * @param {String} selector The CSS selector
5512 * @return {Array} An array of the matched nodes
5514 query : function(selector){
5515 return DQ.select(selector, this.dom);
5519 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
5520 * @param {String} selector The CSS selector
5521 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5522 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5524 child : function(selector, returnDom){
5525 var n = DQ.selectNode(selector, this.dom);
5526 return returnDom ? n : GET(n);
5530 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
5531 * @param {String} selector The CSS selector
5532 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5533 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5535 down : function(selector, returnDom){
5536 var n = DQ.selectNode(" > " + selector, this.dom);
5537 return returnDom ? n : GET(n);
5541 * Gets the parent node for this element, optionally chaining up trying to match a selector
5542 * @param {String} selector (optional) Find a parent node that matches the passed simple selector
5543 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5544 * @return {Ext.Element/HTMLElement} The parent node or null
5546 parent : function(selector, returnDom){
5547 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);
5551 * Gets the next sibling, skipping text nodes
5552 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5553 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5554 * @return {Ext.Element/HTMLElement} The next sibling or null
5556 next : function(selector, returnDom){
5557 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);
5561 * Gets the previous sibling, skipping text nodes
5562 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5563 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5564 * @return {Ext.Element/HTMLElement} The previous sibling or null
5566 prev : function(selector, returnDom){
5567 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);
5572 * Gets the first child, skipping text nodes
5573 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5574 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5575 * @return {Ext.Element/HTMLElement} The first child or null
5577 first : function(selector, returnDom){
5578 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);
5582 * Gets the last child, skipping text nodes
5583 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5584 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5585 * @return {Ext.Element/HTMLElement} The last child or null
5587 last : function(selector, returnDom){
5588 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);
5591 matchNode : function(dir, start, selector, returnDom){
5592 var n = this.dom[start];
5594 if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){
5595 return !returnDom ? GET(n) : n;
5603 * @class Ext.Element
5605 Ext.Element.addMethods({
5607 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5608 * @param {String} selector The CSS selector
5609 * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)
5610 * @return {CompositeElement/CompositeElementLite} The composite element
5612 select : function(selector, unique){
5613 return Ext.Element.select(selector, unique, this.dom);
5616 * @class Ext.Element
5618 Ext.Element.addMethods(
5620 var GETDOM = Ext.getDom,
5626 * Appends the passed element(s) to this element
5627 * @param {String/HTMLElement/Array/Element/CompositeElement} el
5628 * @return {Ext.Element} this
5630 appendChild: function(el){
5631 return GET(el).appendTo(this);
5635 * Appends this element to the passed element
5636 * @param {Mixed} el The new parent element
5637 * @return {Ext.Element} this
5639 appendTo: function(el){
5640 GETDOM(el).appendChild(this.dom);
5645 * Inserts this element before the passed element in the DOM
5646 * @param {Mixed} el The element before which this element will be inserted
5647 * @return {Ext.Element} this
5649 insertBefore: function(el){
5650 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);
5655 * Inserts this element after the passed element in the DOM
5656 * @param {Mixed} el The element to insert after
5657 * @return {Ext.Element} this
5659 insertAfter: function(el){
5660 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);
5665 * Inserts (or creates) an element (or DomHelper config) as the first child of this element
5666 * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
5667 * @return {Ext.Element} The new child
5669 insertFirst: function(el, returnDom){
5671 if(el.nodeType || el.dom || typeof el == 'string'){ // element
5673 this.dom.insertBefore(el, this.dom.firstChild);
5674 return !returnDom ? GET(el) : el;
5676 return this.createChild(el, this.dom.firstChild, returnDom);
5681 * Replaces the passed element with this element
5682 * @param {Mixed} el The element to replace
5683 * @return {Ext.Element} this
5685 replace: function(el){
5687 this.insertBefore(el);
5693 * Replaces this element with the passed element
5694 * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
5695 * @return {Ext.Element} this
5697 replaceWith: function(el){
5700 if(el.nodeType || el.dom || typeof el == 'string'){
5702 me.dom.parentNode.insertBefore(el, me.dom);
5704 el = DH.insertBefore(me.dom, el);
5707 delete Ext.elCache[me.id];
5708 Ext.removeNode(me.dom);
5709 me.id = Ext.id(me.dom = el);
5710 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);
5715 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
5716 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
5717 * automatically generated with the specified attributes.
5718 * @param {HTMLElement} insertBefore (optional) a child element of this element
5719 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
5720 * @return {Ext.Element} The new child element
5722 createChild: function(config, insertBefore, returnDom){
5723 config = config || {tag:'div'};
5724 return insertBefore ?
5725 DH.insertBefore(insertBefore, config, returnDom !== true) :
5726 DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config, returnDom !== true);
5730 * Creates and wraps this element with another element
5731 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
5732 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5733 * @return {HTMLElement/Element} The newly created wrapper element
5735 wrap: function(config, returnDom){
5736 var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
5737 newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
5742 * Inserts an html fragment into this element
5743 * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
5744 * @param {String} html The HTML fragment
5745 * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
5746 * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
5748 insertHtml : function(where, html, returnEl){
5749 var el = DH.insertHtml(where, this.dom, html);
5750 return returnEl ? Ext.get(el) : el;
5754 * @class Ext.Element
5756 Ext.apply(Ext.Element.prototype, function() {
5757 var GETDOM = Ext.getDom,
5763 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
5764 * @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.
5765 * @param {String} where (optional) 'before' or 'after' defaults to before
5766 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5767 * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
5769 insertSibling: function(el, where, returnDom){
5772 isAfter = (where || 'before').toLowerCase() == 'after',
5775 if(Ext.isArray(el)){
5777 Ext.each(el, function(e) {
5778 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
5788 if(el.nodeType || el.dom){
5789 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);
5794 if (isAfter && !me.dom.nextSibling) {
5795 rt = DH.append(me.dom.parentNode, el, !returnDom);
5797 rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
5804 * @class Ext.Element
5806 Ext.Element.addMethods(function(){
5807 // local style camelizing for speed
5809 camelRe = /(-[a-z])/gi,
5810 view = document.defaultView,
5811 propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
5812 opacityRe = /alpha\(opacity=(.*)\)/i,
5813 trimRe = /^\s+|\s+$/g,
5816 PADDING = "padding",
5826 ISCLIPPED = 'isClipped',
5827 OVERFLOW = 'overflow',
5828 OVERFLOWX = 'overflow-x',
5829 OVERFLOWY = 'overflow-y',
5830 ORIGINALCLIP = 'originalClip',
5831 // special markup used throughout Ext when box wrapping elements
5832 borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
5833 paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
5834 margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
5835 data = Ext.Element.data;
5839 function camelFn(m, a) {
5840 return a.charAt(1).toUpperCase();
5843 function chkCache(prop) {
5844 return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
5848 // private ==> used by Fx
5849 adjustWidth : function(width) {
5851 var isNum = (typeof width == "number");
5852 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5853 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
5855 return (isNum && width < 0) ? 0 : width;
5858 // private ==> used by Fx
5859 adjustHeight : function(height) {
5861 var isNum = (typeof height == "number");
5862 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5863 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
5865 return (isNum && height < 0) ? 0 : height;
5870 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
5871 * @param {String/Array} className The CSS class to add, or an array of classes
5872 * @return {Ext.Element} this
5874 addClass : function(className){
5880 // Separate case is for speed
5881 if (!Ext.isArray(className)) {
5882 if (typeof className == 'string' && !this.hasClass(className)) {
5883 me.dom.className += " " + className;
5887 for (i = 0, len = className.length; i < len; i++) {
5889 if (typeof v == 'string' && (' ' + me.dom.className + ' ').indexOf(' ' + v + ' ') == -1) {
5894 me.dom.className += " " + cls.join(" ");
5901 * Removes one or more CSS classes from the element.
5902 * @param {String/Array} className The CSS class to remove, or an array of classes
5903 * @return {Ext.Element} this
5905 removeClass : function(className){
5912 if (!Ext.isArray(className)){
5913 className = [className];
5915 if (me.dom && me.dom.className) {
5916 elClasses = me.dom.className.replace(trimRe, '').split(spacesRe);
5917 for (i = 0, len = className.length; i < len; i++) {
5919 if (typeof cls == 'string') {
5920 cls = cls.replace(trimRe, '');
5921 idx = elClasses.indexOf(cls);
5923 elClasses.splice(idx, 1);
5927 me.dom.className = elClasses.join(" ");
5933 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
5934 * @param {String/Array} className The CSS class to add, or an array of classes
5935 * @return {Ext.Element} this
5937 radioClass : function(className){
5938 var cn = this.dom.parentNode.childNodes,
5942 className = Ext.isArray(className) ? className : [className];
5943 for (i = 0, len = cn.length; i < len; i++) {
5945 if (v && v.nodeType == 1) {
5946 Ext.fly(v, '_internal').removeClass(className);
5949 return this.addClass(className);
5953 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
5954 * @param {String} className The CSS class to toggle
5955 * @return {Ext.Element} this
5957 toggleClass : function(className){
5958 return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
5962 * Checks if the specified CSS class exists on this element's DOM node.
5963 * @param {String} className The CSS class to check for
5964 * @return {Boolean} True if the class exists, else false
5966 hasClass : function(className){
5967 return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
5971 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
5972 * @param {String} oldClassName The CSS class to replace
5973 * @param {String} newClassName The replacement CSS class
5974 * @return {Ext.Element} this
5976 replaceClass : function(oldClassName, newClassName){
5977 return this.removeClass(oldClassName).addClass(newClassName);
5980 isStyle : function(style, val) {
5981 return this.getStyle(style) == val;
5985 * Normalizes currentStyle and computedStyle.
5986 * @param {String} property The style property whose value is returned.
5987 * @return {String} The current value of the style property for this element.
5989 getStyle : function(){
5990 return view && view.getComputedStyle ?
6003 prop = chkCache(prop);
6004 // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
6005 if(wk && (/marginRight/.test(prop))) {
6006 display = this.getStyle('display');
6007 el.style.display = 'inline-block';
6009 out = (v = el.style[prop]) ? v :
6010 (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
6012 // Webkit returns rgb values for transparent.
6014 if(out == 'rgba(0, 0, 0, 0)'){
6015 out = 'transparent';
6017 el.style.display = display;
6027 if(el == document) return null;
6028 if (prop == 'opacity') {
6029 if (el.style.filter.match) {
6030 if(m = el.style.filter.match(opacityRe)){
6031 var fv = parseFloat(m[1]);
6033 return fv ? fv / 100 : 0;
6039 prop = chkCache(prop);
6040 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
6045 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
6046 * are convert to standard 6 digit hex color.
6047 * @param {String} attr The css attribute
6048 * @param {String} defaultValue The default value to use when a valid color isn't found
6049 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
6052 getColor : function(attr, defaultValue, prefix){
6053 var v = this.getStyle(attr),
6054 color = (typeof prefix != 'undefined') ? prefix : '#',
6057 if(!v || (/transparent|inherit/.test(v))) {
6058 return defaultValue;
6061 Ext.each(v.slice(4, v.length -1).split(','), function(s){
6062 h = parseInt(s, 10);
6063 color += (h < 16 ? '0' : '') + h.toString(16);
6066 v = v.replace('#', '');
6067 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
6069 return(color.length > 5 ? color.toLowerCase() : defaultValue);
6073 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
6074 * @param {String/Object} property The style property to be set, or an object of multiple styles.
6075 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
6076 * @return {Ext.Element} this
6078 setStyle : function(prop, value){
6081 if (typeof prop != 'object') {
6086 for (style in prop) {
6087 value = prop[style];
6088 style == 'opacity' ?
6089 this.setOpacity(value) :
6090 this.dom.style[chkCache(style)] = value;
6096 * Set the opacity of the element
6097 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
6098 * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
6099 * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
6100 * @return {Ext.Element} this
6102 setOpacity : function(opacity, animate){
6106 if(!animate || !me.anim){
6108 var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
6109 val = s.filter.replace(opacityRe, '').replace(trimRe, '');
6112 s.filter = val + (val.length > 0 ? ' ' : '') + opac;
6114 s.opacity = opacity;
6117 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
6123 * Clears any opacity settings from this element. Required in some cases for IE.
6124 * @return {Ext.Element} this
6126 clearOpacity : function(){
6127 var style = this.dom.style;
6129 if(!Ext.isEmpty(style.filter)){
6130 style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
6133 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
6139 * Returns the offset height of the element
6140 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
6141 * @return {Number} The element's height
6143 getHeight : function(contentHeight){
6146 hidden = Ext.isIE && me.isStyle('display', 'none'),
6147 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
6149 h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
6150 return h < 0 ? 0 : h;
6154 * Returns the offset width of the element
6155 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
6156 * @return {Number} The element's width
6158 getWidth : function(contentWidth){
6161 hidden = Ext.isIE && me.isStyle('display', 'none'),
6162 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
6163 w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
6164 return w < 0 ? 0 : w;
6168 * Set the width of this Element.
6169 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6170 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6171 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6173 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6174 * @return {Ext.Element} this
6176 setWidth : function(width, animate){
6178 width = me.adjustWidth(width);
6179 !animate || !me.anim ?
6180 me.dom.style.width = me.addUnits(width) :
6181 me.anim({width : {to : width}}, me.preanim(arguments, 1));
6186 * Set the height of this Element.
6188 // change the height to 200px and animate with default configuration
6189 Ext.fly('elementId').setHeight(200, true);
6191 // change the height to 150px and animate with a custom configuration
6192 Ext.fly('elId').setHeight(150, {
6193 duration : .5, // animation will have a duration of .5 seconds
6194 // will change the content to "finished"
6195 callback: function(){ this.{@link #update}("finished"); }
6198 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6199 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
6200 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6202 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6203 * @return {Ext.Element} this
6205 setHeight : function(height, animate){
6207 height = me.adjustHeight(height);
6208 !animate || !me.anim ?
6209 me.dom.style.height = me.addUnits(height) :
6210 me.anim({height : {to : height}}, me.preanim(arguments, 1));
6215 * Gets the width of the border(s) for the specified side(s)
6216 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6217 * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
6218 * @return {Number} The width of the sides passed added together
6220 getBorderWidth : function(side){
6221 return this.addStyles(side, borders);
6225 * Gets the width of the padding(s) for the specified side(s)
6226 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6227 * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
6228 * @return {Number} The padding of the sides passed added together
6230 getPadding : function(side){
6231 return this.addStyles(side, paddings);
6235 * Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
6236 * @return {Ext.Element} this
6242 if(!data(dom, ISCLIPPED)){
6243 data(dom, ISCLIPPED, true);
6244 data(dom, ORIGINALCLIP, {
6245 o: me.getStyle(OVERFLOW),
6246 x: me.getStyle(OVERFLOWX),
6247 y: me.getStyle(OVERFLOWY)
6249 me.setStyle(OVERFLOW, HIDDEN);
6250 me.setStyle(OVERFLOWX, HIDDEN);
6251 me.setStyle(OVERFLOWY, HIDDEN);
6257 * Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
6258 * @return {Ext.Element} this
6260 unclip : function(){
6264 if(data(dom, ISCLIPPED)){
6265 data(dom, ISCLIPPED, false);
6266 var o = data(dom, ORIGINALCLIP);
6268 me.setStyle(OVERFLOW, o.o);
6271 me.setStyle(OVERFLOWX, o.x);
6274 me.setStyle(OVERFLOWY, o.y);
6281 addStyles : function(sides, styles){
6283 sidesArr = sides.match(wordsRe),
6287 len = sidesArr.length;
6288 for (i = 0; i < len; i++) {
6290 size = side && parseInt(this.getStyle(styles[side]), 10);
6292 ttlSize += MATH.abs(size);
6303 * @class Ext.Element
6306 // special markup used throughout Ext when box wrapping elements
6307 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>';
6309 Ext.Element.addMethods(function(){
6310 var INTERNAL = "_internal",
6311 pxMatch = /(\d+\.?\d+)px/;
6314 * More flexible version of {@link #setStyle} for setting style properties.
6315 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
6316 * a function which returns such a specification.
6317 * @return {Ext.Element} this
6319 applyStyles : function(style){
6320 Ext.DomHelper.applyStyles(this.dom, style);
6325 * Returns an object with properties matching the styles requested.
6326 * For example, el.getStyles('color', 'font-size', 'width') might return
6327 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
6328 * @param {String} style1 A style name
6329 * @param {String} style2 A style name
6330 * @param {String} etc.
6331 * @return {Object} The style object
6333 getStyles : function(){
6335 Ext.each(arguments, function(v) {
6336 ret[v] = this.getStyle(v);
6342 // private ==> used by ext full
6343 setOverflow : function(v){
6345 if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
6346 dom.style.overflow = 'hidden';
6347 (function(){dom.style.overflow = 'auto';}).defer(1);
6349 dom.style.overflow = v;
6354 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
6355 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
6356 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
6357 * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}). The markup
6358 * is of this form:</p>
6360 Ext.Element.boxMarkup =
6361 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
6362 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
6363 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
6365 * <p>Example usage:</p>
6368 Ext.get("foo").boxWrap();
6370 // You can also add a custom class and use CSS inheritance rules to customize the box look.
6371 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
6372 // for how to create a custom box wrap style.
6373 Ext.get("foo").boxWrap().addClass("x-box-blue");
6375 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
6376 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
6377 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
6378 * also supply all of the necessary rules.
6379 * @return {Ext.Element} The outermost wrapping element of the created box structure.
6381 boxWrap : function(cls){
6382 cls = cls || 'x-box';
6383 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)));
6384 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
6389 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
6390 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6391 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6392 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6393 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
6395 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6396 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6397 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6399 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6400 * @return {Ext.Element} this
6402 setSize : function(width, height, animate){
6404 if(typeof width == 'object'){ // in case of object from getSize()
6405 height = width.height;
6406 width = width.width;
6408 width = me.adjustWidth(width);
6409 height = me.adjustHeight(height);
6410 if(!animate || !me.anim){
6411 me.dom.style.width = me.addUnits(width);
6412 me.dom.style.height = me.addUnits(height);
6414 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
6420 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
6421 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
6422 * if a height has not been set using CSS.
6425 getComputedHeight : function(){
6427 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
6429 h = parseFloat(me.getStyle('height')) || 0;
6430 if(!me.isBorderBox()){
6431 h += me.getFrameWidth('tb');
6438 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
6439 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
6440 * if a width has not been set using CSS.
6443 getComputedWidth : function(){
6444 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
6446 w = parseFloat(this.getStyle('width')) || 0;
6447 if(!this.isBorderBox()){
6448 w += this.getFrameWidth('lr');
6455 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
6456 for more information about the sides.
6457 * @param {String} sides
6460 getFrameWidth : function(sides, onlyContentBox){
6461 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
6465 * Sets up event handlers to add and remove a css class when the mouse is over this element
6466 * @param {String} className
6467 * @return {Ext.Element} this
6469 addClassOnOver : function(className){
6472 Ext.fly(this, INTERNAL).addClass(className);
6475 Ext.fly(this, INTERNAL).removeClass(className);
6482 * Sets up event handlers to add and remove a css class when this element has the focus
6483 * @param {String} className
6484 * @return {Ext.Element} this
6486 addClassOnFocus : function(className){
6487 this.on("focus", function(){
6488 Ext.fly(this, INTERNAL).addClass(className);
6490 this.on("blur", function(){
6491 Ext.fly(this, INTERNAL).removeClass(className);
6497 * 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)
6498 * @param {String} className
6499 * @return {Ext.Element} this
6501 addClassOnClick : function(className){
6503 this.on("mousedown", function(){
6504 Ext.fly(dom, INTERNAL).addClass(className);
6505 var d = Ext.getDoc(),
6507 Ext.fly(dom, INTERNAL).removeClass(className);
6508 d.removeListener("mouseup", fn);
6510 d.on("mouseup", fn);
6516 * <p>Returns the dimensions of the element available to lay content out in.<p>
6517 * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
6518 * example:<pre><code>
6519 var vpSize = Ext.getBody().getViewSize();
6521 // all Windows created afterwards will have a default value of 90% height and 95% width
6522 Ext.Window.override({
6523 width: vpSize.width * 0.9,
6524 height: vpSize.height * 0.95
6526 // To handle window resizing you would have to hook onto onWindowResize.
6529 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
6530 * To obtain the size including scrollbars, use getStyleSize
6532 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6535 getViewSize : function(){
6538 isDoc = (d == doc || d == doc.body);
6540 // If the body, use Ext.lib.Dom
6542 var extdom = Ext.lib.Dom;
6544 width : extdom.getViewWidth(),
6545 height : extdom.getViewHeight()
6548 // Else use clientHeight/clientWidth
6551 width : d.clientWidth,
6552 height : d.clientHeight
6558 * <p>Returns the dimensions of the element available to lay content out in.<p>
6560 * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
6561 * To obtain the size excluding scrollbars, use getViewSize
6563 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6566 getStyleSize : function(){
6571 isDoc = (d == doc || d == doc.body),
6574 // If the body, use Ext.lib.Dom
6576 var extdom = Ext.lib.Dom;
6578 width : extdom.getViewWidth(),
6579 height : extdom.getViewHeight()
6582 // Use Styles if they are set
6583 if(s.width && s.width != 'auto'){
6584 w = parseFloat(s.width);
6585 if(me.isBorderBox()){
6586 w -= me.getFrameWidth('lr');
6589 // Use Styles if they are set
6590 if(s.height && s.height != 'auto'){
6591 h = parseFloat(s.height);
6592 if(me.isBorderBox()){
6593 h -= me.getFrameWidth('tb');
6596 // Use getWidth/getHeight if style not set.
6597 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
6601 * Returns the size of the element.
6602 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
6603 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
6605 getSize : function(contentSize){
6606 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
6610 * Forces the browser to repaint this element
6611 * @return {Ext.Element} this
6613 repaint : function(){
6615 this.addClass("x-repaint");
6616 setTimeout(function(){
6617 Ext.fly(dom).removeClass("x-repaint");
6623 * Disables text selection for this element (normalized across browsers)
6624 * @return {Ext.Element} this
6626 unselectable : function(){
6627 this.dom.unselectable = "on";
6628 return this.swallowEvent("selectstart", true).
6629 applyStyles("-moz-user-select:none;-khtml-user-select:none;").
6630 addClass("x-unselectable");
6634 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
6635 * then it returns the calculated width of the sides (see getPadding)
6636 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
6637 * @return {Object/Number}
6639 getMargins : function(side){
6642 hash = {t:"top", l:"left", r:"right", b: "bottom"},
6646 for (key in me.margins){
6647 o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
6651 return me.addStyles.call(me, side, me.margins);
6657 * @class Ext.Element
6660 var D = Ext.lib.Dom,
6665 POSITION = "position",
6667 RELATIVE = "relative",
6671 Ext.Element.addMethods({
6673 * 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).
6674 * @return {Number} The X position of the element
6677 return D.getX(this.dom);
6681 * 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).
6682 * @return {Number} The Y position of the element
6685 return D.getY(this.dom);
6689 * 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).
6690 * @return {Array} The XY position of the element
6693 return D.getXY(this.dom);
6697 * 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.
6698 * @param {Mixed} element The element to get the offsets from.
6699 * @return {Array} The XY page offsets (e.g. [100, -200])
6701 getOffsetsTo : function(el){
6702 var o = this.getXY(),
6703 e = Ext.fly(el, '_internal').getXY();
6704 return [o[0]-e[0],o[1]-e[1]];
6708 * 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).
6709 * @param {Number} The X position of the element
6710 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6711 * @return {Ext.Element} this
6713 setX : function(x, animate){
6714 return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));
6718 * 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).
6719 * @param {Number} The Y position of the element
6720 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6721 * @return {Ext.Element} this
6723 setY : function(y, animate){
6724 return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));
6728 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
6729 * @param {String} left The left CSS property value
6730 * @return {Ext.Element} this
6732 setLeft : function(left){
6733 this.setStyle(LEFT, this.addUnits(left));
6738 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
6739 * @param {String} top The top CSS property value
6740 * @return {Ext.Element} this
6742 setTop : function(top){
6743 this.setStyle(TOP, this.addUnits(top));
6748 * Sets the element's CSS right style.
6749 * @param {String} right The right CSS property value
6750 * @return {Ext.Element} this
6752 setRight : function(right){
6753 this.setStyle(RIGHT, this.addUnits(right));
6758 * Sets the element's CSS bottom style.
6759 * @param {String} bottom The bottom CSS property value
6760 * @return {Ext.Element} this
6762 setBottom : function(bottom){
6763 this.setStyle(BOTTOM, this.addUnits(bottom));
6768 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6769 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6770 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
6771 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6772 * @return {Ext.Element} this
6774 setXY : function(pos, animate){
6776 if(!animate || !me.anim){
6777 D.setXY(me.dom, pos);
6779 me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');
6785 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6786 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6787 * @param {Number} x X value for new position (coordinates are page-based)
6788 * @param {Number} y Y value for new position (coordinates are page-based)
6789 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6790 * @return {Ext.Element} this
6792 setLocation : function(x, y, animate){
6793 return this.setXY([x, y], this.animTest(arguments, animate, 2));
6797 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6798 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6799 * @param {Number} x X value for new position (coordinates are page-based)
6800 * @param {Number} y Y value for new position (coordinates are page-based)
6801 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6802 * @return {Ext.Element} this
6804 moveTo : function(x, y, animate){
6805 return this.setXY([x, y], this.animTest(arguments, animate, 2));
6809 * Gets the left X coordinate
6810 * @param {Boolean} local True to get the local css position instead of page coordinate
6813 getLeft : function(local){
6814 return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
6818 * Gets the right X coordinate of the element (element X position + element width)
6819 * @param {Boolean} local True to get the local css position instead of page coordinate
6822 getRight : function(local){
6824 return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
6828 * Gets the top Y coordinate
6829 * @param {Boolean} local True to get the local css position instead of page coordinate
6832 getTop : function(local) {
6833 return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
6837 * Gets the bottom Y coordinate of the element (element Y position + element height)
6838 * @param {Boolean} local True to get the local css position instead of page coordinate
6841 getBottom : function(local){
6843 return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
6847 * Initializes positioning on this element. If a desired position is not passed, it will make the
6848 * the element positioned relative IF it is not already positioned.
6849 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
6850 * @param {Number} zIndex (optional) The zIndex to apply
6851 * @param {Number} x (optional) Set the page X position
6852 * @param {Number} y (optional) Set the page Y position
6854 position : function(pos, zIndex, x, y){
6857 if(!pos && me.isStyle(POSITION, STATIC)){
6858 me.setStyle(POSITION, RELATIVE);
6860 me.setStyle(POSITION, pos);
6863 me.setStyle(ZINDEX, zIndex);
6865 if(x || y) me.setXY([x || false, y || false]);
6869 * Clear positioning back to the default when the document was loaded
6870 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
6871 * @return {Ext.Element} this
6873 clearPositioning : function(value){
6874 value = value || '';
6887 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
6888 * snapshot before performing an update and then restoring the element.
6891 getPositioning : function(){
6892 var l = this.getStyle(LEFT);
6893 var t = this.getStyle(TOP);
6895 "position" : this.getStyle(POSITION),
6897 "right" : l ? "" : this.getStyle(RIGHT),
6899 "bottom" : t ? "" : this.getStyle(BOTTOM),
6900 "z-index" : this.getStyle(ZINDEX)
6905 * Set positioning with an object returned by getPositioning().
6906 * @param {Object} posCfg
6907 * @return {Ext.Element} this
6909 setPositioning : function(pc){
6911 style = me.dom.style;
6915 if(pc.right == AUTO){
6918 if(pc.bottom == AUTO){
6926 * Translates the passed page coordinates into left/top css values for this element
6927 * @param {Number/Array} x The page x or an array containing [x, y]
6928 * @param {Number} y (optional) The page y, required if x is not an array
6929 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
6931 translatePoints : function(x, y){
6932 y = isNaN(x[1]) ? y : x[1];
6933 x = isNaN(x[0]) ? x : x[0];
6935 relative = me.isStyle(POSITION, RELATIVE),
6937 l = parseInt(me.getStyle(LEFT), 10),
6938 t = parseInt(me.getStyle(TOP), 10);
6940 l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
6941 t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);
6943 return {left: (x - o[0] + l), top: (y - o[1] + t)};
6946 animTest : function(args, animate, i) {
6947 return !!animate && this.preanim ? this.preanim(args, i) : false;
6951 * @class Ext.Element
6953 Ext.Element.addMethods({
6955 * 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.
6956 * @param {Object} box The box to fill {x, y, width, height}
6957 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
6958 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6959 * @return {Ext.Element} this
6961 setBox : function(box, adjust, animate){
6965 if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
6966 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
6967 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
6969 me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
6974 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
6975 * set another Element's size/location to match this element.
6976 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
6977 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
6978 * @return {Object} box An object in the format<pre><code>
6980 x: <Element's X position>,
6981 y: <Element's Y position>,
6982 width: <Element's width>,
6983 height: <Element's height>,
6984 bottom: <Element's lower bound>,
6985 right: <Element's rightmost bound>
6988 * The returned object may also be addressed as an Array where index 0 contains the X position
6989 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
6991 getBox : function(contentBox, local) {
6996 getBorderWidth = me.getBorderWidth,
6997 getPadding = me.getPadding,
7005 left = parseInt(me.getStyle("left"), 10) || 0;
7006 top = parseInt(me.getStyle("top"), 10) || 0;
7009 var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
7011 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
7013 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
7014 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
7015 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
7016 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
7017 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)};
7019 bx.right = bx.x + bx.width;
7020 bx.bottom = bx.y + bx.height;
7025 * Move this element relative to its current position.
7026 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
7027 * @param {Number} distance How far to move the element in pixels
7028 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7029 * @return {Ext.Element} this
7031 move : function(direction, distance, animate){
7036 left = [x - distance, y],
7037 right = [x + distance, y],
7038 top = [x, y - distance],
7039 bottom = [x, y + distance],
7053 direction = direction.toLowerCase();
7054 me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
7058 * Quick set left and top adding default units
7059 * @param {String} left The left CSS property value
7060 * @param {String} top The top CSS property value
7061 * @return {Ext.Element} this
7063 setLeftTop : function(left, top){
7065 style = me.dom.style;
7066 style.left = me.addUnits(left);
7067 style.top = me.addUnits(top);
7072 * Returns the region of the given element.
7073 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7074 * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
7076 getRegion : function(){
7077 return Ext.lib.Dom.getRegion(this.dom);
7081 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7082 * @param {Number} x X value for new position (coordinates are page-based)
7083 * @param {Number} y Y value for new position (coordinates are page-based)
7084 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
7085 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7086 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
7088 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
7089 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7090 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
7092 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7093 * @return {Ext.Element} this
7095 setBounds : function(x, y, width, height, animate){
7097 if (!animate || !me.anim) {
7098 me.setSize(width, height);
7099 me.setLocation(x, y);
7101 me.anim({points: {to: [x, y]},
7102 width: {to: me.adjustWidth(width)},
7103 height: {to: me.adjustHeight(height)}},
7104 me.preanim(arguments, 4),
7111 * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
7112 * @param {Ext.lib.Region} region The region to fill
7113 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7114 * @return {Ext.Element} this
7116 setRegion : function(region, animate) {
7117 return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
7120 * @class Ext.Element
7122 Ext.Element.addMethods({
7124 * Returns true if this element is scrollable.
7127 isScrollable : function(){
7129 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
7133 * 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().
7134 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7135 * @param {Number} value The new scroll value.
7136 * @return {Element} this
7138 scrollTo : function(side, value){
7139 this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;
7144 * Returns the current scroll position of the element.
7145 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
7147 getScroll : function(){
7151 docElement = doc.documentElement,
7156 if(d == doc || d == body){
7157 if(Ext.isIE && Ext.isStrict){
7158 l = docElement.scrollLeft;
7159 t = docElement.scrollTop;
7161 l = window.pageXOffset;
7162 t = window.pageYOffset;
7164 ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
7166 ret = {left: d.scrollLeft, top: d.scrollTop};
7171 * @class Ext.Element
7173 Ext.Element.addMethods({
7175 * 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().
7176 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7177 * @param {Number} value The new scroll value
7178 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7179 * @return {Element} this
7181 scrollTo : function(side, value, animate) {
7182 //check if we're scrolling top or left
7183 var top = /top/i.test(side),
7187 if (!animate || !me.anim) {
7188 // just setting the value, so grab the direction
7189 prop = 'scroll' + (top ? 'Top' : 'Left');
7193 // if scrolling top, we need to grab scrollLeft, if left, scrollTop
7194 prop = 'scroll' + (top ? 'Left' : 'Top');
7195 me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}}, me.preanim(arguments, 2), 'scroll');
7201 * Scrolls this element into view within the passed container.
7202 * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a
7203 * string (id), dom node, or Ext.Element.
7204 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7205 * @return {Ext.Element} this
7207 scrollIntoView : function(container, hscroll) {
7208 var c = Ext.getDom(container) || Ext.getBody().dom,
7210 o = this.getOffsetsTo(c),
7211 l = o[0] + c.scrollLeft,
7212 t = o[1] + c.scrollTop,
7213 b = t + el.offsetHeight,
7214 r = l + el.offsetWidth,
7215 ch = c.clientHeight,
7216 ct = parseInt(c.scrollTop, 10),
7217 cl = parseInt(c.scrollLeft, 10),
7219 cr = cl + c.clientWidth;
7221 if (el.offsetHeight > ch || t < ct) {
7227 // corrects IE, other browsers will ignore
7228 c.scrollTop = c.scrollTop;
7230 if (hscroll !== false) {
7231 if (el.offsetWidth > c.clientWidth || l < cl) {
7235 c.scrollLeft = r - c.clientWidth;
7237 c.scrollLeft = c.scrollLeft;
7243 scrollChildIntoView : function(child, hscroll) {
7244 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7248 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
7249 * within this element's scrollable range.
7250 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
7251 * @param {Number} distance How far to scroll the element in pixels
7252 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7253 * @return {Boolean} Returns true if a scroll was triggered or false if the element
7254 * was scrolled as far as it could go.
7256 scroll : function(direction, distance, animate) {
7257 if (!this.isScrollable()) {
7261 l = el.scrollLeft, t = el.scrollTop,
7262 w = el.scrollWidth, h = el.scrollHeight,
7263 cw = el.clientWidth, ch = el.clientHeight,
7264 scrolled = false, v,
7266 l: Math.min(l + distance, w-cw),
7267 r: v = Math.max(l - distance, 0),
7268 t: Math.max(t - distance, 0),
7269 b: Math.min(t + distance, h-ch)
7274 direction = direction.substr(0, 1);
7275 if ((v = hash[direction]) > -1) {
7277 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
7282 * @class Ext.Element
7285 * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
7289 Ext.Element.VISIBILITY = 1;
7291 * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
7295 Ext.Element.DISPLAY = 2;
7297 Ext.Element.addMethods(function(){
7298 var VISIBILITY = "visibility",
7299 DISPLAY = "display",
7301 OFFSETS = "offsets",
7303 ORIGINALDISPLAY = 'originalDisplay',
7304 VISMODE = 'visibilityMode',
7305 ELDISPLAY = Ext.Element.DISPLAY,
7306 data = Ext.Element.data,
7307 getDisplay = function(dom){
7308 var d = data(dom, ORIGINALDISPLAY);
7309 if(d === undefined){
7310 data(dom, ORIGINALDISPLAY, d = '');
7314 getVisMode = function(dom){
7315 var m = data(dom, VISMODE);
7316 if(m === undefined){
7317 data(dom, VISMODE, m = 1);
7324 * The element's default display mode (defaults to "")
7327 originalDisplay : "",
7331 * Sets the element's visibility mode. When setVisible() is called it
7332 * will use this to determine whether to set the visibility or the display property.
7333 * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
7334 * @return {Ext.Element} this
7336 setVisibilityMode : function(visMode){
7337 data(this.dom, VISMODE, visMode);
7342 * Perform custom animation on this element.
7343 * <div><ul class="mdetail-params">
7344 * <li><u>Animation Properties</u></li>
7346 * <p>The Animation Control Object enables gradual transitions for any member of an
7347 * element's style object that takes a numeric value including but not limited to
7348 * these properties:</p><div><ul class="mdetail-params">
7349 * <li><tt>bottom, top, left, right</tt></li>
7350 * <li><tt>height, width</tt></li>
7351 * <li><tt>margin, padding</tt></li>
7352 * <li><tt>borderWidth</tt></li>
7353 * <li><tt>opacity</tt></li>
7354 * <li><tt>fontSize</tt></li>
7355 * <li><tt>lineHeight</tt></li>
7359 * <li><u>Animation Property Attributes</u></li>
7361 * <p>Each Animation Property is a config object with optional properties:</p>
7362 * <div><ul class="mdetail-params">
7363 * <li><tt>by</tt>* : relative change - start at current value, change by this value</li>
7364 * <li><tt>from</tt> : ignore current value, start from this value</li>
7365 * <li><tt>to</tt>* : start at current value, go to this value</li>
7366 * <li><tt>unit</tt> : any allowable unit specification</li>
7367 * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>
7370 * <li><u>Animation Types</u></li>
7372 * <p>The supported animation types:</p><div><ul class="mdetail-params">
7373 * <li><tt>'run'</tt> : Default
7375 var el = Ext.get('complexEl');
7377 // animation control object
7379 borderWidth: {to: 3, from: 0},
7380 opacity: {to: .3, from: 1},
7381 height: {to: 50, from: el.getHeight()},
7382 width: {to: 300, from: el.getWidth()},
7383 top : {by: - 100, unit: 'px'},
7385 0.35, // animation duration
7387 'easeOut', // easing method
7388 'run' // animation type ('run','color','motion','scroll')
7392 * <li><tt>'color'</tt>
7393 * <p>Animates transition of background, text, or border colors.</p>
7396 // animation control object
7398 color: { to: '#06e' },
7399 backgroundColor: { to: '#e06' }
7401 0.35, // animation duration
7403 'easeOut', // easing method
7404 'color' // animation type ('run','color','motion','scroll')
7409 * <li><tt>'motion'</tt>
7410 * <p>Animates the motion of an element to/from specific points using optional bezier
7411 * way points during transit.</p>
7414 // animation control object
7416 borderWidth: {to: 3, from: 0},
7417 opacity: {to: .3, from: 1},
7418 height: {to: 50, from: el.getHeight()},
7419 width: {to: 300, from: el.getWidth()},
7420 top : {by: - 100, unit: 'px'},
7422 to: [50, 100], // go to this point
7423 control: [ // optional bezier way points
7429 3000, // animation duration (milliseconds!)
7431 'easeOut', // easing method
7432 'motion' // animation type ('run','color','motion','scroll')
7436 * <li><tt>'scroll'</tt>
7437 * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>
7440 // animation control object
7442 scroll: {to: [400, 300]}
7444 0.35, // animation duration
7446 'easeOut', // easing method
7447 'scroll' // animation type ('run','color','motion','scroll')
7455 * @param {Object} args The animation control args
7456 * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)
7457 * @param {Function} onComplete (optional) Function to call when animation completes
7458 * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)
7459 * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,
7460 * <tt>'motion'</tt>, or <tt>'scroll'</tt>
7461 * @return {Ext.Element} this
7463 animate : function(args, duration, onComplete, easing, animType){
7464 this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7469 * @private Internal animation call
7471 anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7472 animType = animType || 'run';
7475 anim = Ext.lib.Anim[animType](
7478 (opt.duration || defaultDur) || .35,
7479 (opt.easing || defaultEase) || 'easeOut',
7482 if(opt.callback) opt.callback.call(opt.scope || me, me, opt);
7490 // private legacy anim prep
7491 preanim : function(a, i){
7492 return !a[i] ? false : (typeof a[i] == 'object' ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7496 * Checks whether the element is currently visible using both visibility and display properties.
7497 * @return {Boolean} True if the element is currently visible, else false
7499 isVisible : function() {
7500 return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);
7504 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7505 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7506 * @param {Boolean} visible Whether the element is visible
7507 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7508 * @return {Ext.Element} this
7510 setVisible : function(visible, animate){
7511 var me = this, isDisplay, isVisible, isOffsets,
7514 // hideMode string override
7515 if (typeof animate == 'string'){
7516 isDisplay = animate == DISPLAY;
7517 isVisible = animate == VISIBILITY;
7518 isOffsets = animate == OFFSETS;
7521 isDisplay = getVisMode(this.dom) == ELDISPLAY;
7522 isVisible = !isDisplay;
7525 if (!animate || !me.anim) {
7527 me.setDisplayed(visible);
7528 } else if (isOffsets){
7530 me.hideModeStyles = {
7531 position: me.getStyle('position'),
7532 top: me.getStyle('top'),
7533 left: me.getStyle('left')
7536 me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
7538 me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
7542 dom.style.visibility = visible ? "visible" : HIDDEN;
7545 // closure for composites
7548 me.setVisible(true);
7550 me.anim({opacity: { to: (visible?1:0) }},
7551 me.preanim(arguments, 1),
7557 dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;
7558 Ext.fly(dom).setOpacity(1);
7566 * Toggles the element's visibility or display, depending on visibility mode.
7567 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7568 * @return {Ext.Element} this
7570 toggle : function(animate){
7572 me.setVisible(!me.isVisible(), me.preanim(arguments, 0));
7577 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7578 * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
7579 * @return {Ext.Element} this
7581 setDisplayed : function(value) {
7582 if(typeof value == "boolean"){
7583 value = value ? getDisplay(this.dom) : NONE;
7585 this.setStyle(DISPLAY, value);
7590 fixDisplay : function(){
7592 if(me.isStyle(DISPLAY, NONE)){
7593 me.setStyle(VISIBILITY, HIDDEN);
7594 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
7595 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
7596 me.setStyle(DISPLAY, "block");
7602 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7603 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7604 * @return {Ext.Element} this
7606 hide : function(animate){
7607 // hideMode override
7608 if (typeof animate == 'string'){
7609 this.setVisible(false, animate);
7612 this.setVisible(false, this.preanim(arguments, 0));
7617 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7618 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7619 * @return {Ext.Element} this
7621 show : function(animate){
7622 // hideMode override
7623 if (typeof animate == 'string'){
7624 this.setVisible(true, animate);
7627 this.setVisible(true, this.preanim(arguments, 0));
7633 * @class Ext.Element
7635 Ext.Element.addMethods(
7637 var VISIBILITY = "visibility",
7638 DISPLAY = "display",
7641 XMASKED = "x-masked",
7642 XMASKEDRELATIVE = "x-masked-relative",
7643 data = Ext.Element.data;
7647 * Checks whether the element is currently visible using both visibility and display properties.
7648 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7649 * @return {Boolean} True if the element is currently visible, else false
7651 isVisible : function(deep) {
7652 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),
7653 p = this.dom.parentNode;
7654 if(deep !== true || !vis){
7657 while(p && !/^body/i.test(p.tagName)){
7658 if(!Ext.fly(p, '_isVisible').isVisible()){
7667 * Returns true if display is not "none"
7670 isDisplayed : function() {
7671 return !this.isStyle(DISPLAY, NONE);
7675 * Convenience method for setVisibilityMode(Element.DISPLAY)
7676 * @param {String} display (optional) What to set display to when visible
7677 * @return {Ext.Element} this
7679 enableDisplayMode : function(display){
7680 this.setVisibilityMode(Ext.Element.DISPLAY);
7681 if(!Ext.isEmpty(display)){
7682 data(this.dom, 'originalDisplay', display);
7688 * Puts a mask over this element to disable user interaction. Requires core.css.
7689 * This method can only be applied to elements which accept child nodes.
7690 * @param {String} msg (optional) A message to display in the mask
7691 * @param {String} msgCls (optional) A css class to apply to the msg element
7692 * @return {Element} The mask element
7694 mask : function(msg, msgCls){
7698 EXTELMASKMSG = "ext-el-mask-msg",
7702 if(!/^body/i.test(dom.tagName) && me.getStyle('position') == 'static'){
7703 me.addClass(XMASKEDRELATIVE);
7705 if((el = data(dom, 'maskMsg'))){
7708 if((el = data(dom, 'mask'))){
7712 mask = dh.append(dom, {cls : "ext-el-mask"}, true);
7713 data(dom, 'mask', mask);
7715 me.addClass(XMASKED);
7716 mask.setDisplayed(true);
7717 if(typeof msg == 'string'){
7718 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
7719 data(dom, 'maskMsg', mm);
7720 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
7721 mm.dom.firstChild.innerHTML = msg;
7722 mm.setDisplayed(true);
7725 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically
7726 mask.setSize(undefined, me.getHeight());
7732 * Removes a previously applied mask.
7734 unmask : function(){
7737 mask = data(dom, 'mask'),
7738 maskMsg = data(dom, 'maskMsg');
7742 data(dom, 'maskMsg', undefined);
7745 data(dom, 'mask', undefined);
7748 me.removeClass([XMASKED, XMASKEDRELATIVE]);
7753 * Returns true if this element is masked
7756 isMasked : function(){
7757 var m = data(this.dom, 'mask');
7758 return m && m.isVisible();
7762 * Creates an iframe shim for this element to keep selects and other windowed objects from
7764 * @return {Ext.Element} The new shim element
7766 createShim : function(){
7767 var el = document.createElement('iframe'),
7769 el.frameBorder = '0';
7770 el.className = 'ext-shim';
7771 el.src = Ext.SSL_SECURE_URL;
7772 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
7773 shim.autoBoxAdjust = false;
7778 * @class Ext.Element
7780 Ext.Element.addMethods({
7782 * Convenience method for constructing a KeyMap
7783 * @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:
7784 * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
7785 * @param {Function} fn The function to call
7786 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
7787 * @return {Ext.KeyMap} The KeyMap created
7789 addKeyListener : function(key, fn, scope){
7791 if(typeof key != 'object' || Ext.isArray(key)){
7807 return new Ext.KeyMap(this, config);
7811 * Creates a KeyMap for this element
7812 * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
7813 * @return {Ext.KeyMap} The KeyMap created
7815 addKeyMap : function(config){
7816 return new Ext.KeyMap(this, config);
7822 UNDEFINED = undefined,
7836 ABSOLUTE = "absolute",
7837 VISIBLE = "visible",
7839 POSITION = "position",
7840 EASEOUT = "easeOut",
7842 * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element
7844 flyEl = new Ext.Element.Flyweight(),
7846 getObject = function(o){
7849 fly = function(dom){
7851 flyEl.id = Ext.id(dom);
7855 * Queueing now stored outside of the element due to closure issues
7857 getQueue = function(id){
7863 setQueue = function(id, value){
7867 //Notifies Element that fx methods are available
7868 Ext.enableFx = TRUE;
7872 * <p>A class to provide basic animation and visual effects support. <b>Note:</b> This class is automatically applied
7873 * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.
7874 * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be
7875 * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>
7877 * <p><b><u>Method Chaining</u></b></p>
7878 * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
7879 * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
7880 * method chain. The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
7881 * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately. For this reason,
7882 * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
7883 * expected results and should be done with care. Also see <tt>{@link #callback}</tt>.</p><br/>
7885 * <p><b><u>Anchor Options for Motion Effects</u></b></p>
7886 * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
7887 * that will serve as either the start or end point of the animation. Following are all of the supported anchor positions:</p>
7890 ----- -----------------------------
7891 tl The top left corner
7892 t The center of the top edge
7893 tr The top right corner
7894 l The center of the left edge
7895 r The center of the right edge
7896 bl The bottom left corner
7897 b The center of the bottom edge
7898 br The bottom right corner
7900 * <b>Note</b>: some Fx methods accept specific custom config parameters. The options shown in the Config Options
7901 * section below are common options that can be passed to any Fx method unless otherwise noted.</b>
7903 * @cfg {Function} callback A function called when the effect is finished. Note that effects are queued internally by the
7904 * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together
7905 * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>
7906 * el.slideIn().highlight();
7908 * The callback is intended for any additional code that should run once a particular effect has completed. The Element
7909 * being operated upon is passed as the first parameter.
7911 * @cfg {Object} scope The scope (<code>this</code> reference) in which the <tt>{@link #callback}</tt> function is executed. Defaults to the browser window.
7913 * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>
7914 * <li><b><tt>backBoth</tt></b></li>
7915 * <li><b><tt>backIn</tt></b></li>
7916 * <li><b><tt>backOut</tt></b></li>
7917 * <li><b><tt>bounceBoth</tt></b></li>
7918 * <li><b><tt>bounceIn</tt></b></li>
7919 * <li><b><tt>bounceOut</tt></b></li>
7920 * <li><b><tt>easeBoth</tt></b></li>
7921 * <li><b><tt>easeBothStrong</tt></b></li>
7922 * <li><b><tt>easeIn</tt></b></li>
7923 * <li><b><tt>easeInStrong</tt></b></li>
7924 * <li><b><tt>easeNone</tt></b></li>
7925 * <li><b><tt>easeOut</tt></b></li>
7926 * <li><b><tt>easeOutStrong</tt></b></li>
7927 * <li><b><tt>elasticBoth</tt></b></li>
7928 * <li><b><tt>elasticIn</tt></b></li>
7929 * <li><b><tt>elasticOut</tt></b></li>
7932 * @cfg {String} afterCls A css class to apply after the effect
7933 * @cfg {Number} duration The length of time (in seconds) that the effect should last
7935 * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between
7936 * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.
7938 * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
7939 * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to
7940 * effects that end with the element being visually hidden, ignored otherwise)
7941 * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object
7942 * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the
7943 * Element after the effect finishes.
7944 * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
7945 * @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
7946 * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)
7950 // private - calls the function taking arguments from the argHash based on the key. Returns the return value of the function.
7951 // this is useful for replacing switch statements (for example).
7952 switchStatements : function(key, fn, argHash){
7953 return fn.apply(this, argHash[key]);
7957 * Slides the element into view. An anchor point can be optionally passed to set the point of
7958 * origin for the slide effect. This function automatically handles wrapping the element with
7959 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
7962 // default: slide the element in from the top
7965 // custom: slide the element in from the right with a 2-second duration
7966 el.slideIn('r', { duration: 2 });
7968 // common config options shown with default values
7974 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
7975 * @param {Object} options (optional) Object literal with any of the Fx config options
7976 * @return {Ext.Element} The Element
7978 slideIn : function(anchor, o){
7994 anchor = anchor || "t";
7996 me.queueFx(o, function(){
7997 xy = fly(dom).getXY();
7998 // fix display to visibility
7999 fly(dom).fixDisplay();
8001 // restore values after effect
8002 r = fly(dom).getFxRestore();
8003 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
8004 b.right = b.x + b.width;
8005 b.bottom = b.y + b.height;
8007 // fixed size for slide
8008 fly(dom).setWidth(b.width).setHeight(b.height);
8011 wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);
8013 st.visibility = VISIBLE;
8014 st.position = ABSOLUTE;
8016 // clear out temp styles after slide and unwrap
8018 fly(dom).fxUnwrap(wrap, r.pos, o);
8020 st.height = r.height;
8021 fly(dom).afterFx(o);
8024 // time to calculate the positions
8025 pt = {to: [b.x, b.y]};
8027 bh = {to: b.height};
8029 function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){
8031 fly(wrap).setWidth(ww).setHeight(wh);
8033 fly(wrap)[sXY](sXYval);
8035 style[s1] = style[s2] = "0";
8048 args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
8049 t : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],
8050 l : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],
8051 r : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],
8052 b : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],
8053 tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],
8054 bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],
8055 br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],
8056 tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]
8059 st.visibility = VISIBLE;
8062 arguments.callee.anim = fly(wrap).fxanim(args,
8073 * Slides the element out of view. An anchor point can be optionally passed to set the end point
8074 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
8075 * 'hidden') but block elements will still take up space in the document. The element must be removed
8076 * from the DOM using the 'remove' config option if desired. This function automatically handles
8077 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
8080 // default: slide the element out to the top
8083 // custom: slide the element out to the right with a 2-second duration
8084 el.slideOut('r', { duration: 2 });
8086 // common config options shown with default values
8094 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
8095 * @param {Object} options (optional) Object literal with any of the Fx config options
8096 * @return {Ext.Element} The Element
8098 slideOut : function(anchor, o){
8110 anchor = anchor || "t";
8112 me.queueFx(o, function(){
8114 // restore values after effect
8115 r = fly(dom).getFxRestore();
8116 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
8117 b.right = b.x + b.width;
8118 b.bottom = b.y + b.height;
8120 // fixed size for slide
8121 fly(dom).setWidth(b.width).setHeight(b.height);
8124 wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);
8126 st.visibility = VISIBLE;
8127 st.position = ABSOLUTE;
8128 fly(wrap).setWidth(b.width).setHeight(b.height);
8131 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8132 fly(dom).fxUnwrap(wrap, r.pos, o);
8134 st.height = r.height;
8135 fly(dom).afterFx(o);
8138 function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){
8141 style[s1] = style[s2] = "0";
8153 a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
8154 t : [st, LEFT, BOTTOM, HEIGHT, zero],
8155 l : [st, RIGHT, TOP, WIDTH, zero],
8156 r : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],
8157 b : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8158 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],
8159 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8160 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],
8161 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]
8164 arguments.callee.anim = fly(wrap).fxanim(a,
8175 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
8176 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
8177 * The element must be removed from the DOM using the 'remove' config option if desired.
8183 // common config options shown with default values
8191 * @param {Object} options (optional) Object literal with any of the Fx config options
8192 * @return {Ext.Element} The Element
8203 me.queueFx(o, function(){
8204 width = fly(dom).getWidth();
8205 height = fly(dom).getHeight();
8206 fly(dom).clearOpacity();
8209 // restore values after effect
8210 r = fly(dom).getFxRestore();
8213 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8214 fly(dom).clearOpacity();
8215 fly(dom).setPositioning(r.pos);
8217 st.height = r.height;
8219 fly(dom).afterFx(o);
8222 arguments.callee.anim = fly(dom).fxanim({
8223 width : {to : fly(dom).adjustWidth(width * 2)},
8224 height : {to : fly(dom).adjustHeight(height * 2)},
8225 points : {by : [-width * .5, -height * .5]},
8227 fontSize: {to : 200, unit: "%"}
8239 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
8240 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
8241 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
8247 // all config options shown with default values
8255 * @param {Object} options (optional) Object literal with any of the Fx config options
8256 * @return {Ext.Element} The Element
8258 switchOff : function(o){
8265 me.queueFx(o, function(){
8266 fly(dom).clearOpacity();
8269 // restore values after effect
8270 r = fly(dom).getFxRestore();
8273 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8274 fly(dom).clearOpacity();
8275 fly(dom).setPositioning(r.pos);
8277 st.height = r.height;
8278 fly(dom).afterFx(o);
8281 fly(dom).fxanim({opacity : {to : 0.3}},
8287 fly(dom).clearOpacity();
8291 points : {by : [0, fly(dom).getHeight() * .5]}
8305 * Highlights the Element by setting a color (applies to the background-color by default, but can be
8306 * changed using the "attr" config option) and then fading back to the original color. If no original
8307 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
8310 // default: highlight background to yellow
8313 // custom: highlight foreground text to blue for 2 seconds
8314 el.highlight("0000ff", { attr: 'color', duration: 2 });
8316 // common config options shown with default values
8317 el.highlight("ffff9c", {
8318 attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
8319 endColor: (current color) or "ffffff",
8324 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
8325 * @param {Object} options (optional) Object literal with any of the Fx config options
8326 * @return {Ext.Element} The Element
8328 highlight : function(color, o){
8332 attr = o.attr || "backgroundColor",
8336 me.queueFx(o, function(){
8337 fly(dom).clearOpacity();
8341 dom.style[attr] = restore;
8342 fly(dom).afterFx(o);
8344 restore = dom.style[attr];
8345 a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};
8346 arguments.callee.anim = fly(dom).fxanim(a,
8357 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
8360 // default: a single light blue ripple
8363 // custom: 3 red ripples lasting 3 seconds total
8364 el.frame("ff0000", 3, { duration: 3 });
8366 // common config options shown with default values
8367 el.frame("C3DAF9", 1, {
8368 duration: 1 //duration of each individual ripple.
8369 // Note: Easing is not configurable and will be ignored if included
8372 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
8373 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
8374 * @param {Object} options (optional) Object literal with any of the Fx config options
8375 * @return {Ext.Element} The Element
8377 frame : function(color, count, o){
8384 me.queueFx(o, function(){
8385 color = color || '#C3DAF9';
8386 if(color.length == 6){
8387 color = '#' + color;
8392 var xy = fly(dom).getXY(),
8393 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},
8395 proxy = fly(document.body || document.documentElement).createChild({
8397 position : ABSOLUTE,
8398 'z-index': 35000, // yee haw
8399 border : '0px solid ' + color
8402 return proxy.queueFx({}, animFn);
8406 arguments.callee.anim = {
8415 var scale = Ext.isBorderBox ? 2 : 1;
8416 active = proxy.anim({
8417 top : {from : b.y, to : b.y - 20},
8418 left : {from : b.x, to : b.x - 20},
8419 borderWidth : {from : 0, to : 10},
8420 opacity : {from : 1, to : 0},
8421 height : {from : b.height, to : b.height + 20 * scale},
8422 width : {from : b.width, to : b.width + 20 * scale}
8424 duration: o.duration || 1,
8425 callback: function() {
8427 --count > 0 ? queue() : fly(dom).afterFx(o);
8430 arguments.callee.anim = {
8443 * Creates a pause before any subsequent queued effects begin. If there are
8444 * no effects queued after the pause it will have no effect.
8449 * @param {Number} seconds The length of time to pause (in seconds)
8450 * @return {Ext.Element} The Element
8452 pause : function(seconds){
8456 this.queueFx({}, function(){
8457 t = setTimeout(function(){
8458 fly(dom).afterFx({});
8460 arguments.callee.anim = {
8464 fly(dom).afterFx({});
8472 * Fade an element in (from transparent to opaque). The ending opacity can be specified
8473 * using the <tt>{@link #endOpacity}</tt> config option.
8476 // default: fade in from opacity 0 to 100%
8479 // custom: fade in from opacity 0 to 75% over 2 seconds
8480 el.fadeIn({ endOpacity: .75, duration: 2});
8482 // common config options shown with default values
8484 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
8489 * @param {Object} options (optional) Object literal with any of the Fx config options
8490 * @return {Ext.Element} The Element
8492 fadeIn : function(o){
8496 to = o.endOpacity || 1;
8498 me.queueFx(o, function(){
8499 fly(dom).setOpacity(0);
8500 fly(dom).fixDisplay();
8501 dom.style.visibility = VISIBLE;
8502 arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},
8503 o, NULL, .5, EASEOUT, function(){
8505 fly(dom).clearOpacity();
8507 fly(dom).afterFx(o);
8514 * Fade an element out (from opaque to transparent). The ending opacity can be specified
8515 * using the <tt>{@link #endOpacity}</tt> config option. Note that IE may require
8516 * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
8519 // default: fade out from the element's current opacity to 0
8522 // custom: fade out from the element's current opacity to 25% over 2 seconds
8523 el.fadeOut({ endOpacity: .25, duration: 2});
8525 // common config options shown with default values
8527 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
8534 * @param {Object} options (optional) Object literal with any of the Fx config options
8535 * @return {Ext.Element} The Element
8537 fadeOut : function(o){
8542 to = o.endOpacity || 0;
8544 me.queueFx(o, function(){
8545 arguments.callee.anim = fly(dom).fxanim({
8546 opacity : {to : to}},
8553 Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ?
8554 style.display = "none" :
8555 style.visibility = HIDDEN;
8557 fly(dom).clearOpacity();
8559 fly(dom).afterFx(o);
8566 * Animates the transition of an element's dimensions from a starting height/width
8567 * to an ending height/width. This method is a convenience implementation of {@link shift}.
8570 // change height and width to 100x100 pixels
8573 // common config options shown with default values. The height and width will default to
8574 // the element's existing values if passed as null.
8576 [element's width],
8577 [element's height], {
8583 * @param {Number} width The new width (pass undefined to keep the original width)
8584 * @param {Number} height The new height (pass undefined to keep the original height)
8585 * @param {Object} options (optional) Object literal with any of the Fx config options
8586 * @return {Ext.Element} The Element
8588 scale : function(w, h, o){
8589 this.shift(Ext.apply({}, o, {
8597 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
8598 * Any of these properties not specified in the config object will not be changed. This effect
8599 * requires that at least one new dimension, position or opacity setting must be passed in on
8600 * the config object in order for the function to have any effect.
8603 // slide the element horizontally to x position 200 while changing the height and opacity
8604 el.shift({ x: 200, height: 50, opacity: .8 });
8606 // common config options shown with default values.
8608 width: [element's width],
8609 height: [element's height],
8610 x: [element's x position],
8611 y: [element's y position],
8612 opacity: [element's opacity],
8617 * @param {Object} options Object literal with any of the Fx config options
8618 * @return {Ext.Element} The Element
8620 shift : function(o){
8625 this.queueFx(o, function(){
8626 for (var prop in o) {
8627 if (o[prop] != UNDEFINED) {
8628 a[prop] = {to : o[prop]};
8632 a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;
8633 a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;
8635 if (a.x || a.y || a.xy) {
8637 {to : [ a.x ? a.x.to : fly(dom).getX(),
8638 a.y ? a.y.to : fly(dom).getY()]};
8641 arguments.callee.anim = fly(dom).fxanim(a,
8647 fly(dom).afterFx(o);
8654 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
8655 * ending point of the effect.
8658 // default: slide the element downward while fading out
8661 // custom: slide the element out to the right with a 2-second duration
8662 el.ghost('r', { duration: 2 });
8664 // common config options shown with default values
8672 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
8673 * @param {Object} options (optional) Object literal with any of the Fx config options
8674 * @return {Ext.Element} The Element
8676 ghost : function(anchor, o){
8681 a = {opacity: {to: 0}, points: {}},
8687 anchor = anchor || "b";
8689 me.queueFx(o, function(){
8690 // restore values after effect
8691 r = fly(dom).getFxRestore();
8692 w = fly(dom).getWidth();
8693 h = fly(dom).getHeight();
8696 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8697 fly(dom).clearOpacity();
8698 fly(dom).setPositioning(r.pos);
8700 st.height = r.height;
8701 fly(dom).afterFx(o);
8704 pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {
8715 arguments.callee.anim = fly(dom).fxanim(a,
8725 * Ensures that all effects queued after syncFx is called on the element are
8726 * run concurrently. This is the opposite of {@link #sequenceFx}.
8727 * @return {Ext.Element} The Element
8729 syncFx : function(){
8731 me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8740 * Ensures that all effects queued after sequenceFx is called on the element are
8741 * run in sequence. This is the opposite of {@link #syncFx}.
8742 * @return {Ext.Element} The Element
8744 sequenceFx : function(){
8746 me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8755 nextFx : function(){
8756 var ef = getQueue(this.dom.id)[0];
8763 * Returns true if the element has any effects actively running or queued, else returns false.
8764 * @return {Boolean} True if element has active effects, else false
8766 hasActiveFx : function(){
8767 return getQueue(this.dom.id)[0];
8771 * Stops any running effects and clears the element's internal effects queue if it contains
8772 * any additional effects that haven't started yet.
8773 * @return {Ext.Element} The Element
8775 stopFx : function(finish){
8778 if(me.hasActiveFx()){
8779 var cur = getQueue(id)[0];
8780 if(cur && cur.anim){
8781 if(cur.anim.isAnimated){
8782 setQueue(id, [cur]); //clear
8783 cur.anim.stop(finish !== undefined ? finish : TRUE);
8793 beforeFx : function(o){
8794 if(this.hasActiveFx() && !o.concurrent){
8805 * Returns true if the element is currently blocking so that no other effect can be queued
8806 * until this effect is finished, else returns false if blocking is not set. This is commonly
8807 * used to ensure that an effect initiated by a user action runs to completion prior to the
8808 * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
8809 * @return {Boolean} True if blocking, else false
8811 hasFxBlock : function(){
8812 var q = getQueue(this.dom.id);
8813 return q && q[0] && q[0].block;
8817 queueFx : function(o, fn){
8818 var me = fly(this.dom);
8819 if(!me.hasFxBlock()){
8820 Ext.applyIf(o, me.fxDefaults);
8822 var run = me.beforeFx(o);
8824 getQueue(me.dom.id).push(fn);
8836 fxWrap : function(pos, o, vis){
8840 if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){
8842 wrapXY = fly(dom).getXY();
8844 var div = document.createElement("div");
8845 div.style.visibility = vis;
8846 wrap = dom.parentNode.insertBefore(div, dom);
8847 fly(wrap).setPositioning(pos);
8848 if(fly(wrap).isStyle(POSITION, "static")){
8849 fly(wrap).position("relative");
8851 fly(dom).clearPositioning('auto');
8853 wrap.appendChild(dom);
8855 fly(wrap).setXY(wrapXY);
8862 fxUnwrap : function(wrap, pos, o){
8864 fly(dom).clearPositioning();
8865 fly(dom).setPositioning(pos);
8867 var pn = fly(wrap).dom.parentNode;
8868 pn.insertBefore(dom, wrap);
8874 getFxRestore : function(){
8875 var st = this.dom.style;
8876 return {pos: this.getPositioning(), width: st.width, height : st.height};
8880 afterFx : function(o){
8884 fly(dom).setStyle(o.afterStyle);
8887 fly(dom).addClass(o.afterCls);
8889 if(o.remove == TRUE){
8893 o.callback.call(o.scope, fly(dom));
8896 getQueue(id).shift();
8902 fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
8903 animType = animType || 'run';
8905 var anim = Ext.lib.Anim[animType](
8908 (opt.duration || defaultDur) || .35,
8909 (opt.easing || defaultEase) || EASEOUT,
8919 Ext.Fx.resize = Ext.Fx.scale;
8921 //When included, Ext.Fx is automatically applied to Element so that all basic
8922 //effects are available directly via the Element API
8923 Ext.Element.addMethods(Ext.Fx);
8926 * @class Ext.CompositeElementLite
8927 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
8928 * members, or to perform collective actions upon the whole set.</p>
8929 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
8930 * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
8931 * Example:<pre><code>
8932 var els = Ext.select("#some-el div.some-class");
8933 // or select directly from an existing element
8934 var el = Ext.get('some-el');
8935 el.select('div.some-class');
8937 els.setWidth(100); // all elements become 100 width
8938 els.hide(true); // all elements fade out and hide
8940 els.setWidth(100).hide(true);
8943 Ext.CompositeElementLite = function(els, root){
8945 * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
8946 * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
8947 * to augment the capabilities of the CompositeElementLite class may use it when adding
8948 * methods to the class.</p>
8949 * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
8950 * following siblings of selected elements, the code would be</p><code><pre>
8951 Ext.override(Ext.CompositeElementLite, {
8952 nextAll: function() {
8953 var els = this.elements, i, l = els.length, n, r = [], ri = -1;
8955 // Loop through all elements in this Composite, accumulating
8956 // an Array of all siblings.
8957 for (i = 0; i < l; i++) {
8958 for (n = els[i].nextSibling; n; n = n.nextSibling) {
8963 // Add all found siblings to this Composite
8968 * @property elements
8971 this.add(els, root);
8972 this.el = new Ext.Element.Flyweight();
8975 Ext.CompositeElementLite.prototype = {
8979 getElement : function(el){
8980 // Set the shared flyweight dom property to the current element
8988 transformElement : function(el){
8989 return Ext.getDom(el);
8993 * Returns the number of elements in this Composite.
8996 getCount : function(){
8997 return this.elements.length;
9000 * Adds elements to this Composite object.
9001 * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
9002 * @return {CompositeElement} This Composite object.
9004 add : function(els, root){
9006 elements = me.elements;
9010 if(typeof els == "string"){
9011 els = Ext.Element.selectorFunction(els, root);
9012 }else if(els.isComposite){
9014 }else if(!Ext.isIterable(els)){
9018 for(var i = 0, len = els.length; i < len; ++i){
9019 elements.push(me.transformElement(els[i]));
9024 invoke : function(fn, args){
9031 for(i = 0; i < len; i++) {
9034 Ext.Element.prototype[fn].apply(me.getElement(e), args);
9040 * Returns a flyweight Element of the dom element object at the specified index
9041 * @param {Number} index
9042 * @return {Ext.Element}
9044 item : function(index){
9046 el = me.elements[index],
9050 out = me.getElement(el);
9055 // fixes scope with flyweight
9056 addListener : function(eventName, handler, scope, opt){
9057 var els = this.elements,
9061 for(i = 0; i<len; i++) {
9064 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
9070 * <p>Calls the passed function for each element in this composite.</p>
9071 * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
9072 * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
9073 * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
9074 * a reference to the dom node, use el.dom.</b></div></li>
9075 * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
9076 * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
9078 * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
9079 * @return {CompositeElement} this
9081 each : function(fn, scope){
9087 for(i = 0; i<len; i++) {
9090 e = this.getElement(e);
9091 if(fn.call(scope || e, e, me, i) === false){
9100 * Clears this Composite and adds the elements passed.
9101 * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
9102 * @return {CompositeElement} this
9104 fill : function(els){
9112 * Filters this composite to only elements that match the passed selector.
9113 * @param {String/Function} selector A string CSS selector or a comparison function.
9114 * The comparison function will be called with the following arguments:<ul>
9115 * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
9116 * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
9118 * @return {CompositeElement} this
9120 filter : function(selector){
9123 fn = Ext.isFunction(selector) ? selector
9125 return el.is(selector);
9128 me.each(function(el, self, i) {
9129 if (fn(el, i) !== false) {
9130 els[els.length] = me.transformElement(el);
9139 * Find the index of the passed element within the composite collection.
9140 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9141 * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
9143 indexOf : function(el){
9144 return this.elements.indexOf(this.transformElement(el));
9148 * Replaces the specified element with the passed element.
9149 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9151 * @param {Mixed} replacement The id of an element or the Element itself.
9152 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
9153 * @return {CompositeElement} this
9155 replaceElement : function(el, replacement, domReplace){
9156 var index = !isNaN(el) ? el : this.indexOf(el),
9159 replacement = Ext.getDom(replacement);
9161 d = this.elements[index];
9162 d.parentNode.insertBefore(replacement, d);
9165 this.elements.splice(index, 1, replacement);
9171 * Removes all elements.
9178 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
9182 ElProto = Ext.Element.prototype,
9183 CelProto = Ext.CompositeElementLite.prototype;
9185 for(fnName in ElProto){
9186 if(Ext.isFunction(ElProto[fnName])){
9188 CelProto[fnName] = CelProto[fnName] || function(){
9189 return this.invoke(fnName, arguments);
9191 }).call(CelProto, fnName);
9198 Ext.Element.selectorFunction = Ext.DomQuery.select;
9202 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9203 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9204 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9205 * @param {String/Array} selector The CSS selector or an array of elements
9206 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9207 * @return {CompositeElementLite/CompositeElement}
9208 * @member Ext.Element
9211 Ext.Element.select = function(selector, root){
9213 if(typeof selector == "string"){
9214 els = Ext.Element.selectorFunction(selector, root);
9215 }else if(selector.length !== undefined){
9218 throw "Invalid selector";
9220 return new Ext.CompositeElementLite(els);
9223 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9224 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9225 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9226 * @param {String/Array} selector The CSS selector or an array of elements
9227 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9228 * @return {CompositeElementLite/CompositeElement}
9232 Ext.select = Ext.Element.select;
9234 * @class Ext.CompositeElementLite
9236 Ext.apply(Ext.CompositeElementLite.prototype, {
9237 addElements : function(els, root){
9241 if(typeof els == "string"){
9242 els = Ext.Element.selectorFunction(els, root);
9244 var yels = this.elements;
9245 Ext.each(els, function(e) {
9246 yels.push(Ext.get(e));
9252 * Returns the first Element
9253 * @return {Ext.Element}
9256 return this.item(0);
9260 * Returns the last Element
9261 * @return {Ext.Element}
9264 return this.item(this.getCount()-1);
9268 * Returns true if this composite contains the passed element
9269 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9272 contains : function(el){
9273 return this.indexOf(el) != -1;
9277 * Removes the specified element(s).
9278 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9279 * or an array of any of those.
9280 * @param {Boolean} removeDom (optional) True to also remove the element from the document
9281 * @return {CompositeElement} this
9283 removeElement : function(keys, removeDom){
9285 els = this.elements,
9287 Ext.each(keys, function(val){
9288 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
9303 * @class Ext.CompositeElement
9304 * @extends Ext.CompositeElementLite
9305 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
9306 * members, or to perform collective actions upon the whole set.</p>
9307 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
9308 * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
9309 * <p>All methods return <i>this</i> and can be chained.</p>
9312 var els = Ext.select("#some-el div.some-class", true);
9313 // or select directly from an existing element
9314 var el = Ext.get('some-el');
9315 el.select('div.some-class', true);
9317 els.setWidth(100); // all elements become 100 width
9318 els.hide(true); // all elements fade out and hide
9320 els.setWidth(100).hide(true);
9323 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
9325 constructor : function(els, root){
9327 this.add(els, root);
9331 getElement : function(el){
9332 // In this case just return it, since we already have a reference to it
9337 transformElement : function(el){
9342 * Adds elements to this composite.
9343 * @param {String/Array} els A string CSS selector, an array of elements or an element
9344 * @return {CompositeElement} this
9348 * Returns the Element object at the specified index
9349 * @param {Number} index
9350 * @return {Ext.Element}
9354 * Iterates each <code>element</code> in this <code>composite</code>
9355 * calling the supplied function using {@link Ext#each}.
9356 * @param {Function} fn The function to be called with each
9357 * <code>element</code>. If the supplied function returns <tt>false</tt>,
9358 * iteration stops. This function is called with the following arguments:
9359 * <div class="mdetail-params"><ul>
9360 * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>
9361 * in the <code>composite</code></div></li>
9362 * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>
9363 * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>
9365 * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.
9366 * Defaults to the <code>element</code> at the current <code>index</code>
9367 * within the composite.
9368 * @return {CompositeElement} this
9373 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9374 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9375 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9376 * @param {String/Array} selector The CSS selector or an array of elements
9377 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9378 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9379 * @return {CompositeElementLite/CompositeElement}
9380 * @member Ext.Element
9383 Ext.Element.select = function(selector, unique, root){
9385 if(typeof selector == "string"){
9386 els = Ext.Element.selectorFunction(selector, root);
9387 }else if(selector.length !== undefined){
9390 throw "Invalid selector";
9393 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
9397 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9398 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9399 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9400 * @param {String/Array} selector The CSS selector or an array of elements
9401 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9402 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9403 * @return {CompositeElementLite/CompositeElement}
9407 Ext.select = Ext.Element.select;(function(){
9408 var BEFOREREQUEST = "beforerequest",
9409 REQUESTCOMPLETE = "requestcomplete",
9410 REQUESTEXCEPTION = "requestexception",
9411 UNDEFINED = undefined,
9418 * @class Ext.data.Connection
9419 * @extends Ext.util.Observable
9420 * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made
9421 * either to a configured URL, or to a URL specified at request time.</p>
9422 * <p>Requests made by this class are asynchronous, and will return immediately. No data from
9423 * the server will be available to the statement immediately following the {@link #request} call.
9424 * To process returned data, use a
9425 * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>
9426 * in the request options object,
9427 * or an {@link #requestcomplete event listener}.</p>
9428 * <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
9429 * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
9430 * manner with the DOM <tt><form></tt> element temporarily modified to have its
9431 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9432 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
9433 * but removed after the return data has been gathered.</p>
9434 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9435 * server is using JSON to send the return object, then the
9436 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9437 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9438 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
9439 * "<" as "&lt;", "&" as "&amp;" etc.</p>
9440 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9441 * is created containing a <tt>responseText</tt> property in order to conform to the
9442 * requirements of event handlers and callbacks.</p>
9443 * <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>
9444 * and some server technologies (notably JEE) may require some custom processing in order to
9445 * retrieve parameter names and parameter values from the packet content.</p>
9446 * <p>Also note that it's not possible to check the response code of the hidden iframe, so the success handler will ALWAYS fire.</p>
9448 * @param {Object} config a configuration object.
9450 Ext.data.Connection = function(config){
9451 Ext.apply(this, config);
9454 * @event beforerequest
9455 * Fires before a network request is made to retrieve a data object.
9456 * @param {Connection} conn This Connection object.
9457 * @param {Object} options The options config object passed to the {@link #request} method.
9461 * @event requestcomplete
9462 * Fires if the request was successfully completed.
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 * @event requestexception
9472 * Fires if an error HTTP status was returned from the server.
9473 * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
9474 * for details of HTTP status codes.
9475 * @param {Connection} conn This Connection object.
9476 * @param {Object} response The XHR object containing the response data.
9477 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9479 * @param {Object} options The options config object passed to the {@link #request} method.
9483 Ext.data.Connection.superclass.constructor.call(this);
9486 Ext.extend(Ext.data.Connection, Ext.util.Observable, {
9488 * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>
9489 * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope
9490 * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>
9493 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9494 * extra parameters to each request made by this object. (defaults to undefined)
9497 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9498 * to each request made by this object. (defaults to undefined)
9501 * @cfg {String} method (Optional) The default HTTP method to be used for requests.
9502 * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;
9503 * otherwise, GET will be used.)
9506 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9510 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9516 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9519 disableCaching: true,
9522 * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
9523 * through a cache buster. Defaults to '_dc'
9526 disableCachingParam: '_dc',
9529 * <p>Sends an HTTP request to a remote server.</p>
9530 * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
9531 * return before the response has been received. Process any returned data
9532 * in a callback function.</p>
9535 url: 'ajax_demo/sample.json',
9536 success: function(response, opts) {
9537 var obj = Ext.decode(response.responseText);
9540 failure: function(response, opts) {
9541 console.log('server-side failure with status code ' + response.status);
9545 * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
9546 * @param {Object} options An object which may contain the following properties:<ul>
9547 * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
9548 * which to send the request, or a function to call which returns a URL string. The scope of the
9549 * function is specified by the <tt>scope</tt> option. Defaults to the configured
9550 * <tt>{@link #url}</tt>.</div></li>
9551 * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
9552 * An object containing properties which are used as parameters to the
9553 * request, a url encoded string or a function to call to get either. The scope of the function
9554 * is specified by the <tt>scope</tt> option.</div></li>
9555 * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
9556 * for the request. Defaults to the configured method, or if no method was configured,
9557 * "GET" if no parameters are being sent, and "POST" if parameters are being sent. Note that
9558 * the method name is case-sensitive and should be all caps.</div></li>
9559 * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
9560 * function to be called upon receipt of the HTTP response. The callback is
9561 * called regardless of success or failure and is passed the following
9563 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9564 * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
9565 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
9566 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
9567 * accessing elements of the response.</div></li>
9569 * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
9570 * to be called upon success of the request. The callback is passed the following
9572 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9573 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9575 * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
9576 * to be called upon failure of the request. The callback is passed the
9577 * following parameters:<ul>
9578 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9579 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9581 * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
9582 * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
9583 * specified as functions from which to draw values, then this also serves as the scope for those function calls.
9584 * Defaults to the browser window.</div></li>
9585 * <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>
9586 * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt><form></tt>
9587 * Element or the id of the <tt><form></tt> to pull parameters from.</div></li>
9588 * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
9589 * with the <tt>form</tt> option</b>.
9590 * <p>True if the form object is a file upload (will be set automatically if the form was
9591 * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
9592 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
9593 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
9594 * DOM <tt><form></tt> element temporarily modified to have its
9595 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9596 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
9597 * but removed after the return data has been gathered.</p>
9598 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9599 * server is using JSON to send the return object, then the
9600 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9601 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9602 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9603 * is created containing a <tt>responseText</tt> property in order to conform to the
9604 * requirements of event handlers and callbacks.</p>
9605 * <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>
9606 * and some server technologies (notably JEE) may require some custom processing in order to
9607 * retrieve parameter names and parameter values from the packet content.</p>
9609 * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
9610 * headers to set for the request.</div></li>
9611 * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
9612 * to use for the post. Note: This will be used instead of params for the post
9613 * data. Any params will be appended to the URL.</div></li>
9614 * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
9615 * data to use as the post. Note: This will be used instead of params for the post
9616 * data. Any params will be appended to the URL.</div></li>
9617 * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
9618 * to add a unique cache-buster param to GET requests.</div></li>
9620 * <p>The options object may also contain any other property which might be needed to perform
9621 * postprocessing in a callback because it is passed to callback functions.</p>
9622 * @return {Number} transactionId The id of the server transaction. This may be used
9623 * to cancel the request.
9625 request : function(o){
9627 if(me.fireEvent(BEFOREREQUEST, me, o)){
9629 if(!Ext.isEmpty(o.indicatorText)){
9630 me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";
9632 if(me.indicatorText) {
9633 Ext.getDom(o.el).innerHTML = me.indicatorText;
9635 o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {
9636 Ext.getDom(o.el).innerHTML = response.responseText;
9641 url = o.url || me.url,
9643 cb = {success: me.handleResponse,
9644 failure: me.handleFailure,
9646 argument: {options: o},
9647 timeout : o.timeout || me.timeout
9653 if (Ext.isFunction(p)) {
9654 p = p.call(o.scope||WINDOW, o);
9657 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);
9659 if (Ext.isFunction(url)) {
9660 url = url.call(o.scope || WINDOW, o);
9663 if((form = Ext.getDom(o.form))){
9664 url = url || form.action;
9665 if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) {
9666 return me.doFormUpload.call(me, o, p, url);
9668 serForm = Ext.lib.Ajax.serializeForm(form);
9669 p = p ? (p + '&' + serForm) : serForm;
9672 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);
9674 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
9675 var dcp = o.disableCachingParam || me.disableCachingParam;
9676 url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));
9679 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});
9681 if(o.autoAbort === true || me.autoAbort) {
9685 if((method == GET || o.xmlData || o.jsonData) && p){
9686 url = Ext.urlAppend(url, p);
9689 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));
9691 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;
9696 * Determine whether this object has a request outstanding.
9697 * @param {Number} transactionId (Optional) defaults to the last transaction
9698 * @return {Boolean} True if there is an outstanding request.
9700 isLoading : function(transId){
9701 return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;
9705 * Aborts any outstanding request.
9706 * @param {Number} transactionId (Optional) defaults to the last transaction
9708 abort : function(transId){
9709 if(transId || this.isLoading()){
9710 Ext.lib.Ajax.abort(transId || this.transId);
9715 handleResponse : function(response){
9716 this.transId = false;
9717 var options = response.argument.options;
9718 response.argument = options ? options.argument : null;
9719 this.fireEvent(REQUESTCOMPLETE, this, response, options);
9720 if(options.success){
9721 options.success.call(options.scope, response, options);
9723 if(options.callback){
9724 options.callback.call(options.scope, options, true, response);
9729 handleFailure : function(response, e){
9730 this.transId = false;
9731 var options = response.argument.options;
9732 response.argument = options ? options.argument : null;
9733 this.fireEvent(REQUESTEXCEPTION, this, response, options, e);
9734 if(options.failure){
9735 options.failure.call(options.scope, response, options);
9737 if(options.callback){
9738 options.callback.call(options.scope, options, false, response);
9743 doFormUpload : function(o, ps, url){
9746 frame = doc.createElement('iframe'),
9747 form = Ext.getDom(o.form),
9750 encoding = 'multipart/form-data',
9752 target: form.target,
9753 method: form.method,
9754 encoding: form.encoding,
9755 enctype: form.enctype,
9760 * Originally this behaviour was modified for Opera 10 to apply the secure URL after
9761 * the frame had been added to the document. It seems this has since been corrected in
9762 * Opera so the behaviour has been reverted, the URL will be set before being added.
9764 Ext.fly(frame).set({
9768 src: Ext.SSL_SECURE_URL
9771 doc.body.appendChild(frame);
9773 // This is required so that IE doesn't pop the response up in a new window.
9775 document.frames[id].name = id;
9784 action: url || buf.action
9787 // add dynamic params
9788 Ext.iterate(Ext.urlDecode(ps, false), function(k, v){
9789 hd = doc.createElement('input');
9795 form.appendChild(hd);
9801 // bogus response object
9802 r = {responseText : '',
9804 argument : o.argument},
9809 doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
9812 if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea
9813 r.responseText = firstChild.value;
9815 r.responseText = doc.body.innerHTML;
9818 //in IE the document may still have a body even if returns XML.
9819 r.responseXML = doc.XMLDocument || doc;
9824 Ext.EventManager.removeListener(frame, LOAD, cb, me);
9826 me.fireEvent(REQUESTCOMPLETE, me, r, o);
9828 function runCallback(fn, scope, args){
9829 if(Ext.isFunction(fn)){
9830 fn.apply(scope, args);
9834 runCallback(o.success, o.scope, [r, o]);
9835 runCallback(o.callback, o.scope, [o, true, r]);
9837 if(!me.debugUploads){
9838 setTimeout(function(){Ext.removeNode(frame);}, 100);
9842 Ext.EventManager.on(frame, LOAD, cb, this);
9845 Ext.fly(form).set(buf);
9846 Ext.each(hiddens, function(h) {
9855 * @extends Ext.data.Connection
9856 * <p>The global Ajax request class that provides a simple way to make Ajax requests
9857 * with maximum flexibility.</p>
9858 * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once
9859 * and override them at the request function level only if necessary.</p>
9860 * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>
9861 * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>
9862 * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>
9863 * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>
9866 // Default headers to pass in every request
9867 Ext.Ajax.defaultHeaders = {
9872 * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>
9873 * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>
9874 * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>
9875 * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>
9878 // Example: show a spinner during all Ajax requests
9879 Ext.Ajax.on('beforerequest', this.showSpinner, this);
9880 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
9881 Ext.Ajax.on('requestexception', this.hideSpinner, this);
9884 * <p>An example request:</p>
9887 Ext.Ajax.{@link Ext.data.Connection#request request}({
9894 params: { foo: 'bar' }
9897 // Simple ajax form submission
9898 Ext.Ajax.{@link Ext.data.Connection#request request}({
9906 Ext.Ajax = new Ext.data.Connection({
9908 * @cfg {String} url @hide
9911 * @cfg {Object} extraParams @hide
9914 * @cfg {Object} defaultHeaders @hide
9917 * @cfg {String} method (Optional) @hide
9920 * @cfg {Number} timeout (Optional) @hide
9923 * @cfg {Boolean} autoAbort (Optional) @hide
9927 * @cfg {Boolean} disableCaching (Optional) @hide
9931 * @property disableCaching
9932 * True to add a unique cache-buster param to GET requests. (defaults to true)
9937 * The default URL to be used for requests to the server. (defaults to undefined)
9938 * If the server receives all requests through one URL, setting this once is easier than
9939 * entering it on every request.
9943 * @property extraParams
9944 * An object containing properties which are used as extra parameters to each request made
9945 * by this object (defaults to undefined). Session information and other data that you need
9946 * to pass with each request are commonly put here.
9950 * @property defaultHeaders
9951 * An object containing request headers which are added to each request made by this object
9952 * (defaults to undefined).
9957 * The default HTTP method to be used for requests. Note that this is case-sensitive and
9958 * should be all caps (defaults to undefined; if not set but params are present will use
9959 * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
9964 * The timeout in milliseconds to be used for requests. (defaults to 30000)
9969 * @property autoAbort
9970 * Whether a new request should abort any pending requests. (defaults to false)
9976 * Serialize the passed form into a url encoded string
9977 * @param {String/HTMLElement} form
9980 serializeForm : function(form){
9981 return Ext.lib.Ajax.serializeForm(form);
9985 * @class Ext.Updater
9986 * @extends Ext.util.Observable
9987 * Provides AJAX-style update capabilities for Element objects. Updater can be used to {@link #update}
9988 * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9989 * {@link Ext.Element Element} on a specific interval.<br><br>
9992 * var el = Ext.get("foo"); // Get Ext.Element object
9993 * var mgr = el.getUpdater();
9995 url: "http://myserver.com/index.php",
10002 * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
10004 * // or directly (returns the same Updater instance)
10005 * var mgr = new Ext.Updater("myElementId");
10006 * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
10007 * mgr.on("update", myFcnNeedsToKnow);
10009 * // short handed call directly from the element object
10010 * Ext.get("foo").load({
10013 params: "param1=foo&param2=bar",
10014 text: "Loading Foo..."
10018 * Create new Updater directly.
10019 * @param {Mixed} el The element to update
10020 * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
10021 * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
10023 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
10025 var BEFOREUPDATE = "beforeupdate",
10027 FAILURE = "failure";
10030 function processSuccess(response){
10032 me.transaction = null;
10033 if (response.argument.form && response.argument.reset) {
10034 try { // put in try/catch since some older FF releases had problems with this
10035 response.argument.form.reset();
10038 if (me.loadScripts) {
10039 me.renderer.render(me.el, response, me,
10040 updateComplete.createDelegate(me, [response]));
10042 me.renderer.render(me.el, response, me);
10043 updateComplete.call(me, response);
10048 function updateComplete(response, type, success){
10049 this.fireEvent(type || UPDATE, this.el, response);
10050 if(Ext.isFunction(response.argument.callback)){
10051 response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
10056 function processFailure(response){
10057 updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
10061 constructor: function(el, forceNew){
10064 if(!forceNew && el.updateManager){
10065 return el.updateManager;
10068 * The Element object
10069 * @type Ext.Element
10073 * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
10076 me.defaultUrl = null;
10080 * @event beforeupdate
10081 * Fired before an update is made, return false from your handler and the update is cancelled.
10082 * @param {Ext.Element} el
10083 * @param {String/Object/Function} url
10084 * @param {String/Object} params
10089 * Fired after successful update is made.
10090 * @param {Ext.Element} el
10091 * @param {Object} oResponseObject The response Object
10096 * Fired on update failure.
10097 * @param {Ext.Element} el
10098 * @param {Object} oResponseObject The response Object
10103 Ext.apply(me, Ext.Updater.defaults);
10105 * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
10106 * @property sslBlankUrl
10110 * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
10111 * @property disableCaching
10115 * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
10116 * @property indicatorText
10120 * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
10121 * @property showLoadIndicator
10125 * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
10126 * @property timeout
10130 * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
10131 * @property loadScripts
10136 * Transaction object of the current executing transaction, or null if there is no active transaction.
10138 me.transaction = null;
10140 * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
10143 me.refreshDelegate = me.refresh.createDelegate(me);
10145 * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
10148 me.updateDelegate = me.update.createDelegate(me);
10150 * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
10153 me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
10156 * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
10158 me.renderer = me.renderer || me.getDefaultRenderer();
10160 Ext.Updater.superclass.constructor.call(me);
10164 * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10165 * @param {Object} renderer The object implementing the render() method
10167 setRenderer : function(renderer){
10168 this.renderer = renderer;
10172 * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10175 getRenderer : function(){
10176 return this.renderer;
10180 * This is an overrideable method which returns a reference to a default
10181 * renderer class if none is specified when creating the Ext.Updater.
10182 * Defaults to {@link Ext.Updater.BasicRenderer}
10184 getDefaultRenderer: function() {
10185 return new Ext.Updater.BasicRenderer();
10189 * Sets the default URL used for updates.
10190 * @param {String/Function} defaultUrl The url or a function to call to get the url
10192 setDefaultUrl : function(defaultUrl){
10193 this.defaultUrl = defaultUrl;
10197 * Get the Element this Updater is bound to
10198 * @return {Ext.Element} The element
10200 getEl : function(){
10205 * Performs an <b>asynchronous</b> request, updating this element with the response.
10206 * If params are specified it uses POST, otherwise it uses GET.<br><br>
10207 * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
10208 * will not have been fully updated when the function returns. To post-process the returned
10209 * data, use the callback option, or an <b><code>update</code></b> event handler.
10210 * @param {Object} options A config object containing any of the following options:<ul>
10211 * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
10212 * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
10213 * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
10214 * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
10215 * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
10216 * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
10217 * string, or as an object containing properties which represent parameters,
10218 * or as a function, which returns such an object.</p></li>
10219 * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
10220 * any <script> tags embedded in the response text will be extracted
10221 * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
10222 * the callback will be called <i>after</i> the execution of the scripts.</p></li>
10223 * <li>callback : <b>Function</b><p class="sub-desc">A function to
10224 * be called when the response from the server arrives. The following
10225 * parameters are passed:<ul>
10226 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10227 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10228 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
10229 * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
10231 * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
10232 * to execute the callback (The callback's <code>this</code> reference.) If the
10233 * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
10234 * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
10235 * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
10236 * calls. To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
10237 * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
10238 * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
10239 * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
10240 * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...'). To replace the entire div, not
10241 * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
10242 * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
10243 * requests, this option causes an extra, auto-generated parameter to be appended to the request
10244 * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
10249 url: "your-url.php",
10250 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
10251 callback: yourFunction,
10252 scope: yourObject, //(optional scope)
10255 text: "Loading...",
10257 scripts: false // Save time by avoiding RegExp execution.
10261 update : function(url, params, callback, discardUrl){
10266 if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
10267 if(Ext.isObject(url)){ // must be config object
10270 params = params || cfg.params;
10271 callback = callback || cfg.callback;
10272 discardUrl = discardUrl || cfg.discardUrl;
10273 callerScope = cfg.scope;
10274 if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
10275 if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
10276 if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
10277 if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
10282 me.defaultUrl = url;
10284 if(Ext.isFunction(url)){
10285 url = url.call(me);
10288 var o = Ext.apply({}, {
10290 params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
10291 success: processSuccess,
10292 failure: processFailure,
10294 callback: undefined,
10295 timeout: (me.timeout*1000),
10296 disableCaching: me.disableCaching,
10301 "callback": callback,
10302 "scope": callerScope || window,
10307 me.transaction = Ext.Ajax.request(o);
10312 * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
10313 * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
10314 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
10315 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
10316 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
10317 * DOM <code><form></code> element temporarily modified to have its
10318 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
10319 * to a dynamically generated, hidden <code><iframe></code> which is inserted into the document
10320 * but removed after the return data has been gathered.</p>
10321 * <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>
10322 * and some server technologies (notably JEE) may require some custom processing in order to
10323 * retrieve parameter names and parameter values from the packet content.</p>
10324 * @param {String/HTMLElement} form The form Id or form element
10325 * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
10326 * @param {Boolean} reset (optional) Whether to try to reset the form after the update
10327 * @param {Function} callback (optional) Callback when transaction is complete. The following
10328 * parameters are passed:<ul>
10329 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10330 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10331 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
10333 formUpdate : function(form, url, reset, callback){
10335 if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
10336 if(Ext.isFunction(url)){
10337 url = url.call(me);
10339 form = Ext.getDom(form);
10340 me.transaction = Ext.Ajax.request({
10343 success: processSuccess,
10344 failure: processFailure,
10346 timeout: (me.timeout*1000),
10350 "callback": callback,
10354 me.showLoading.defer(1, me);
10359 * Set this element to auto refresh. Can be canceled by calling {@link #stopAutoRefresh}.
10360 * @param {Number} interval How often to update (in seconds).
10361 * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
10362 * supported by {@link #load}, or a function to call to get the url (defaults to the last used url). Note that while
10363 * the url used in a load call can be reused by this method, other load config options will not be reused and must be
10364 * sepcified as part of a config object passed as this paramter if needed.
10365 * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
10366 * "¶m1=1¶m2=2" or as an object {param1: 1, param2: 2}
10367 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10368 * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
10370 startAutoRefresh : function(interval, url, params, callback, refreshNow){
10373 me.update(url || me.defaultUrl, params, callback, true);
10375 if(me.autoRefreshProcId){
10376 clearInterval(me.autoRefreshProcId);
10378 me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
10382 * Stop auto refresh on this element.
10384 stopAutoRefresh : function(){
10385 if(this.autoRefreshProcId){
10386 clearInterval(this.autoRefreshProcId);
10387 delete this.autoRefreshProcId;
10392 * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
10394 isAutoRefreshing : function(){
10395 return !!this.autoRefreshProcId;
10399 * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
10400 * method may be overridden to perform a custom action while this Updater is actively updating its contents.
10402 showLoading : function(){
10403 if(this.showLoadIndicator){
10404 this.el.dom.innerHTML = this.indicatorText;
10409 * Aborts the currently executing transaction, if any.
10411 abort : function(){
10412 if(this.transaction){
10413 Ext.Ajax.abort(this.transaction);
10418 * Returns true if an update is in progress, otherwise false.
10419 * @return {Boolean}
10421 isUpdating : function(){
10422 return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
10426 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
10427 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10429 refresh : function(callback){
10430 if(this.defaultUrl){
10431 this.update(this.defaultUrl, null, callback, true);
10438 * @class Ext.Updater.defaults
10439 * The defaults collection enables customizing the default properties of Updater
10441 Ext.Updater.defaults = {
10443 * Timeout for requests or form posts in seconds (defaults to 30 seconds).
10448 * True to append a unique parameter to GET requests to disable caching (defaults to false).
10451 disableCaching : false,
10453 * Whether or not to show {@link #indicatorText} during loading (defaults to true).
10456 showLoadIndicator : true,
10458 * Text for loading indicator (defaults to '<div class="loading-indicator">Loading...</div>').
10461 indicatorText : '<div class="loading-indicator">Loading...</div>',
10463 * True to process scripts by default (defaults to false).
10466 loadScripts : false,
10468 * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
10471 sslBlankUrl : Ext.SSL_SECURE_URL
10476 * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
10478 * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
10479 * @param {Mixed} el The element to update
10480 * @param {String} url The url
10481 * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
10482 * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
10483 * example: {disableCaching:true, indicatorText: "Loading data..."}
10486 * @member Ext.Updater
10488 Ext.Updater.updateElement = function(el, url, params, options){
10489 var um = Ext.get(el).getUpdater();
10490 Ext.apply(um, options);
10491 um.update(url, params, options ? options.callback : null);
10495 * @class Ext.Updater.BasicRenderer
10496 * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
10497 * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
10498 * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
10500 Ext.Updater.BasicRenderer = function(){};
10502 Ext.Updater.BasicRenderer.prototype = {
10504 * This method is called when an Ajax response is received, and an Element needs updating.
10505 * @param {Ext.Element} el The element being rendered
10506 * @param {Object} xhr The XMLHttpRequest object
10507 * @param {Updater} updateManager The calling update manager
10508 * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
10510 render : function(el, response, updateManager, callback){
10511 el.update(response.responseText, updateManager.loadScripts, callback);
10516 * The date parsing and formatting syntax contains a subset of
10517 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
10518 * supported will provide results equivalent to their PHP versions.
10520 * The following is a list of all currently supported formats:
10522 Format Description Example returned values
10523 ------ ----------------------------------------------------------------------- -----------------------
10524 d Day of the month, 2 digits with leading zeros 01 to 31
10525 D A short textual representation of the day of the week Mon to Sun
10526 j Day of the month without leading zeros 1 to 31
10527 l A full textual representation of the day of the week Sunday to Saturday
10528 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
10529 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
10530 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
10531 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
10532 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
10533 F A full textual representation of a month, such as January or March January to December
10534 m Numeric representation of a month, with leading zeros 01 to 12
10535 M A short textual representation of a month Jan to Dec
10536 n Numeric representation of a month, without leading zeros 1 to 12
10537 t Number of days in the given month 28 to 31
10538 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
10539 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
10540 belongs to the previous or next year, that year is used instead)
10541 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
10542 y A two digit representation of a year Examples: 99 or 03
10543 a Lowercase Ante meridiem and Post meridiem am or pm
10544 A Uppercase Ante meridiem and Post meridiem AM or PM
10545 g 12-hour format of an hour without leading zeros 1 to 12
10546 G 24-hour format of an hour without leading zeros 0 to 23
10547 h 12-hour format of an hour with leading zeros 01 to 12
10548 H 24-hour format of an hour with leading zeros 00 to 23
10549 i Minutes, with leading zeros 00 to 59
10550 s Seconds, with leading zeros 00 to 59
10551 u Decimal fraction of a second Examples:
10552 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
10553 100 (i.e. 0.100s) or
10554 999 (i.e. 0.999s) or
10555 999876543210 (i.e. 0.999876543210s)
10556 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
10557 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
10558 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
10559 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
10562 1) If unspecified, the month / day defaults to the current month / day, 1991 or
10563 the time defaults to midnight, while the timezone defaults to the 1992-10 or
10564 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
10565 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
10566 are optional. 1995-07-18T17:21:28-02:00 or
10567 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
10568 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
10569 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
10570 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
10571 date-time granularity which are supported, or see 2000-02-13T21:25:33
10572 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
10573 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
10574 M$ Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
10575 \/Date(1238606590509+0800)\/
10578 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
10581 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
10583 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
10584 document.write(dt.format('Y-m-d')); // 2007-01-10
10585 document.write(dt.format('F j, Y, g:i a')); // January 10, 2007, 3:05 pm
10586 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
10589 * Here are some standard date/time patterns that you might find helpful. They
10590 * are not part of the source of Date.js, but to use them you can simply copy this
10591 * block of code into any script that is included after Date.js and they will also become
10592 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
10595 ISO8601Long:"Y-m-d H:i:s",
10596 ISO8601Short:"Y-m-d",
10597 ShortDate: "n/j/Y",
10598 LongDate: "l, F d, Y",
10599 FullDateTime: "l, F d, Y g:i:s A",
10601 ShortTime: "g:i A",
10602 LongTime: "g:i:s A",
10603 SortableDateTime: "Y-m-d\\TH:i:s",
10604 UniversalSortableDateTime: "Y-m-d H:i:sO",
10611 var dt = new Date();
10612 document.write(dt.format(Date.patterns.ShortDate));
10614 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
10615 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
10619 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
10620 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
10621 * They generate precompiled functions from format patterns instead of parsing and
10622 * processing each pattern every time a date is formatted. These functions are available
10623 * on every Date object.
10629 * Global flag which determines if strict date parsing should be used.
10630 * Strict date parsing will not roll-over invalid dates, which is the
10631 * default behaviour of javascript Date objects.
10632 * (see {@link #parseDate} for more information)
10633 * Defaults to <tt>false</tt>.
10637 Date.useStrict = false;
10640 // create private copy of Ext's String.format() method
10641 // - to remove unnecessary dependency
10642 // - to resolve namespace conflict with M$-Ajax's implementation
10643 function xf(format) {
10644 var args = Array.prototype.slice.call(arguments, 1);
10645 return format.replace(/\{(\d+)\}/g, function(m, i) {
10652 Date.formatCodeToRegex = function(character, currentGroup) {
10653 // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
10654 var p = Date.parseCodes[character];
10657 p = typeof p == 'function'? p() : p;
10658 Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
10661 return p ? Ext.applyIf({
10662 c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
10666 s:Ext.escapeRe(character) // treat unrecognised characters as literals
10670 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
10671 var $f = Date.formatCodeToRegex;
10675 * <p>An object hash in which each property is a date parsing function. The property name is the
10676 * format string which that function parses.</p>
10677 * <p>This object is automatically populated with date parsing functions as
10678 * date formats are requested for Ext standard formatting strings.</p>
10679 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
10680 * may be used as a format string to {@link #parseDate}.<p>
10681 * <p>Example:</p><pre><code>
10682 Date.parseFunctions['x-date-format'] = myDateParser;
10684 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10685 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
10686 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
10687 * (i.e. prevent javascript Date "rollover") (The default must be false).
10688 * Invalid date strings should return null when parsed.</div></li>
10690 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
10691 * formatting function must be placed into the {@link #formatFunctions} property.
10692 * @property parseFunctions
10697 "M$": function(input, strict) {
10698 // note: the timezone offset is ignored since the M$ Ajax server sends
10699 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
10700 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
10701 var r = (input || '').match(re);
10702 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
10708 * <p>An object hash in which each property is a date formatting function. The property name is the
10709 * format string which corresponds to the produced formatted date string.</p>
10710 * <p>This object is automatically populated with date formatting functions as
10711 * date formats are requested for Ext standard formatting strings.</p>
10712 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
10713 * may be used as a format string to {@link #format}. Example:</p><pre><code>
10714 Date.formatFunctions['x-date-format'] = myDateFormatter;
10716 * <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>
10717 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
10719 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
10720 * parsing function must be placed into the {@link #parseFunctions} property.
10721 * @property formatFunctions
10727 // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
10728 return '\\/Date(' + this.getTime() + ')\\/';
10735 * Date interval constant
10742 * Date interval constant
10749 * Date interval constant
10755 /** Date interval constant
10762 * Date interval constant
10769 * Date interval constant
10776 * Date interval constant
10783 * <p>An object hash containing default date values used during date parsing.</p>
10784 * <p>The following properties are available:<div class="mdetail-params"><ul>
10785 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
10786 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
10787 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
10788 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
10789 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
10790 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
10791 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
10793 * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
10794 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
10795 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
10796 * It is the responsiblity of the developer to account for this.</b></p>
10799 // set default day value to the first day of the month
10800 Date.defaults.d = 1;
10802 // parse a February date string containing only year and month values.
10803 // setting the default day value to 1 prevents weird date rollover issues
10804 // when attempting to parse the following date string on, for example, March 31st 2009.
10805 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
10807 * @property defaults
10814 * An array of textual day names.
10815 * Override these values for international dates.
10819 'SundayInYourLang',
10820 'MondayInYourLang',
10838 * An array of textual month names.
10839 * Override these values for international dates.
10842 Date.monthNames = [
10867 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
10868 * Override these values for international dates.
10871 Date.monthNumbers = {
10872 'ShortJanNameInYourLang':0,
10873 'ShortFebNameInYourLang':1,
10896 * Get the short month name for the given month number.
10897 * Override this function for international dates.
10898 * @param {Number} month A zero-based javascript month number.
10899 * @return {String} The short month name.
10902 getShortMonthName : function(month) {
10903 return Date.monthNames[month].substring(0, 3);
10907 * Get the short day name for the given day number.
10908 * Override this function for international dates.
10909 * @param {Number} day A zero-based javascript day number.
10910 * @return {String} The short day name.
10913 getShortDayName : function(day) {
10914 return Date.dayNames[day].substring(0, 3);
10918 * Get the zero-based javascript month number for the given short/full month name.
10919 * Override this function for international dates.
10920 * @param {String} name The short/full month name.
10921 * @return {Number} The zero-based javascript month number.
10924 getMonthNumber : function(name) {
10925 // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
10926 return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
10930 * The base format-code to formatting-function hashmap used by the {@link #format} method.
10931 * Formatting functions are strings (or functions which return strings) which
10932 * will return the appropriate value when evaluated in the context of the Date object
10933 * from which the {@link #format} method is called.
10934 * Add to / override these mappings for custom date formatting.
10935 * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
10938 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
10939 (new Date()).format("X"); // returns the current day of the month
10945 d: "String.leftPad(this.getDate(), 2, '0')",
10946 D: "Date.getShortDayName(this.getDay())", // get localised short day name
10947 j: "this.getDate()",
10948 l: "Date.dayNames[this.getDay()]",
10949 N: "(this.getDay() ? this.getDay() : 7)",
10950 S: "this.getSuffix()",
10951 w: "this.getDay()",
10952 z: "this.getDayOfYear()",
10953 W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10954 F: "Date.monthNames[this.getMonth()]",
10955 m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10956 M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10957 n: "(this.getMonth() + 1)",
10958 t: "this.getDaysInMonth()",
10959 L: "(this.isLeapYear() ? 1 : 0)",
10960 o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10961 Y: "this.getFullYear()",
10962 y: "('' + this.getFullYear()).substring(2, 4)",
10963 a: "(this.getHours() < 12 ? 'am' : 'pm')",
10964 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10965 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10966 G: "this.getHours()",
10967 h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10968 H: "String.leftPad(this.getHours(), 2, '0')",
10969 i: "String.leftPad(this.getMinutes(), 2, '0')",
10970 s: "String.leftPad(this.getSeconds(), 2, '0')",
10971 u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10972 O: "this.getGMTOffset()",
10973 P: "this.getGMTOffset(true)",
10974 T: "this.getTimezone()",
10975 Z: "(this.getTimezoneOffset() * -60)",
10977 c: function() { // ISO-8601 -- GMT format
10978 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10979 var e = c.charAt(i);
10980 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10982 return code.join(" + ");
10985 c: function() { // ISO-8601 -- UTC format
10987 "this.getUTCFullYear()", "'-'",
10988 "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10989 "String.leftPad(this.getUTCDate(), 2, '0')",
10991 "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10992 "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10993 "String.leftPad(this.getUTCSeconds(), 2, '0')",
10999 U: "Math.round(this.getTime() / 1000)"
11003 * Checks if the passed Date parameters will cause a javascript Date "rollover".
11004 * @param {Number} year 4-digit year
11005 * @param {Number} month 1-based month-of-year
11006 * @param {Number} day Day of month
11007 * @param {Number} hour (optional) Hour
11008 * @param {Number} minute (optional) Minute
11009 * @param {Number} second (optional) Second
11010 * @param {Number} millisecond (optional) Millisecond
11011 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
11014 isValid : function(y, m, d, h, i, s, ms) {
11021 var dt = new Date(y, m - 1, d, h, i, s, ms);
11023 return y == dt.getFullYear() &&
11024 m == dt.getMonth() + 1 &&
11025 d == dt.getDate() &&
11026 h == dt.getHours() &&
11027 i == dt.getMinutes() &&
11028 s == dt.getSeconds() &&
11029 ms == dt.getMilliseconds();
11033 * Parses the passed string using the specified date format.
11034 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
11035 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
11036 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
11037 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
11038 * Keep in mind that the input date string must precisely match the specified format string
11039 * in order for the parse operation to be successful (failed parse operations return a null value).
11040 * <p>Example:</p><pre><code>
11041 //dt = Fri May 25 2007 (current date)
11042 var dt = new Date();
11044 //dt = Thu May 25 2006 (today's month/day in 2006)
11045 dt = Date.parseDate("2006", "Y");
11047 //dt = Sun Jan 15 2006 (all date parts specified)
11048 dt = Date.parseDate("2006-01-15", "Y-m-d");
11050 //dt = Sun Jan 15 2006 15:20:01
11051 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
11053 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
11054 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
11056 * @param {String} input The raw date string.
11057 * @param {String} format The expected date string format.
11058 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
11059 (defaults to false). Invalid date strings will return null when parsed.
11060 * @return {Date} The parsed Date.
11063 parseDate : function(input, format, strict) {
11064 var p = Date.parseFunctions;
11065 if (p[format] == null) {
11066 Date.createParser(format);
11068 return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
11072 getFormatCode : function(character) {
11073 var f = Date.formatCodes[character];
11076 f = typeof f == 'function'? f() : f;
11077 Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
11080 // note: unknown characters are treated as literals
11081 return f || ("'" + String.escape(character) + "'");
11085 createFormat : function(format) {
11090 for (var i = 0; i < format.length; ++i) {
11091 ch = format.charAt(i);
11092 if (!special && ch == "\\") {
11094 } else if (special) {
11096 code.push("'" + String.escape(ch) + "'");
11098 code.push(Date.getFormatCode(ch))
11101 Date.formatFunctions[format] = new Function("return " + code.join('+'));
11105 createParser : function() {
11107 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
11108 "def = Date.defaults,",
11109 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
11114 "if(u != null){", // i.e. unix time is defined
11115 "v = new Date(u * 1000);", // give top priority to UNIX time
11117 // create Date object representing midnight of the current day;
11118 // this will provide us with our date defaults
11119 // (note: clearTime() handles Daylight Saving Time automatically)
11120 "dt = (new Date()).clearTime();",
11122 // date calculations (note: these calculations create a dependency on Ext.num())
11123 "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
11124 "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
11125 "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
11127 // time calculations (note: these calculations create a dependency on Ext.num())
11128 "h = Ext.num(h, Ext.num(def.h, dt.getHours()));",
11129 "i = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
11130 "s = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
11131 "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
11133 "if(z >= 0 && y >= 0){",
11134 // both the year and zero-based day of year are defined and >= 0.
11135 // these 2 values alone provide sufficient info to create a full date object
11137 // create Date object representing January 1st for the given year
11138 "v = new Date(y, 0, 1, h, i, s, ms);",
11140 // then add day of year, checking for Date "rollover" if necessary
11141 "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
11142 "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
11143 "v = null;", // invalid date, so return null
11145 // plain old Date object
11146 "v = new Date(y, m, d, h, i, s, ms);",
11152 // favour UTC offset over GMT offset
11154 // reset to UTC, then add offset
11155 "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
11157 // reset to GMT, then add offset
11158 "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
11165 return function(format) {
11166 var regexNum = Date.parseRegexes.length,
11173 for (var i = 0; i < format.length; ++i) {
11174 ch = format.charAt(i);
11175 if (!special && ch == "\\") {
11177 } else if (special) {
11179 regex.push(String.escape(ch));
11181 var obj = $f(ch, currentGroup);
11182 currentGroup += obj.g;
11184 if (obj.g && obj.c) {
11190 Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$");
11191 Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
11199 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
11200 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
11201 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
11205 c:"d = parseInt(results[{0}], 10);\n",
11206 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
11210 c:"d = parseInt(results[{0}], 10);\n",
11211 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
11214 for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
11218 s:"(?:" + a.join("|") +")"
11225 s:"(?:" + Date.dayNames.join("|") + ")"
11231 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
11236 s:"(?:st|nd|rd|th)"
11241 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
11245 c:"z = parseInt(results[{0}], 10);\n",
11246 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
11251 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
11256 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
11257 s:"(" + Date.monthNames.join("|") + ")"
11261 for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
11262 return Ext.applyIf({
11263 s:"(" + a.join("|") + ")"
11268 c:"m = parseInt(results[{0}], 10) - 1;\n",
11269 s:"(\\d{2})" // month number with leading zeros (01 - 12)
11273 c:"m = parseInt(results[{0}], 10) - 1;\n",
11274 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
11279 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
11291 c:"y = parseInt(results[{0}], 10);\n",
11292 s:"(\\d{4})" // 4-digit year
11296 c:"var ty = parseInt(results[{0}], 10);\n"
11297 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
11302 c:"if (results[{0}] == 'am') {\n"
11303 + "if (!h || h == 12) { h = 0; }\n"
11304 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11309 c:"if (results[{0}] == 'AM') {\n"
11310 + "if (!h || h == 12) { h = 0; }\n"
11311 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11319 c:"h = parseInt(results[{0}], 10);\n",
11320 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
11327 c:"h = parseInt(results[{0}], 10);\n",
11328 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
11332 c:"i = parseInt(results[{0}], 10);\n",
11333 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
11337 c:"s = parseInt(results[{0}], 10);\n",
11338 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
11342 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
11343 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11348 "o = results[{0}];",
11349 "var sn = o.substring(0,1),", // get + / - sign
11350 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11351 "mn = o.substring(3,5) % 60;", // get minutes
11352 "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
11354 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
11359 "o = results[{0}];",
11360 "var sn = o.substring(0,1),", // get + / - sign
11361 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11362 "mn = o.substring(4,6) % 60;", // get minutes
11363 "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
11365 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
11370 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
11374 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
11375 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
11376 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
11381 $f("Y", 1), // year
11382 $f("m", 2), // month
11384 $f("h", 4), // hour
11385 $f("i", 5), // minute
11386 $f("s", 6), // second
11387 {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)
11388 {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
11389 "if(results[8]) {", // timezone specified
11390 "if(results[8] == 'Z'){",
11392 "}else if (results[8].indexOf(':') > -1){",
11393 $f("P", 8).c, // timezone offset with colon separator
11395 $f("O", 8).c, // timezone offset without colon separator
11401 for (var i = 0, l = arr.length; i < l; ++i) {
11402 calc.push(arr[i].c);
11409 arr[0].s, // year (required)
11410 "(?:", "-", arr[1].s, // month (optional)
11411 "(?:", "-", arr[2].s, // day (optional)
11413 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
11414 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
11415 "(?::", arr[5].s, ")?", // seconds (optional)
11416 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
11417 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
11426 c:"u = parseInt(results[{0}], 10);\n",
11427 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
11434 Ext.apply(Date.prototype, {
11436 dateFormat : function(format) {
11437 if (Date.formatFunctions[format] == null) {
11438 Date.createFormat(format);
11440 return Date.formatFunctions[format].call(this);
11444 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
11446 * Note: The date string returned by the javascript Date object's toString() method varies
11447 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
11448 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
11449 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
11450 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
11451 * from the GMT offset portion of the date string.
11452 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
11454 getTimezone : function() {
11455 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
11457 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
11458 // 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)
11459 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
11460 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
11461 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
11463 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
11464 // step 1: (?:\((.*)\) -- find timezone in parentheses
11465 // 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
11466 // step 3: remove all non uppercase characters found in step 1 and 2
11467 return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
11471 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
11472 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
11473 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
11475 getGMTOffset : function(colon) {
11476 return (this.getTimezoneOffset() > 0 ? "-" : "+")
11477 + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
11478 + (colon ? ":" : "")
11479 + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
11483 * Get the numeric day number of the year, adjusted for leap year.
11484 * @return {Number} 0 to 364 (365 in leap years).
11486 getDayOfYear: function() {
11489 m = this.getMonth(),
11492 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
11493 num += d.getDaysInMonth();
11495 return num + this.getDate() - 1;
11499 * Get the numeric ISO-8601 week number of the year.
11500 * (equivalent to the format specifier 'W', but without a leading zero).
11501 * @return {Number} 1 to 53
11503 getWeekOfYear : function() {
11504 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
11505 var ms1d = 864e5, // milliseconds in a day
11506 ms7d = 7 * ms1d; // milliseconds in a week
11508 return function() { // return a closure so constants get calculated only once
11509 var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
11510 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
11511 Wyr = new Date(AWN * ms7d).getUTCFullYear();
11513 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
11518 * Checks if the current date falls within a leap year.
11519 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
11521 isLeapYear : function() {
11522 var year = this.getFullYear();
11523 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
11527 * Get the first day of the current month, adjusted for leap year. The returned value
11528 * is the numeric day index within the week (0-6) which can be used in conjunction with
11529 * the {@link #monthNames} array to retrieve the textual day name.
11532 var dt = new Date('1/10/2007');
11533 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
11535 * @return {Number} The day number (0-6).
11537 getFirstDayOfMonth : function() {
11538 var day = (this.getDay() - (this.getDate() - 1)) % 7;
11539 return (day < 0) ? (day + 7) : day;
11543 * Get the last day of the current month, adjusted for leap year. The returned value
11544 * is the numeric day index within the week (0-6) which can be used in conjunction with
11545 * the {@link #monthNames} array to retrieve the textual day name.
11548 var dt = new Date('1/10/2007');
11549 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
11551 * @return {Number} The day number (0-6).
11553 getLastDayOfMonth : function() {
11554 return this.getLastDateOfMonth().getDay();
11559 * Get the date of the first day of the month in which this date resides.
11562 getFirstDateOfMonth : function() {
11563 return new Date(this.getFullYear(), this.getMonth(), 1);
11567 * Get the date of the last day of the month in which this date resides.
11570 getLastDateOfMonth : function() {
11571 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
11575 * Get the number of days in the current month, adjusted for leap year.
11576 * @return {Number} The number of days in the month.
11578 getDaysInMonth: function() {
11579 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
11581 return function() { // return a closure for efficiency
11582 var m = this.getMonth();
11584 return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
11589 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
11590 * @return {String} 'st, 'nd', 'rd' or 'th'.
11592 getSuffix : function() {
11593 switch (this.getDate()) {
11610 * Creates and returns a new Date instance with the exact same date value as the called instance.
11611 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
11612 * variable will also be changed. When the intention is to create a new variable that will not
11613 * modify the original instance, you should create a clone.
11615 * Example of correctly cloning a date:
11618 var orig = new Date('10/1/2006');
11621 document.write(orig); //returns 'Thu Oct 05 2006'!
11624 var orig = new Date('10/1/2006');
11625 var copy = orig.clone();
11627 document.write(orig); //returns 'Thu Oct 01 2006'
11629 * @return {Date} The new Date instance.
11631 clone : function() {
11632 return new Date(this.getTime());
11636 * Checks if the current date is affected by Daylight Saving Time (DST).
11637 * @return {Boolean} True if the current date is affected by DST.
11639 isDST : function() {
11640 // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
11641 // courtesy of @geoffrey.mcgill
11642 return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
11646 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
11647 * automatically adjusting for Daylight Saving Time (DST) where applicable.
11648 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
11649 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
11650 * @return {Date} this or the clone.
11652 clearTime : function(clone) {
11654 return this.clone().clearTime();
11657 // get current date before clearing time
11658 var d = this.getDate();
11662 this.setMinutes(0);
11663 this.setSeconds(0);
11664 this.setMilliseconds(0);
11666 if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
11667 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
11668 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
11670 // increment hour until cloned date == current date
11671 for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
11674 this.setHours(c.getHours());
11681 * Provides a convenient method for performing basic date arithmetic. This method
11682 * does not modify the Date instance being called - it creates and returns
11683 * a new Date instance containing the resulting date value.
11688 var dt = new Date('10/29/2006').add(Date.DAY, 5);
11689 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
11691 // Negative values will be subtracted:
11692 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
11693 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
11695 // You can even chain several calls together in one line:
11696 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
11697 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
11700 * @param {String} interval A valid date interval enum value.
11701 * @param {Number} value The amount to add to the current date.
11702 * @return {Date} The new Date instance.
11704 add : function(interval, value) {
11705 var d = this.clone();
11706 if (!interval || value === 0) return d;
11708 switch(interval.toLowerCase()) {
11710 d.setMilliseconds(this.getMilliseconds() + value);
11713 d.setSeconds(this.getSeconds() + value);
11716 d.setMinutes(this.getMinutes() + value);
11719 d.setHours(this.getHours() + value);
11722 d.setDate(this.getDate() + value);
11725 var day = this.getDate();
11727 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
11730 d.setMonth(this.getMonth() + value);
11733 d.setFullYear(this.getFullYear() + value);
11740 * Checks if this date falls on or between the given start and end dates.
11741 * @param {Date} start Start date
11742 * @param {Date} end End date
11743 * @return {Boolean} true if this date falls on or between the given start and end dates.
11745 between : function(start, end) {
11746 var t = this.getTime();
11747 return start.getTime() <= t && t <= end.getTime();
11753 * Formats a date given the supplied format string.
11754 * @param {String} format The format string.
11755 * @return {String} The formatted date.
11758 Date.prototype.format = Date.prototype.dateFormat;
11762 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
11763 Ext.apply(Date.prototype, {
11764 _xMonth : Date.prototype.setMonth,
11765 _xDate : Date.prototype.setDate,
11767 // Bug in Safari 1.3, 2.0 (WebKit build < 420)
11768 // Date.setMonth does not work consistently if iMonth is not 0-11
11769 setMonth : function(num) {
11771 var n = Math.ceil(-num),
11772 back_year = Math.ceil(n / 12),
11773 month = (n % 12) ? 12 - n % 12 : 0;
11775 this.setFullYear(this.getFullYear() - back_year);
11777 return this._xMonth(month);
11779 return this._xMonth(num);
11783 // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
11784 // The parameter for Date.setDate() is converted to a signed byte integer in Safari
11785 // http://brianary.blogspot.com/2006/03/safari-date-bug.html
11786 setDate : function(d) {
11787 // use setTime() to workaround setDate() bug
11788 // subtract current day of month in milliseconds, then add desired day of month in milliseconds
11789 return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
11796 /* Some basic Date tests... (requires Firebug)
11798 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
11799 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
11802 console.group('Standard Date.parseDate() Tests');
11803 console.log('Date.parseDate("2009-01-05T11:38:56", "c") = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
11804 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
11805 console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c") = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
11806 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
11807 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
11808 console.groupEnd();
11810 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
11811 // -- accepts ALL 6 levels of date-time granularity
11812 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
11813 console.log('Date.parseDate("1997", "c") = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
11814 console.log('Date.parseDate("1997-07", "c") = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
11815 console.log('Date.parseDate("1997-07-16", "c") = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
11816 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)
11817 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)
11818 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)
11819 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)
11820 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
11821 console.groupEnd();
11825 * @class Ext.util.MixedCollection
11826 * @extends Ext.util.Observable
11827 * A Collection class that maintains both numeric indexes and keys and exposes events.
11829 * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11830 * function should add function references to the collection. Defaults to
11832 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
11833 * and return the key value for that item. This is used when available to look up the key on items that
11834 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
11835 * equivalent to providing an implementation for the {@link #getKey} method.
11837 Ext.util.MixedCollection = function(allowFunctions, keyFn){
11845 * Fires when the collection is cleared.
11850 * Fires when an item is added to the collection.
11851 * @param {Number} index The index at which the item was added.
11852 * @param {Object} o The item added.
11853 * @param {String} key The key associated with the added item.
11858 * Fires when an item is replaced in the collection.
11859 * @param {String} key he key associated with the new added.
11860 * @param {Object} old The item being replaced.
11861 * @param {Object} new The new item.
11866 * Fires when an item is removed from the collection.
11867 * @param {Object} o The item being removed.
11868 * @param {String} key (optional) The key associated with the removed item.
11873 this.allowFunctions = allowFunctions === true;
11875 this.getKey = keyFn;
11877 Ext.util.MixedCollection.superclass.constructor.call(this);
11880 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
11883 * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11884 * function should add function references to the collection. Defaults to
11887 allowFunctions : false,
11890 * Adds an item to the collection. Fires the {@link #add} event when complete.
11891 * @param {String} key <p>The key to associate with the item, or the new item.</p>
11892 * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
11893 * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
11894 * the MixedCollection will be able to <i>derive</i> the key for the new item.
11895 * In this case just pass the new item in this parameter.</p>
11896 * @param {Object} o The item to add.
11897 * @return {Object} The item added.
11899 add : function(key, o){
11900 if(arguments.length == 1){
11902 key = this.getKey(o);
11904 if(typeof key != 'undefined' && key !== null){
11905 var old = this.map[key];
11906 if(typeof old != 'undefined'){
11907 return this.replace(key, o);
11912 this.items.push(o);
11913 this.keys.push(key);
11914 this.fireEvent('add', this.length-1, o, key);
11919 * MixedCollection has a generic way to fetch keys if you implement getKey. The default implementation
11920 * simply returns <b><code>item.id</code></b> but you can provide your own implementation
11921 * to return a different value as in the following examples:<pre><code>
11923 var mc = new Ext.util.MixedCollection();
11924 mc.add(someEl.dom.id, someEl);
11925 mc.add(otherEl.dom.id, otherEl);
11929 var mc = new Ext.util.MixedCollection();
11930 mc.getKey = function(el){
11936 // or via the constructor
11937 var mc = new Ext.util.MixedCollection(false, function(el){
11943 * @param {Object} item The item for which to find the key.
11944 * @return {Object} The key for the passed item.
11946 getKey : function(o){
11951 * Replaces an item in the collection. Fires the {@link #replace} event when complete.
11952 * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
11953 * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
11954 * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
11955 * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
11956 * with one having the same key value, then just pass the replacement item in this parameter.</p>
11957 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
11959 * @return {Object} The new item.
11961 replace : function(key, o){
11962 if(arguments.length == 1){
11964 key = this.getKey(o);
11966 var old = this.map[key];
11967 if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
11968 return this.add(key, o);
11970 var index = this.indexOfKey(key);
11971 this.items[index] = o;
11973 this.fireEvent('replace', key, old, o);
11978 * Adds all elements of an Array or an Object to the collection.
11979 * @param {Object/Array} objs An Object containing properties which will be added
11980 * to the collection, or an Array of values, each of which are added to the collection.
11981 * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
11982 * has been set to <tt>true</tt>.
11984 addAll : function(objs){
11985 if(arguments.length > 1 || Ext.isArray(objs)){
11986 var args = arguments.length > 1 ? arguments : objs;
11987 for(var i = 0, len = args.length; i < len; i++){
11991 for(var key in objs){
11992 if(this.allowFunctions || typeof objs[key] != 'function'){
11993 this.add(key, objs[key]);
12000 * Executes the specified function once for every item in the collection, passing the following arguments:
12001 * <div class="mdetail-params"><ul>
12002 * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
12003 * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
12004 * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
12006 * The function should return a boolean value. Returning false from the function will stop the iteration.
12007 * @param {Function} fn The function to execute for each item.
12008 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
12010 each : function(fn, scope){
12011 var items = [].concat(this.items); // each safe for removal
12012 for(var i = 0, len = items.length; i < len; i++){
12013 if(fn.call(scope || items[i], items[i], i, len) === false){
12020 * Executes the specified function once for every key in the collection, passing each
12021 * key, and its associated item as the first two parameters.
12022 * @param {Function} fn The 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.
12025 eachKey : function(fn, scope){
12026 for(var i = 0, len = this.keys.length; i < len; i++){
12027 fn.call(scope || window, this.keys[i], this.items[i], i, len);
12032 * Returns the first item in the collection which elicits a true return value from the
12033 * passed selection function.
12034 * @param {Function} fn The selection function to execute for each item.
12035 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
12036 * @return {Object} The first item in the collection which returned true from the selection function.
12038 find : function(fn, scope){
12039 for(var i = 0, len = this.items.length; i < len; i++){
12040 if(fn.call(scope || window, this.items[i], this.keys[i])){
12041 return this.items[i];
12048 * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
12049 * @param {Number} index The index to insert the item at.
12050 * @param {String} key The key to associate with the new item, or the item itself.
12051 * @param {Object} o (optional) If the second parameter was a key, the new item.
12052 * @return {Object} The item inserted.
12054 insert : function(index, key, o){
12055 if(arguments.length == 2){
12057 key = this.getKey(o);
12059 if(this.containsKey(key)){
12060 this.suspendEvents();
12061 this.removeKey(key);
12062 this.resumeEvents();
12064 if(index >= this.length){
12065 return this.add(key, o);
12068 this.items.splice(index, 0, o);
12069 if(typeof key != 'undefined' && key !== null){
12072 this.keys.splice(index, 0, key);
12073 this.fireEvent('add', index, o, key);
12078 * Remove an item from the collection.
12079 * @param {Object} o The item to remove.
12080 * @return {Object} The item removed or false if no item was removed.
12082 remove : function(o){
12083 return this.removeAt(this.indexOf(o));
12087 * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
12088 * @param {Number} index The index within the collection of the item to remove.
12089 * @return {Object} The item removed or false if no item was removed.
12091 removeAt : function(index){
12092 if(index < this.length && index >= 0){
12094 var o = this.items[index];
12095 this.items.splice(index, 1);
12096 var key = this.keys[index];
12097 if(typeof key != 'undefined'){
12098 delete this.map[key];
12100 this.keys.splice(index, 1);
12101 this.fireEvent('remove', o, key);
12108 * Removed an item associated with the passed key fom the collection.
12109 * @param {String} key The key of the item to remove.
12110 * @return {Object} The item removed or false if no item was removed.
12112 removeKey : function(key){
12113 return this.removeAt(this.indexOfKey(key));
12117 * Returns the number of items in the collection.
12118 * @return {Number} the number of items in the collection.
12120 getCount : function(){
12121 return this.length;
12125 * Returns index within the collection of the passed Object.
12126 * @param {Object} o The item to find the index of.
12127 * @return {Number} index of the item. Returns -1 if not found.
12129 indexOf : function(o){
12130 return this.items.indexOf(o);
12134 * Returns index within the collection of the passed key.
12135 * @param {String} key The key to find the index of.
12136 * @return {Number} index of the key.
12138 indexOfKey : function(key){
12139 return this.keys.indexOf(key);
12143 * Returns the item associated with the passed key OR index.
12144 * Key has priority over index. This is the equivalent
12145 * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
12146 * @param {String/Number} key The key or index of the item.
12147 * @return {Object} If the item is found, returns the item. If the item was not found, returns <tt>undefined</tt>.
12148 * If an item was found, but is a Class, returns <tt>null</tt>.
12150 item : function(key){
12151 var mk = this.map[key],
12152 item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
12153 return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12157 * Returns the item at the specified index.
12158 * @param {Number} index The index of the item.
12159 * @return {Object} The item at the specified index.
12161 itemAt : function(index){
12162 return this.items[index];
12166 * Returns the item associated with the passed key.
12167 * @param {String/Number} key The key of the item.
12168 * @return {Object} The item associated with the passed key.
12170 key : function(key){
12171 return this.map[key];
12175 * Returns true if the collection contains the passed Object as an item.
12176 * @param {Object} o The Object to look for in the collection.
12177 * @return {Boolean} True if the collection contains the Object as an item.
12179 contains : function(o){
12180 return this.indexOf(o) != -1;
12184 * Returns true if the collection contains the passed Object as a key.
12185 * @param {String} key The key to look for in the collection.
12186 * @return {Boolean} True if the collection contains the Object as a key.
12188 containsKey : function(key){
12189 return typeof this.map[key] != 'undefined';
12193 * Removes all items from the collection. Fires the {@link #clear} event when complete.
12195 clear : function(){
12200 this.fireEvent('clear');
12204 * Returns the first item in the collection.
12205 * @return {Object} the first item in the collection..
12207 first : function(){
12208 return this.items[0];
12212 * Returns the last item in the collection.
12213 * @return {Object} the last item in the collection..
12216 return this.items[this.length-1];
12221 * Performs the actual sorting based on a direction and a sorting function. Internally,
12222 * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
12223 * the sorted array data back into this.items and this.keys
12224 * @param {String} property Property to sort by ('key', 'value', or 'index')
12225 * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
12226 * @param {Function} fn (optional) Comparison function that defines the sort order.
12227 * Defaults to sorting by numeric value.
12229 _sort : function(property, dir, fn){
12231 dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
12233 //this is a temporary array used to apply the sorting function
12236 items = this.items;
12238 //default to a simple sorter function if one is not provided
12239 fn = fn || function(a, b) {
12243 //copy all the items into a temporary array, which we will sort
12244 for(i = 0, len = items.length; i < len; i++){
12252 //sort the temporary array
12253 c.sort(function(a, b){
12254 var v = fn(a[property], b[property]) * dsc;
12256 v = (a.index < b.index ? -1 : 1);
12261 //copy the temporary array back into the main this.items and this.keys objects
12262 for(i = 0, len = c.length; i < len; i++){
12263 items[i] = c[i].value;
12264 keys[i] = c[i].key;
12267 this.fireEvent('sort', this);
12271 * Sorts this collection by <b>item</b> value with the passed comparison function.
12272 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12273 * @param {Function} fn (optional) Comparison function that defines the sort order.
12274 * Defaults to sorting by numeric value.
12276 sort : function(dir, fn){
12277 this._sort('value', dir, fn);
12281 * Reorders each of the items based on a mapping from old index to new index. Internally this
12282 * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
12283 * @param {Object} mapping Mapping from old item index to new item index
12285 reorder: function(mapping) {
12286 this.suspendEvents();
12288 var items = this.items,
12290 length = items.length,
12295 //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
12296 for (oldIndex in mapping) {
12297 order[mapping[oldIndex]] = items[oldIndex];
12300 for (index = 0; index < length; index++) {
12301 if (mapping[index] == undefined) {
12302 remaining.push(items[index]);
12306 for (index = 0; index < length; index++) {
12307 if (order[index] == undefined) {
12308 order[index] = remaining.shift();
12313 this.addAll(order);
12315 this.resumeEvents();
12316 this.fireEvent('sort', this);
12320 * Sorts this collection by <b>key</b>s.
12321 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12322 * @param {Function} fn (optional) Comparison function that defines the sort order.
12323 * Defaults to sorting by case insensitive string.
12325 keySort : function(dir, fn){
12326 this._sort('key', dir, fn || function(a, b){
12327 var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
12328 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12333 * Returns a range of items in this collection
12334 * @param {Number} startIndex (optional) The starting index. Defaults to 0.
12335 * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
12336 * @return {Array} An array of items
12338 getRange : function(start, end){
12339 var items = this.items;
12340 if(items.length < 1){
12343 start = start || 0;
12344 end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
12347 for(i = start; i <= end; i++) {
12348 r[r.length] = items[i];
12351 for(i = start; i >= end; i--) {
12352 r[r.length] = items[i];
12359 * Filter the <i>objects</i> in this collection by a specific property.
12360 * Returns a new collection that has been filtered.
12361 * @param {String} property A property on your objects
12362 * @param {String/RegExp} value Either string that the property values
12363 * should start with or a RegExp to test against the property
12364 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
12365 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
12366 * @return {MixedCollection} The new filtered collection
12368 filter : function(property, value, anyMatch, caseSensitive){
12369 if(Ext.isEmpty(value, false)){
12370 return this.clone();
12372 value = this.createValueMatcher(value, anyMatch, caseSensitive);
12373 return this.filterBy(function(o){
12374 return o && value.test(o[property]);
12379 * Filter by a function. Returns a <i>new</i> collection that has been filtered.
12380 * The passed function will be called with each object in the collection.
12381 * If the function returns true, the value is included otherwise it is filtered.
12382 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12383 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12384 * @return {MixedCollection} The new filtered collection
12386 filterBy : function(fn, scope){
12387 var r = new Ext.util.MixedCollection();
12388 r.getKey = this.getKey;
12389 var k = this.keys, it = this.items;
12390 for(var i = 0, len = it.length; i < len; i++){
12391 if(fn.call(scope||this, it[i], k[i])){
12392 r.add(k[i], it[i]);
12399 * Finds the index of the first matching object in this collection by a specific property/value.
12400 * @param {String} property The name of a property on your objects.
12401 * @param {String/RegExp} value A string that the property values
12402 * should start with or a RegExp to test against the property.
12403 * @param {Number} start (optional) The index to start searching at (defaults to 0).
12404 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
12405 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
12406 * @return {Number} The matched index or -1
12408 findIndex : function(property, value, start, anyMatch, caseSensitive){
12409 if(Ext.isEmpty(value, false)){
12412 value = this.createValueMatcher(value, anyMatch, caseSensitive);
12413 return this.findIndexBy(function(o){
12414 return o && value.test(o[property]);
12419 * Find the index of the first matching object in this collection by a function.
12420 * If the function returns <i>true</i> it is considered a match.
12421 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
12422 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12423 * @param {Number} start (optional) The index to start searching at (defaults to 0).
12424 * @return {Number} The matched index or -1
12426 findIndexBy : function(fn, scope, start){
12427 var k = this.keys, it = this.items;
12428 for(var i = (start||0), len = it.length; i < len; i++){
12429 if(fn.call(scope||this, it[i], k[i])){
12437 * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
12438 * and by Ext.data.Store#filter
12440 * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
12441 * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
12442 * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
12443 * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
12445 createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
12446 if (!value.exec) { // not a regex
12447 var er = Ext.escapeRe;
12448 value = String(value);
12450 if (anyMatch === true) {
12453 value = '^' + er(value);
12454 if (exactMatch === true) {
12458 value = new RegExp(value, caseSensitive ? '' : 'i');
12464 * Creates a shallow copy of this collection
12465 * @return {MixedCollection}
12467 clone : function(){
12468 var r = new Ext.util.MixedCollection();
12469 var k = this.keys, it = this.items;
12470 for(var i = 0, len = it.length; i < len; i++){
12471 r.add(k[i], it[i]);
12473 r.getKey = this.getKey;
12478 * This method calls {@link #item item()}.
12479 * Returns the item associated with the passed key OR index. Key has priority
12480 * over index. This is the equivalent of calling {@link #key} first, then if
12481 * nothing matched calling {@link #itemAt}.
12482 * @param {String/Number} key The key or index of the item.
12483 * @return {Object} If the item is found, returns the item. If the item was
12484 * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
12485 * returns <tt>null</tt>.
12487 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;
12489 * @class Ext.util.JSON
12490 * Modified version of Douglas Crockford"s json.js that doesn"t
12491 * mess with the Object prototype
12492 * http://www.json.org/js.html
12495 Ext.util.JSON = new (function(){
12496 var useHasOwn = !!{}.hasOwnProperty,
12497 isNative = function() {
12498 var useNative = null;
12500 return function() {
12501 if (useNative === null) {
12502 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
12508 pad = function(n) {
12509 return n < 10 ? "0" + n : n;
12511 doDecode = function(json){
12512 return eval("(" + json + ')');
12514 doEncode = function(o){
12515 if(!Ext.isDefined(o) || o === null){
12517 }else if(Ext.isArray(o)){
12518 return encodeArray(o);
12519 }else if(Ext.isDate(o)){
12520 return Ext.util.JSON.encodeDate(o);
12521 }else if(Ext.isString(o)){
12522 return encodeString(o);
12523 }else if(typeof o == "number"){
12524 //don't use isNumber here, since finite checks happen inside isNumber
12525 return isFinite(o) ? String(o) : "null";
12526 }else if(Ext.isBoolean(o)){
12529 var a = ["{"], b, i, v;
12531 // don't encode DOM objects
12532 if(!o.getElementsByTagName){
12533 if(!useHasOwn || o.hasOwnProperty(i)) {
12535 switch (typeof v) {
12544 a.push(doEncode(i), ":",
12545 v === null ? "null" : doEncode(v));
12564 encodeString = function(s){
12565 if (/["\\\x00-\x1f]/.test(s)) {
12566 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12571 c = b.charCodeAt();
12573 Math.floor(c / 16).toString(16) +
12574 (c % 16).toString(16);
12577 return '"' + s + '"';
12579 encodeArray = function(o){
12580 var a = ["["], b, i, l = o.length, v;
12581 for (i = 0; i < l; i += 1) {
12583 switch (typeof v) {
12592 a.push(v === null ? "null" : Ext.util.JSON.encode(v));
12601 * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
12602 * <b>The returned value includes enclosing double quotation marks.</b></p>
12603 * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
12604 * <p>To override this:</p><pre><code>
12605 Ext.util.JSON.encodeDate = function(d) {
12606 return d.format('"Y-m-d"');
12609 * @param {Date} d The Date to encode
12610 * @return {String} The string literal to use in a JSON string.
12612 this.encodeDate = function(o){
12613 return '"' + o.getFullYear() + "-" +
12614 pad(o.getMonth() + 1) + "-" +
12615 pad(o.getDate()) + "T" +
12616 pad(o.getHours()) + ":" +
12617 pad(o.getMinutes()) + ":" +
12618 pad(o.getSeconds()) + '"';
12622 * Encodes an Object, Array or other value
12623 * @param {Mixed} o The variable to encode
12624 * @return {String} The JSON string
12626 this.encode = function() {
12628 return function(o) {
12630 // setup encoding function on first access
12631 ec = isNative() ? JSON.stringify : doEncode;
12639 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
12640 * @param {String} json The JSON string
12641 * @return {Object} The resulting object
12643 this.decode = function() {
12645 return function(json) {
12647 // setup decoding function on first access
12648 dc = isNative() ? JSON.parse : doDecode;
12656 * Shorthand for {@link Ext.util.JSON#encode}
12657 * @param {Mixed} o The variable to encode
12658 * @return {String} The JSON string
12662 Ext.encode = Ext.util.JSON.encode;
12664 * Shorthand for {@link Ext.util.JSON#decode}
12665 * @param {String} json The JSON string
12666 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
12667 * @return {Object} The resulting object
12671 Ext.decode = Ext.util.JSON.decode;
12673 * @class Ext.util.Format
12674 * Reusable data formatting functions
12677 Ext.util.Format = function(){
12678 var trimRe = /^\s+|\s+$/g,
12679 stripTagsRE = /<\/?[^>]+>/gi,
12680 stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
12681 nl2brRe = /\r?\n/g;
12685 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12686 * @param {String} value The string to truncate
12687 * @param {Number} length The maximum length to allow before truncating
12688 * @param {Boolean} word True to try to find a common work break
12689 * @return {String} The converted text
12691 ellipsis : function(value, len, word){
12692 if(value && value.length > len){
12694 var vs = value.substr(0, len - 2),
12695 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
12696 if(index == -1 || index < (len - 15)){
12697 return value.substr(0, len - 3) + "...";
12699 return vs.substr(0, index) + "...";
12702 return value.substr(0, len - 3) + "...";
12709 * Checks a reference and converts it to empty string if it is undefined
12710 * @param {Mixed} value Reference to check
12711 * @return {Mixed} Empty string if converted, otherwise the original value
12713 undef : function(value){
12714 return value !== undefined ? value : "";
12718 * Checks a reference and converts it to the default value if it's empty
12719 * @param {Mixed} value Reference to check
12720 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
12723 defaultValue : function(value, defaultValue){
12724 return value !== undefined && value !== '' ? value : defaultValue;
12728 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12729 * @param {String} value The string to encode
12730 * @return {String} The encoded text
12732 htmlEncode : function(value){
12733 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
12737 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12738 * @param {String} value The string to decode
12739 * @return {String} The decoded text
12741 htmlDecode : function(value){
12742 return !value ? value : String(value).replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&");
12746 * Trims any whitespace from either side of a string
12747 * @param {String} value The text to trim
12748 * @return {String} The trimmed text
12750 trim : function(value){
12751 return String(value).replace(trimRe, "");
12755 * Returns a substring from within an original string
12756 * @param {String} value The original text
12757 * @param {Number} start The start index of the substring
12758 * @param {Number} length The length of the substring
12759 * @return {String} The substring
12761 substr : function(value, start, length){
12762 return String(value).substr(start, length);
12766 * Converts a string to all lower case letters
12767 * @param {String} value The text to convert
12768 * @return {String} The converted text
12770 lowercase : function(value){
12771 return String(value).toLowerCase();
12775 * Converts a string to all upper case letters
12776 * @param {String} value The text to convert
12777 * @return {String} The converted text
12779 uppercase : function(value){
12780 return String(value).toUpperCase();
12784 * Converts the first character only of a string to upper case
12785 * @param {String} value The text to convert
12786 * @return {String} The converted text
12788 capitalize : function(value){
12789 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12793 call : function(value, fn){
12794 if(arguments.length > 2){
12795 var args = Array.prototype.slice.call(arguments, 2);
12796 args.unshift(value);
12797 return eval(fn).apply(window, args);
12799 return eval(fn).call(window, value);
12804 * Format a number as US currency
12805 * @param {Number/String} value The numeric value to format
12806 * @return {String} The formatted currency string
12808 usMoney : function(v){
12809 v = (Math.round((v-0)*100))/100;
12810 v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12812 var ps = v.split('.'),
12814 sub = ps[1] ? '.'+ ps[1] : '.00',
12815 r = /(\d+)(\d{3})/;
12816 while (r.test(whole)) {
12817 whole = whole.replace(r, '$1' + ',' + '$2');
12820 if(v.charAt(0) == '-'){
12821 return '-$' + v.substr(1);
12827 * Parse a value into a formatted date using the specified format pattern.
12828 * @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)
12829 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12830 * @return {String} The formatted date string
12832 date : function(v, format){
12836 if(!Ext.isDate(v)){
12837 v = new Date(Date.parse(v));
12839 return v.dateFormat(format || "m/d/Y");
12843 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12844 * @param {String} format Any valid date format string
12845 * @return {Function} The date formatting function
12847 dateRenderer : function(format){
12848 return function(v){
12849 return Ext.util.Format.date(v, format);
12854 * Strips all HTML tags
12855 * @param {Mixed} value The text from which to strip tags
12856 * @return {String} The stripped text
12858 stripTags : function(v){
12859 return !v ? v : String(v).replace(stripTagsRE, "");
12863 * Strips all script tags
12864 * @param {Mixed} value The text from which to strip script tags
12865 * @return {String} The stripped text
12867 stripScripts : function(v){
12868 return !v ? v : String(v).replace(stripScriptsRe, "");
12872 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
12873 * @param {Number/String} size The numeric value to format
12874 * @return {String} The formatted file size
12876 fileSize : function(size){
12878 return size + " bytes";
12879 } else if(size < 1048576) {
12880 return (Math.round(((size*10) / 1024))/10) + " KB";
12882 return (Math.round(((size*10) / 1048576))/10) + " MB";
12887 * It does simple math for use in a template, for example:<pre><code>
12888 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
12890 * @return {Function} A function that operates on the passed value.
12894 return function(v, a){
12896 fns[a] = new Function('v', 'return v ' + a + ';');
12903 * Rounds the passed number to the required decimal precision.
12904 * @param {Number/String} value The numeric value to round.
12905 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
12906 * @return {Number} The rounded value.
12908 round : function(value, precision) {
12909 var result = Number(value);
12910 if (typeof precision == 'number') {
12911 precision = Math.pow(10, precision);
12912 result = Math.round(value * precision) / precision;
12918 * Formats the number according to the format string.
12919 * <div style="margin-left:40px">examples (123456.789):
12920 * <div style="margin-left:10px">
12921 * 0 - (123456) show only digits, no precision<br>
12922 * 0.00 - (123456.78) show only digits, 2 precision<br>
12923 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
12924 * 0,000 - (123,456) show comma and digits, no precision<br>
12925 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
12926 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
12927 * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
12928 * For example: 0.000,00/i
12930 * @param {Number} v The number to format.
12931 * @param {String} format The way you would like to format this text.
12932 * @return {String} The formatted number.
12934 number: function(v, format) {
12938 v = Ext.num(v, NaN);
12948 if(format.substr(format.length - 2) == '/i'){
12949 format = format.substr(0, format.length - 2);
12955 var hasComma = format.indexOf(comma) != -1,
12956 psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
12958 if(1 < psplit.length){
12959 v = v.toFixed(psplit[1].length);
12960 }else if(2 < psplit.length){
12961 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
12966 var fnum = v.toString();
12968 psplit = fnum.split('.');
12971 var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;
12973 for (var i = 0; i < j; i += n) {
12977 parr[parr.length] = cnum.substr(i, n);
12980 fnum = parr.join(comma);
12982 fnum += dec + psplit[1];
12986 fnum = psplit[0] + dec + psplit[1];
12990 return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
12994 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
12995 * @param {String} format Any valid number format string for {@link #number}
12996 * @return {Function} The number formatting function
12998 numberRenderer : function(format){
12999 return function(v){
13000 return Ext.util.Format.number(v, format);
13005 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
13006 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
13007 * if the value is 0 or greater than 1.
13008 * @param {Number} value The value to compare against
13009 * @param {String} singular The singular form of the word
13010 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
13012 plural : function(v, s, p){
13013 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
13017 * Converts newline characters to the HTML tag <br/>
13018 * @param {String} The string value to format.
13019 * @return {String} The string with embedded <br/> tags in place of newlines.
13021 nl2br : function(v){
13022 return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
13027 * @class Ext.XTemplate
13028 * @extends Ext.Template
13029 * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
13030 * <li>Autofilling arrays using templates and sub-templates</li>
13031 * <li>Conditional processing with basic comparison operators</li>
13032 * <li>Basic math function support</li>
13033 * <li>Execute arbitrary inline code with special built-in template variables</li>
13034 * <li>Custom member functions</li>
13035 * <li>Many special tags and built-in operators that aren't defined as part of
13036 * the API, but are supported in the templates that can be created</li>
13038 * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
13039 * <li>{@link Ext.DataView}</li>
13040 * <li>{@link Ext.ListView}</li>
13041 * <li>{@link Ext.form.ComboBox}</li>
13042 * <li>{@link Ext.grid.TemplateColumn}</li>
13043 * <li>{@link Ext.grid.GroupingView}</li>
13044 * <li>{@link Ext.menu.Item}</li>
13045 * <li>{@link Ext.layout.MenuLayout}</li>
13046 * <li>{@link Ext.ColorPalette}</li>
13049 * <p>For example usage {@link #XTemplate see the constructor}.</p>
13052 * The {@link Ext.Template#Template Ext.Template constructor} describes
13053 * the acceptable parameters to pass to the constructor. The following
13054 * examples demonstrate all of the supported features.</p>
13056 * <div class="mdetail-params"><ul>
13058 * <li><b><u>Sample Data</u></b>
13059 * <div class="sub-desc">
13060 * <p>This is the data object used for reference in each code example:</p>
13063 name: 'Jack Slocum',
13064 title: 'Lead Developer',
13065 company: 'Ext JS, LLC',
13066 email: 'jack@extjs.com',
13067 address: '4 Red Bulls Drive',
13071 drinks: ['Red Bull', 'Coffee', 'Water'],
13073 name: 'Sara Grace',
13079 name: 'John James',
13088 * <li><b><u>Auto filling of arrays</u></b>
13089 * <div class="sub-desc">
13090 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
13091 * to process the provided data object:
13093 * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
13094 * repeating the template block inside the <tt>tpl</tt> tag for each item in the
13096 * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
13097 * <li>While processing an array, the special variable <tt>{#}</tt>
13098 * will provide the current array index + 1 (starts at 1, not 0).</li>
13102 <tpl <b>for</b>=".">...</tpl> // loop through array at root node
13103 <tpl <b>for</b>="foo">...</tpl> // loop through array at foo node
13104 <tpl <b>for</b>="foo.bar">...</tpl> // loop through array at foo.bar node
13106 * Using the sample data above:
13108 var tpl = new Ext.XTemplate(
13110 '<tpl <b>for</b>=".">', // process the data.kids node
13111 '<p>{#}. {name}</p>', // use current array index to autonumber
13114 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
13116 * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
13117 * to access specified members of the provided data object to populate the template:</p>
13119 var tpl = new Ext.XTemplate(
13120 '<p>Name: {name}</p>',
13121 '<p>Title: {title}</p>',
13122 '<p>Company: {company}</p>',
13124 '<tpl <b>for="kids"</b>>', // interrogate the kids property within the data
13125 '<p>{name}</p>',
13128 tpl.overwrite(panel.body, data); // pass the root node of the data object
13130 * <p>Flat arrays that contain values (and not objects) can be auto-rendered
13131 * using the special <b><tt>{.}</tt></b> variable inside a loop. This variable
13132 * will represent the value of the array at the current index:</p>
13134 var tpl = new Ext.XTemplate(
13135 '<p>{name}\'s favorite beverages:</p>',
13136 '<tpl for="drinks">',
13137 '<div> - {.}</div>',
13140 tpl.overwrite(panel.body, data);
13142 * <p>When processing a sub-template, for example while looping through a child array,
13143 * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
13145 var tpl = new Ext.XTemplate(
13146 '<p>Name: {name}</p>',
13148 '<tpl for="kids">',
13149 '<tpl if="age > 1">',
13150 '<p>{name}</p>',
13151 '<p>Dad: {<b>parent</b>.name}</p>',
13155 tpl.overwrite(panel.body, data);
13161 * <li><b><u>Conditional processing with basic comparison operators</u></b>
13162 * <div class="sub-desc">
13163 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
13164 * to provide conditional checks for deciding whether or not to render specific
13165 * parts of the template. Notes:<div class="sub-desc"><ul>
13166 * <li>Double quotes must be encoded if used within the conditional</li>
13167 * <li>There is no <tt>else</tt> operator — if needed, two opposite
13168 * <tt>if</tt> statements should be used.</li>
13171 <tpl if="age > 1 && age < 10">Child</tpl>
13172 <tpl if="age >= 10 && age < 18">Teenager</tpl>
13173 <tpl <b>if</b>="this.isGirl(name)">...</tpl>
13174 <tpl <b>if</b>="id==\'download\'">...</tpl>
13175 <tpl <b>if</b>="needsIcon"><img src="{icon}" class="{iconCls}"/></tpl>
13177 <tpl if="name == "Jack"">Hello</tpl>
13178 // encode " if it is part of the condition, e.g.
13179 <tpl if="name == &quot;Jack&quot;">Hello</tpl>
13181 * Using the sample data above:
13183 var tpl = new Ext.XTemplate(
13184 '<p>Name: {name}</p>',
13186 '<tpl for="kids">',
13187 '<tpl if="age > 1">',
13188 '<p>{name}</p>',
13192 tpl.overwrite(panel.body, data);
13198 * <li><b><u>Basic math support</u></b>
13199 * <div class="sub-desc">
13200 * <p>The following basic math operators may be applied directly on numeric
13201 * data values:</p><pre>
13206 var tpl = new Ext.XTemplate(
13207 '<p>Name: {name}</p>',
13209 '<tpl for="kids">',
13210 '<tpl if="age &gt; 1">', // <-- Note that the > is encoded
13211 '<p>{#}: {name}</p>', // <-- Auto-number each item
13212 '<p>In 5 Years: {age+5}</p>', // <-- Basic math
13213 '<p>Dad: {parent.name}</p>',
13217 tpl.overwrite(panel.body, data);
13223 * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
13224 * <div class="sub-desc">
13225 * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
13226 * in the scope of the template. There are some special variables available in that code:
13228 * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
13229 * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
13230 * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
13231 * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
13232 * loop you are in (1-based).</li>
13233 * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
13234 * of the array you are looping.</li>
13235 * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
13237 * This example demonstrates basic row striping using an inline code block and the
13238 * <tt>xindex</tt> variable:</p>
13240 var tpl = new Ext.XTemplate(
13241 '<p>Name: {name}</p>',
13242 '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
13244 '<tpl for="kids">',
13245 '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
13250 tpl.overwrite(panel.body, data);
13255 * <li><b><u>Template member functions</u></b>
13256 * <div class="sub-desc">
13257 * <p>One or more member functions can be specified in a configuration
13258 * object passed into the XTemplate constructor for more complex processing:</p>
13260 var tpl = new Ext.XTemplate(
13261 '<p>Name: {name}</p>',
13263 '<tpl for="kids">',
13264 '<tpl if="this.isGirl(name)">',
13265 '<p>Girl: {name} - {age}</p>',
13267 // use opposite if statement to simulate 'else' processing:
13268 '<tpl if="this.isGirl(name) == false">',
13269 '<p>Boy: {name} - {age}</p>',
13271 '<tpl if="this.isBaby(age)">',
13272 '<p>{name} is a baby!</p>',
13274 '</tpl></p>',
13276 // XTemplate configuration:
13278 disableFormats: true,
13279 // member functions:
13280 isGirl: function(name){
13281 return name == 'Sara Grace';
13283 isBaby: function(age){
13288 tpl.overwrite(panel.body, data);
13295 * @param {Mixed} config
13297 Ext.XTemplate = function(){
13298 Ext.XTemplate.superclass.constructor.apply(this, arguments);
13302 re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
13303 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
13304 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
13305 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
13313 RETURN = 'return ',
13314 WITHVALUES = 'with(values){ ';
13316 s = ['<tpl>', s, '</tpl>'].join('');
13318 while((m = s.match(re))){
13319 var m2 = m[0].match(nameRe),
13320 m3 = m[0].match(ifRe),
13321 m4 = m[0].match(execRe),
13325 name = m2 && m2[1] ? m2[1] : '';
13328 exp = m3 && m3[1] ? m3[1] : null;
13330 fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
13334 exp = m4 && m4[1] ? m4[1] : null;
13336 exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
13341 case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
13342 case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
13343 default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
13353 s = s.replace(m[0], '{xtpl'+ id + '}');
13356 for(var i = tpls.length-1; i >= 0; --i){
13357 me.compileTpl(tpls[i]);
13359 me.master = tpls[tpls.length-1];
13362 Ext.extend(Ext.XTemplate, Ext.Template, {
13364 re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
13366 codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
13369 applySubTemplate : function(id, values, parent, xindex, xcount){
13375 if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
13376 (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
13379 vs = t.target ? t.target.call(me, values, parent) : values;
13381 parent = t.target ? values : parent;
13382 if(t.target && Ext.isArray(vs)){
13383 for(var i = 0, len = vs.length; i < len; i++){
13384 buf[buf.length] = t.compiled.call(me, vs[i], parent, i+1, len);
13386 return buf.join('');
13388 return t.compiled.call(me, vs, parent, xindex, xcount);
13392 compileTpl : function(tpl){
13393 var fm = Ext.util.Format,
13394 useF = this.disableFormats !== true,
13395 sep = Ext.isGecko ? "+" : ",",
13398 function fn(m, name, format, args, math){
13399 if(name.substr(0, 4) == 'xtpl'){
13400 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
13405 }else if(name === '#'){
13407 }else if(name.indexOf('.') != -1){
13410 v = "values['" + name + "']";
13413 v = '(' + v + math + ')';
13415 if (format && useF) {
13416 args = args ? ',' + args : "";
13417 if(format.substr(0, 5) != "this."){
13418 format = "fm." + format + '(';
13420 format = 'this.call("'+ format.substr(5) + '", ';
13424 args= ''; format = "("+v+" === undefined ? '' : ";
13426 return "'"+ sep + format + v + args + ")"+sep+"'";
13429 function codeFn(m, code){
13430 // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
13431 return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
13434 // branched to use + in gecko and [].join() in others
13436 body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
13437 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
13440 body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
13441 body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
13442 body.push("'].join('');};");
13443 body = body.join('');
13450 * Returns an HTML fragment of this template with the specified values applied.
13451 * @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'})
13452 * @return {String} The HTML fragment
13454 applyTemplate : function(values){
13455 return this.master.compiled.call(this, values, {}, 1, 1);
13459 * Compile the template to a function for optimized performance. Recommended if the template will be used frequently.
13460 * @return {Function} The compiled function
13462 compile : function(){return this;}
13469 * @property disableFormats
13479 * Alias for {@link #applyTemplate}
13480 * Returns an HTML fragment of this template with the specified values applied.
13481 * @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'})
13482 * @return {String} The HTML fragment
13483 * @member Ext.XTemplate
13486 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
13489 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
13490 * @param {String/HTMLElement} el A DOM element or its id
13491 * @return {Ext.Template} The created template
13494 Ext.XTemplate.from = function(el){
13495 el = Ext.getDom(el);
13496 return new Ext.XTemplate(el.value || el.innerHTML);
13499 * @class Ext.util.CSS
13500 * Utility class for manipulating CSS rules
13503 Ext.util.CSS = function(){
13505 var doc = document;
13507 var camelRe = /(-[a-z])/gi;
13508 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13512 * Creates a stylesheet from a text blob of rules.
13513 * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
13514 * @param {String} cssText The text containing the css rules
13515 * @param {String} id An id to add to the stylesheet for later removal
13516 * @return {StyleSheet}
13518 createStyleSheet : function(cssText, id){
13520 var head = doc.getElementsByTagName("head")[0];
13521 var rules = doc.createElement("style");
13522 rules.setAttribute("type", "text/css");
13524 rules.setAttribute("id", id);
13527 head.appendChild(rules);
13528 ss = rules.styleSheet;
13529 ss.cssText = cssText;
13532 rules.appendChild(doc.createTextNode(cssText));
13534 rules.cssText = cssText;
13536 head.appendChild(rules);
13537 ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13539 this.cacheStyleSheet(ss);
13544 * Removes a style or link tag by id
13545 * @param {String} id The id of the tag
13547 removeStyleSheet : function(id){
13548 var existing = doc.getElementById(id);
13550 existing.parentNode.removeChild(existing);
13555 * Dynamically swaps an existing stylesheet reference for a new one
13556 * @param {String} id The id of an existing link tag to remove
13557 * @param {String} url The href of the new stylesheet to include
13559 swapStyleSheet : function(id, url){
13560 this.removeStyleSheet(id);
13561 var ss = doc.createElement("link");
13562 ss.setAttribute("rel", "stylesheet");
13563 ss.setAttribute("type", "text/css");
13564 ss.setAttribute("id", id);
13565 ss.setAttribute("href", url);
13566 doc.getElementsByTagName("head")[0].appendChild(ss);
13570 * Refresh the rule cache if you have dynamically added stylesheets
13571 * @return {Object} An object (hash) of rules indexed by selector
13573 refreshCache : function(){
13574 return this.getRules(true);
13578 cacheStyleSheet : function(ss){
13582 try{// try catch for cross domain access issue
13583 var ssRules = ss.cssRules || ss.rules;
13584 for(var j = ssRules.length-1; j >= 0; --j){
13585 rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
13591 * Gets all css rules for the document
13592 * @param {Boolean} refreshCache true to refresh the internal cache
13593 * @return {Object} An object (hash) of rules indexed by selector
13595 getRules : function(refreshCache){
13596 if(rules === null || refreshCache){
13598 var ds = doc.styleSheets;
13599 for(var i =0, len = ds.length; i < len; i++){
13601 this.cacheStyleSheet(ds[i]);
13609 * Gets an an individual CSS rule by selector(s)
13610 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13611 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13612 * @return {CSSRule} The CSS rule or null if one is not found
13614 getRule : function(selector, refreshCache){
13615 var rs = this.getRules(refreshCache);
13616 if(!Ext.isArray(selector)){
13617 return rs[selector.toLowerCase()];
13619 for(var i = 0; i < selector.length; i++){
13620 if(rs[selector[i]]){
13621 return rs[selector[i].toLowerCase()];
13629 * Updates a rule property
13630 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13631 * @param {String} property The css property
13632 * @param {String} value The new value for the property
13633 * @return {Boolean} true If a rule was found and updated
13635 updateRule : function(selector, property, value){
13636 if(!Ext.isArray(selector)){
13637 var rule = this.getRule(selector);
13639 rule.style[property.replace(camelRe, camelFn)] = value;
13643 for(var i = 0; i < selector.length; i++){
13644 if(this.updateRule(selector[i], property, value)){
13653 @class Ext.util.ClickRepeater
13654 @extends Ext.util.Observable
13656 A wrapper class which can be applied to any element. Fires a "click" event while the
13657 mouse is pressed. The interval between firings may be specified in the config but
13658 defaults to 20 milliseconds.
13660 Optionally, a CSS class may be applied to the element during the time it is pressed.
13662 @cfg {Mixed} el The element to act as a button.
13663 @cfg {Number} delay The initial delay before the repeating event begins firing.
13664 Similar to an autorepeat key delay.
13665 @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
13666 @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13667 @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13668 "interval" and "delay" are ignored.
13669 @cfg {Boolean} preventDefault True to prevent the default click event
13670 @cfg {Boolean} stopDefault True to stop the default click event
13673 2007-02-02 jvs Original code contributed by Nige "Animal" White
13674 2007-02-02 jvs Renamed to ClickRepeater
13675 2007-02-03 jvs Modifications for FF Mac and Safari
13678 @param {Mixed} el The element to listen on
13679 @param {Object} config
13681 Ext.util.ClickRepeater = Ext.extend(Ext.util.Observable, {
13683 constructor : function(el, config){
13684 this.el = Ext.get(el);
13685 this.el.unselectable();
13687 Ext.apply(this, config);
13692 * Fires when the mouse button is depressed.
13693 * @param {Ext.util.ClickRepeater} this
13694 * @param {Ext.EventObject} e
13699 * Fires on a specified interval during the time the element is pressed.
13700 * @param {Ext.util.ClickRepeater} this
13701 * @param {Ext.EventObject} e
13706 * Fires when the mouse key is released.
13707 * @param {Ext.util.ClickRepeater} this
13708 * @param {Ext.EventObject} e
13713 if(!this.disabled){
13714 this.disabled = true;
13718 // allow inline handler
13720 this.on("click", this.handler, this.scope || this);
13723 Ext.util.ClickRepeater.superclass.constructor.call(this);
13728 preventDefault : true,
13729 stopDefault : false,
13733 * Enables the repeater and allows events to fire.
13735 enable: function(){
13737 this.el.on('mousedown', this.handleMouseDown, this);
13739 this.el.on('dblclick', this.handleDblClick, this);
13741 if(this.preventDefault || this.stopDefault){
13742 this.el.on('click', this.eventOptions, this);
13745 this.disabled = false;
13749 * Disables the repeater and stops events from firing.
13751 disable: function(/* private */ force){
13752 if(force || !this.disabled){
13753 clearTimeout(this.timer);
13754 if(this.pressClass){
13755 this.el.removeClass(this.pressClass);
13757 Ext.getDoc().un('mouseup', this.handleMouseUp, this);
13758 this.el.removeAllListeners();
13760 this.disabled = true;
13764 * Convenience function for setting disabled/enabled by boolean.
13765 * @param {Boolean} disabled
13767 setDisabled: function(disabled){
13768 this[disabled ? 'disable' : 'enable']();
13771 eventOptions: function(e){
13772 if(this.preventDefault){
13773 e.preventDefault();
13775 if(this.stopDefault){
13781 destroy : function() {
13782 this.disable(true);
13783 Ext.destroy(this.el);
13784 this.purgeListeners();
13787 handleDblClick : function(e){
13788 clearTimeout(this.timer);
13791 this.fireEvent("mousedown", this, e);
13792 this.fireEvent("click", this, e);
13796 handleMouseDown : function(e){
13797 clearTimeout(this.timer);
13799 if(this.pressClass){
13800 this.el.addClass(this.pressClass);
13802 this.mousedownTime = new Date();
13804 Ext.getDoc().on("mouseup", this.handleMouseUp, this);
13805 this.el.on("mouseout", this.handleMouseOut, this);
13807 this.fireEvent("mousedown", this, e);
13808 this.fireEvent("click", this, e);
13810 // Do not honor delay or interval if acceleration wanted.
13811 if (this.accelerate) {
13814 this.timer = this.click.defer(this.delay || this.interval, this, [e]);
13818 click : function(e){
13819 this.fireEvent("click", this, e);
13820 this.timer = this.click.defer(this.accelerate ?
13821 this.easeOutExpo(this.mousedownTime.getElapsed(),
13825 this.interval, this, [e]);
13828 easeOutExpo : function (t, b, c, d) {
13829 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
13833 handleMouseOut : function(){
13834 clearTimeout(this.timer);
13835 if(this.pressClass){
13836 this.el.removeClass(this.pressClass);
13838 this.el.on("mouseover", this.handleMouseReturn, this);
13842 handleMouseReturn : function(){
13843 this.el.un("mouseover", this.handleMouseReturn, this);
13844 if(this.pressClass){
13845 this.el.addClass(this.pressClass);
13851 handleMouseUp : function(e){
13852 clearTimeout(this.timer);
13853 this.el.un("mouseover", this.handleMouseReturn, this);
13854 this.el.un("mouseout", this.handleMouseOut, this);
13855 Ext.getDoc().un("mouseup", this.handleMouseUp, this);
13856 this.el.removeClass(this.pressClass);
13857 this.fireEvent("mouseup", this, e);
13860 * @class Ext.KeyNav
13861 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
13862 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13863 * way to implement custom navigation schemes for any UI component.</p>
13864 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13865 * pageUp, pageDown, del, home, end. Usage:</p>
13867 var nav = new Ext.KeyNav("my-element", {
13868 "left" : function(e){
13869 this.moveLeft(e.ctrlKey);
13871 "right" : function(e){
13872 this.moveRight(e.ctrlKey);
13874 "enter" : function(e){
13881 * @param {Mixed} el The element to bind to
13882 * @param {Object} config The config
13884 Ext.KeyNav = function(el, config){
13885 this.el = Ext.get(el);
13886 Ext.apply(this, config);
13887 if(!this.disabled){
13888 this.disabled = true;
13893 Ext.KeyNav.prototype = {
13895 * @cfg {Boolean} disabled
13896 * True to disable this KeyNav instance (defaults to false)
13900 * @cfg {String} defaultEventAction
13901 * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key. Valid values are
13902 * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
13903 * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
13905 defaultEventAction: "stopEvent",
13907 * @cfg {Boolean} forceKeyDown
13908 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
13909 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13910 * handle keydown instead of keypress.
13912 forceKeyDown : false,
13915 relay : function(e){
13916 var k = e.getKey();
13917 var h = this.keyToHandler[k];
13919 if(this.doRelay(e, this[h], h) !== true){
13920 e[this.defaultEventAction]();
13926 doRelay : function(e, h, hname){
13927 return h.call(this.scope || this, e);
13930 // possible handlers
13944 // quick lookup hash
13960 stopKeyUp: function(e) {
13961 var k = e.getKey();
13963 if (k >= 37 && k <= 40) {
13964 // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
13965 // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
13971 * Destroy this KeyNav (this is the same as calling disable).
13973 destroy: function(){
13978 * Enable this KeyNav
13980 enable: function() {
13981 if (this.disabled) {
13982 if (Ext.isSafari2) {
13983 // call stopKeyUp() on "keyup" event
13984 this.el.on('keyup', this.stopKeyUp, this);
13987 this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13988 this.disabled = false;
13993 * Disable this KeyNav
13995 disable: function() {
13996 if (!this.disabled) {
13997 if (Ext.isSafari2) {
13998 // remove "keyup" event handler
13999 this.el.un('keyup', this.stopKeyUp, this);
14002 this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
14003 this.disabled = true;
14008 * Convenience function for setting disabled/enabled by boolean.
14009 * @param {Boolean} disabled
14011 setDisabled : function(disabled){
14012 this[disabled ? "disable" : "enable"]();
14016 isKeydown: function(){
14017 return this.forceKeyDown || Ext.EventManager.useKeydown;
14021 * @class Ext.KeyMap
14022 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14023 * The constructor accepts the same config object as defined by {@link #addBinding}.
14024 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14025 * combination it will call the function with this signature (if the match is a multi-key
14026 * combination the callback will still be called only once): (String key, Ext.EventObject e)
14027 * A KeyMap can also handle a string representation of keys.<br />
14030 // map one key by key code
14031 var map = new Ext.KeyMap("my-element", {
14032 key: 13, // or Ext.EventObject.ENTER
14037 // map multiple keys to one action by string
14038 var map = new Ext.KeyMap("my-element", {
14044 // map multiple keys to multiple actions by strings and array of codes
14045 var map = new Ext.KeyMap("my-element", [
14048 fn: function(){ alert("Return was pressed"); }
14051 fn: function(){ alert('a, b or c was pressed'); }
14056 fn: function(){ alert('Control + shift + tab was pressed.'); }
14060 * <b>Note: A KeyMap starts enabled</b>
14062 * @param {Mixed} el The element to bind to
14063 * @param {Object} config The config (see {@link #addBinding})
14064 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14066 Ext.KeyMap = function(el, config, eventName){
14067 this.el = Ext.get(el);
14068 this.eventName = eventName || "keydown";
14069 this.bindings = [];
14071 this.addBinding(config);
14076 Ext.KeyMap.prototype = {
14078 * True to stop the event from bubbling and prevent the default browser action if the
14079 * key was handled by the KeyMap (defaults to false)
14085 * Add a new binding to this KeyMap. The following config object properties are supported:
14087 Property Type Description
14088 ---------- --------------- ----------------------------------------------------------------------
14089 key String/Array A single keycode or an array of keycodes to handle
14090 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)
14091 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)
14092 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)
14093 handler Function The function to call when KeyMap finds the expected key combination
14094 fn Function Alias of handler (for backwards-compatibility)
14095 scope Object The scope of the callback function
14096 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)
14102 var map = new Ext.KeyMap(document, {
14103 key: Ext.EventObject.ENTER,
14108 //Add a new binding to the existing KeyMap later
14116 * @param {Object/Array} config A single KeyMap config or an array of configs
14118 addBinding : function(config){
14119 if(Ext.isArray(config)){
14120 Ext.each(config, function(c){
14121 this.addBinding(c);
14125 var keyCode = config.key,
14126 fn = config.fn || config.handler,
14127 scope = config.scope;
14129 if (config.stopEvent) {
14130 this.stopEvent = config.stopEvent;
14133 if(typeof keyCode == "string"){
14135 var keyString = keyCode.toUpperCase();
14136 for(var j = 0, len = keyString.length; j < len; j++){
14137 ks.push(keyString.charCodeAt(j));
14141 var keyArray = Ext.isArray(keyCode);
14143 var handler = function(e){
14144 if(this.checkModifiers(config, e)){
14145 var k = e.getKey();
14147 for(var i = 0, len = keyCode.length; i < len; i++){
14148 if(keyCode[i] == k){
14149 if(this.stopEvent){
14152 fn.call(scope || window, k, e);
14158 if(this.stopEvent){
14161 fn.call(scope || window, k, e);
14166 this.bindings.push(handler);
14170 checkModifiers: function(config, e){
14171 var val, key, keys = ['shift', 'ctrl', 'alt'];
14172 for (var i = 0, len = keys.length; i < len; ++i){
14175 if(!(val === undefined || (val === e[key + 'Key']))){
14183 * Shorthand for adding a single key listener
14184 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14185 * following options:
14186 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14187 * @param {Function} fn The function to call
14188 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
14190 on : function(key, fn, scope){
14191 var keyCode, shift, ctrl, alt;
14192 if(typeof key == "object" && !Ext.isArray(key)){
14211 handleKeyDown : function(e){
14212 if(this.enabled){ //just in case
14213 var b = this.bindings;
14214 for(var i = 0, len = b.length; i < len; i++){
14215 b[i].call(this, e);
14221 * Returns true if this KeyMap is enabled
14222 * @return {Boolean}
14224 isEnabled : function(){
14225 return this.enabled;
14229 * Enables this KeyMap
14231 enable: function(){
14233 this.el.on(this.eventName, this.handleKeyDown, this);
14234 this.enabled = true;
14239 * Disable this KeyMap
14241 disable: function(){
14243 this.el.removeListener(this.eventName, this.handleKeyDown, this);
14244 this.enabled = false;
14249 * Convenience function for setting disabled/enabled by boolean.
14250 * @param {Boolean} disabled
14252 setDisabled : function(disabled){
14253 this[disabled ? "disable" : "enable"]();
14256 * @class Ext.util.TextMetrics
14257 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14258 * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
14259 * should not contain any HTML, otherwise it may not be measured correctly.
14262 Ext.util.TextMetrics = function(){
14266 * Measures the size of the specified text
14267 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14268 * that can affect the size of the rendered text
14269 * @param {String} text The text to measure
14270 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14271 * in order to accurately measure the text height
14272 * @return {Object} An object containing the text's size {width: (width), height: (height)}
14274 measure : function(el, text, fixedWidth){
14276 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
14279 shared.setFixedWidth(fixedWidth || 'auto');
14280 return shared.getSize(text);
14284 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
14285 * the overhead of multiple calls to initialize the style properties on each measurement.
14286 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14287 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14288 * in order to accurately measure the text height
14289 * @return {Ext.util.TextMetrics.Instance} instance The new instance
14291 createInstance : function(el, fixedWidth){
14292 return Ext.util.TextMetrics.Instance(el, fixedWidth);
14297 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14298 var ml = new Ext.Element(document.createElement('div'));
14299 document.body.appendChild(ml.dom);
14300 ml.position('absolute');
14301 ml.setLeftTop(-1000, -1000);
14305 ml.setWidth(fixedWidth);
14310 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14311 * Returns the size of the specified text based on the internal element's style and width properties
14312 * @param {String} text The text to measure
14313 * @return {Object} An object containing the text's size {width: (width), height: (height)}
14315 getSize : function(text){
14317 var s = ml.getSize();
14323 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14324 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14325 * that can affect the size of the rendered text
14326 * @param {String/HTMLElement} el The element, dom node or id
14328 bind : function(el){
14330 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
14335 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14336 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
14337 * to set a fixed width in order to accurately measure the text height.
14338 * @param {Number} width The width to set on the element
14340 setFixedWidth : function(width){
14341 ml.setWidth(width);
14345 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14346 * Returns the measured width of the specified text
14347 * @param {String} text The text to measure
14348 * @return {Number} width The width in pixels
14350 getWidth : function(text){
14351 ml.dom.style.width = 'auto';
14352 return this.getSize(text).width;
14356 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14357 * Returns the measured height of the specified text. For multiline text, be sure to call
14358 * {@link #setFixedWidth} if necessary.
14359 * @param {String} text The text to measure
14360 * @return {Number} height The height in pixels
14362 getHeight : function(text){
14363 return this.getSize(text).height;
14367 instance.bind(bindTo);
14372 Ext.Element.addMethods({
14374 * Returns the width in pixels of the passed text, or the width of the text in this Element.
14375 * @param {String} text The text to measure. Defaults to the innerHTML of the element.
14376 * @param {Number} min (Optional) The minumum value to return.
14377 * @param {Number} max (Optional) The maximum value to return.
14378 * @return {Number} The text width in pixels.
14379 * @member Ext.Element getTextWidth
14381 getTextWidth : function(text, min, max){
14382 return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
14386 * @class Ext.util.Cookies
14387 * Utility class for managing and interacting with cookies.
14390 Ext.util.Cookies = {
14392 * Create a cookie with the specified name and value. Additional settings
14393 * for the cookie may be optionally specified (for example: expiration,
14394 * access restriction, SSL).
14395 * @param {String} name The name of the cookie to set.
14396 * @param {Mixed} value The value to set for the cookie.
14397 * @param {Object} expires (Optional) Specify an expiration date the
14398 * cookie is to persist until. Note that the specified Date object will
14399 * be converted to Greenwich Mean Time (GMT).
14400 * @param {String} path (Optional) Setting a path on the cookie restricts
14401 * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>).
14402 * @param {String} domain (Optional) Setting a domain restricts access to
14403 * pages on a given domain (typically used to allow cookie access across
14404 * subdomains). For example, "extjs.com" will create a cookie that can be
14405 * accessed from any subdomain of extjs.com, including www.extjs.com,
14406 * support.extjs.com, etc.
14407 * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
14408 * should only be accessible via SSL on a page using the HTTPS protocol.
14409 * Defaults to <tt>false</tt>. Note that this will only work if the page
14410 * calling this code uses the HTTPS protocol, otherwise the cookie will be
14411 * created with default options.
14413 set : function(name, value){
14414 var argv = arguments;
14415 var argc = arguments.length;
14416 var expires = (argc > 2) ? argv[2] : null;
14417 var path = (argc > 3) ? argv[3] : '/';
14418 var domain = (argc > 4) ? argv[4] : null;
14419 var secure = (argc > 5) ? argv[5] : false;
14420 document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
14424 * Retrieves cookies that are accessible by the current page. If a cookie
14425 * does not exist, <code>get()</code> returns <tt>null</tt>. The following
14426 * example retrieves the cookie called "valid" and stores the String value
14427 * in the variable <tt>validStatus</tt>.
14429 * var validStatus = Ext.util.Cookies.get("valid");
14431 * @param {String} name The name of the cookie to get
14432 * @return {Mixed} Returns the cookie value for the specified name;
14433 * null if the cookie name does not exist.
14435 get : function(name){
14436 var arg = name + "=";
14437 var alen = arg.length;
14438 var clen = document.cookie.length;
14443 if(document.cookie.substring(i, j) == arg){
14444 return Ext.util.Cookies.getCookieVal(j);
14446 i = document.cookie.indexOf(" ", i) + 1;
14455 * Removes a cookie with the provided name from the browser
14456 * if found by setting its expiration date to sometime in the past.
14457 * @param {String} name The name of the cookie to remove
14459 clear : function(name){
14460 if(Ext.util.Cookies.get(name)){
14461 document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
14467 getCookieVal : function(offset){
14468 var endstr = document.cookie.indexOf(";", offset);
14470 endstr = document.cookie.length;
14472 return unescape(document.cookie.substring(offset, endstr));
14475 * Framework-wide error-handler. Developers can override this method to provide
14476 * custom exception-handling. Framework errors will often extend from the base
14478 * @param {Object/Error} e The thrown exception object.
14480 Ext.handleError = function(e) {
14487 * <p>A base error class. Future implementations are intended to provide more
14488 * robust error handling throughout the framework (<b>in the debug build only</b>)
14489 * to check for common errors and problems. The messages issued by this class
14490 * will aid error checking. Error checks will be automatically removed in the
14491 * production build so that performance is not negatively impacted.</p>
14492 * <p>Some sample messages currently implemented:</p><pre>
14493 "DataProxy attempted to execute an API-action but found an undefined
14494 url / function. Please review your Proxy url/api-configuration."
14496 "Could not locate your "root" property in your server response.
14497 Please review your JsonReader config to ensure the config-property
14498 "root" matches the property your server-response. See the JsonReader
14499 docs for additional assistance."
14501 * <p>An example of the code used for generating error messages:</p><pre><code>
14510 function generateError(data) {
14511 throw new Ext.Error('foo-error', data);
14514 * @param {String} message
14516 Ext.Error = function(message) {
14517 // Try to read the message from Ext.Error.lang
14518 this.message = (this.lang[message]) ? this.lang[message] : message;
14521 Ext.Error.prototype = new Error();
14522 Ext.apply(Ext.Error.prototype, {
14523 // protected. Extensions place their error-strings here.
14531 getName : function() {
14538 getMessage : function() {
14539 return this.message;
14545 toJson : function() {
14546 return Ext.encode(this);