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,
140 // kill repeat to save bytes
141 afterbegin = 'afterbegin',
142 afterend = 'afterend',
143 beforebegin = 'beforebegin',
144 beforeend = 'beforeend',
153 function doInsert(el, o, returnElement, pos, sibling, append){
154 var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));
155 return returnElement ? Ext.get(newNode, true) : newNode;
158 // build as innerHTML where available
159 function createHtml(o){
169 } else if (Ext.isArray(o)) {
170 for (var i=0; i < o.length; i++) {
172 b += createHtml(o[i]);
176 b += '<' + (o.tag = o.tag || 'div');
177 Ext.iterate(o, function(attr, val){
178 if(!/tag|children|cn|html$/i.test(attr)){
179 if (Ext.isObject(val)) {
180 b += ' ' + attr + '="';
181 Ext.iterate(val, function(key, keyVal){
182 b += key + ':' + keyVal + ';';
186 b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
190 // Now either just close the tag or try to add children and close the tag.
191 if (emptyTags.test(o.tag)) {
195 if ((cn = o.children || o.cn)) {
200 b += '</' + o.tag + '>';
206 function ieTable(depth, s, h, e){
207 tempTableEl.innerHTML = [s, h, e].join('');
214 // If the result is multiple siblings, then encapsulate them into one fragment.
215 if(ns = el.nextSibling){
216 var df = document.createDocumentFragment();
229 * Nasty code for IE's broken table implementation
231 function insertIntoTable(tag, where, el, html) {
235 tempTableEl = tempTableEl || document.createElement('div');
237 if(tag == 'td' && (where == afterbegin || where == beforeend) ||
238 !/td|tr|tbody/i.test(tag) && (where == beforebegin || where == afterend)) {
241 before = where == beforebegin ? el :
242 where == afterend ? el.nextSibling :
243 where == afterbegin ? el.firstChild : null;
245 if (where == beforebegin || where == afterend) {
249 if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
250 node = ieTable(4, trs, html, tre);
251 } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
252 (tag == 'tr' && (where == beforebegin || where == afterend))) {
253 node = ieTable(3, tbs, html, tbe);
255 node = ieTable(2, ts, html, te);
257 el.insertBefore(node, before);
264 * Returns the markup for the passed Element(s) config.
265 * @param {Object} o The DOM object spec (and children)
268 markup : function(o){
269 return createHtml(o);
273 * Applies a style specification to an element.
274 * @param {String/HTMLElement} el The element to apply styles to
275 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
276 * a function which returns such a specification.
278 applyStyles : function(el, styles){
285 if(Ext.isFunction(styles)){
286 styles = styles.call();
288 if(Ext.isString(styles)){
289 styles = styles.trim().split(/\s*(?::|;)\s*/);
290 for(len = styles.length; i < len;){
291 el.setStyle(styles[i++], styles[i++]);
293 }else if (Ext.isObject(styles)){
300 * Inserts an HTML fragment into the DOM.
301 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
302 * @param {HTMLElement} el The context element
303 * @param {String} html The HTML fragment
304 * @return {HTMLElement} The new node
306 insertHtml : function(where, el, html){
315 where = where.toLowerCase();
316 // add these here because they are used in both branches of the condition.
317 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
318 hash[afterend] = ['AfterEnd', 'nextSibling'];
320 if (el.insertAdjacentHTML) {
321 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
324 // add these two to the hash.
325 hash[afterbegin] = ['AfterBegin', 'firstChild'];
326 hash[beforeend] = ['BeforeEnd', 'lastChild'];
327 if ((hashVal = hash[where])) {
328 el.insertAdjacentHTML(hashVal[0], html);
329 return el[hashVal[1]];
332 range = el.ownerDocument.createRange();
333 setStart = 'setStart' + (/end/i.test(where) ? 'After' : 'Before');
336 frag = range.createContextualFragment(html);
337 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
338 return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
340 rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
342 range[setStart](el[rangeEl]);
343 frag = range.createContextualFragment(html);
344 if(where == afterbegin){
345 el.insertBefore(frag, el.firstChild);
347 el.appendChild(frag);
355 throw 'Illegal insertion point -> "' + where + '"';
359 * Creates new DOM element(s) and inserts them before el.
360 * @param {Mixed} el The context element
361 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
362 * @param {Boolean} returnElement (optional) true to return a Ext.Element
363 * @return {HTMLElement/Ext.Element} The new node
365 insertBefore : function(el, o, returnElement){
366 return doInsert(el, o, returnElement, beforebegin);
370 * Creates new DOM element(s) and inserts them after el.
371 * @param {Mixed} el The context element
372 * @param {Object} o The DOM object spec (and children)
373 * @param {Boolean} returnElement (optional) true to return a Ext.Element
374 * @return {HTMLElement/Ext.Element} The new node
376 insertAfter : function(el, o, returnElement){
377 return doInsert(el, o, returnElement, afterend, 'nextSibling');
381 * Creates new DOM element(s) and inserts them as the first child of el.
382 * @param {Mixed} el The context element
383 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
384 * @param {Boolean} returnElement (optional) true to return a Ext.Element
385 * @return {HTMLElement/Ext.Element} The new node
387 insertFirst : function(el, o, returnElement){
388 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
392 * Creates new DOM element(s) and appends them to el.
393 * @param {Mixed} el The context element
394 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
395 * @param {Boolean} returnElement (optional) true to return a Ext.Element
396 * @return {HTMLElement/Ext.Element} The new node
398 append : function(el, o, returnElement){
399 return doInsert(el, o, returnElement, beforeend, '', true);
403 * Creates new DOM element(s) and overwrites the contents of el with them.
404 * @param {Mixed} el The context element
405 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
406 * @param {Boolean} returnElement (optional) true to return a Ext.Element
407 * @return {HTMLElement/Ext.Element} The new node
409 overwrite : function(el, o, returnElement){
411 el.innerHTML = createHtml(o);
412 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
415 createHtml : createHtml
419 * @class Ext.DomHelper
421 Ext.apply(Ext.DomHelper,
424 afterbegin = 'afterbegin',
425 afterend = 'afterend',
426 beforebegin = 'beforebegin',
427 beforeend = 'beforeend';
430 function doInsert(el, o, returnElement, pos, sibling, append){
434 newNode = createDom(o, null);
436 el.appendChild(newNode);
438 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
441 newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
443 return returnElement ? Ext.get(newNode, true) : newNode;
448 function createDom(o, parentNode){
456 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
457 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
458 Ext.each(o, function(v) {
461 } else if (Ext.isString(o)) { // Allow a string as a child spec.
462 el = doc.createTextNode(o);
464 el = doc.createElement( o.tag || 'div' );
465 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
466 Ext.iterate(o, function(attr, val){
467 if(!/tag|children|cn|html|style/.test(attr)){
472 el.setAttribute(attr, val);
479 Ext.DomHelper.applyStyles(el, o.style);
481 if ((cn = o.children || o.cn)) {
484 el.innerHTML = o.html;
488 parentNode.appendChild(el);
495 * Creates a new Ext.Template from the DOM object spec.
496 * @param {Object} o The DOM object spec (and children)
497 * @return {Ext.Template} The new template
499 createTemplate : function(o){
500 var html = Ext.DomHelper.createHtml(o);
501 return new Ext.Template(html);
504 /** True to force the use of DOM instead of html fragments @type Boolean */
508 * Creates new DOM element(s) and inserts them before el.
509 * @param {Mixed} el The context element
510 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
511 * @param {Boolean} returnElement (optional) true to return a Ext.Element
512 * @return {HTMLElement/Ext.Element} The new node
515 insertBefore : function(el, o, returnElement){
516 return doInsert(el, o, returnElement, beforebegin);
520 * Creates new DOM element(s) and inserts them after el.
521 * @param {Mixed} el The context element
522 * @param {Object} o The DOM object spec (and children)
523 * @param {Boolean} returnElement (optional) true to return a Ext.Element
524 * @return {HTMLElement/Ext.Element} The new node
527 insertAfter : function(el, o, returnElement){
528 return doInsert(el, o, returnElement, afterend, 'nextSibling');
532 * Creates new DOM element(s) and inserts them as the first child of el.
533 * @param {Mixed} el The context element
534 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
535 * @param {Boolean} returnElement (optional) true to return a Ext.Element
536 * @return {HTMLElement/Ext.Element} The new node
539 insertFirst : function(el, o, returnElement){
540 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
544 * Creates new DOM element(s) and appends them to el.
545 * @param {Mixed} el The context element
546 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
547 * @param {Boolean} returnElement (optional) true to return a Ext.Element
548 * @return {HTMLElement/Ext.Element} The new node
551 append: function(el, o, returnElement){
552 return doInsert(el, o, returnElement, beforeend, '', true);
556 * Creates new DOM element(s) without inserting them to the document.
557 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
558 * @return {HTMLElement} The new uninserted node
564 * @class Ext.Template
565 * <p>Represents an HTML fragment template. Templates may be {@link #compile precompiled}
566 * for greater performance.</p>
567 * <p>For example usage {@link #Template see the constructor}.</p>
570 * An instance of this class may be created by passing to the constructor either
571 * a single argument, or multiple arguments:
572 * <div class="mdetail-params"><ul>
573 * <li><b>single argument</b> : String/Array
574 * <div class="sub-desc">
575 * The single argument may be either a String or an Array:<ul>
576 * <li><tt>String</tt> : </li><pre><code>
577 var t = new Ext.Template("<div>Hello {0}.</div>");
578 t.{@link #append}('some-element', ['foo']);
580 * <li><tt>Array</tt> : </li>
581 * An Array will be combined with <code>join('')</code>.
583 var t = new Ext.Template([
584 '<div name="{id}">',
585 '<span class="{cls}">{name:trim} {value:ellipsis(10)}</span>',
588 t.{@link #compile}();
589 t.{@link #append}('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
592 * <li><b>multiple arguments</b> : String, Object, Array, ...
593 * <div class="sub-desc">
594 * Multiple arguments will be combined with <code>join('')</code>.
596 var t = new Ext.Template(
597 '<div name="{id}">',
598 '<span class="{cls}">{name} {value}</span>',
600 // a configuration object:
602 compiled: true, // {@link #compile} immediately
603 disableFormats: true // See Notes below.
607 * <p><b>Notes</b>:</p>
608 * <div class="mdetail-params"><ul>
609 * <li>Formatting and <code>disableFormats</code> are not applicable for Ext Core.</li>
610 * <li>For a list of available format functions, see {@link Ext.util.Format}.</li>
611 * <li><code>disableFormats</code> reduces <code>{@link #apply}</code> time
612 * when no formatting is required.</li>
616 * @param {Mixed} config
618 Ext.Template = function(html){
623 if (Ext.isArray(html)) {
624 html = html.join("");
625 } else if (a.length > 1) {
626 Ext.each(a, function(v) {
627 if (Ext.isObject(v)) {
639 * @cfg {Boolean} compiled Specify <tt>true</tt> to compile the template
640 * immediately (see <code>{@link #compile}</code>).
641 * Defaults to <tt>false</tt>.
647 Ext.Template.prototype = {
649 * @cfg {RegExp} re The regular expression used to match template variables.
650 * Defaults to:<pre><code>
651 * re : /\{([\w-]+)\}/g // for Ext Core
652 * re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g // for Ext JS
655 re : /\{([\w-]+)\}/g,
657 * See <code>{@link #re}</code>.
663 * Returns an HTML fragment of this template with the specified <code>values</code> applied.
664 * @param {Object/Array} values
665 * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
666 * or an object (i.e. <code>{foo: 'bar'}</code>).
667 * @return {String} The HTML fragment
669 applyTemplate : function(values){
673 me.compiled(values) :
674 me.html.replace(me.re, function(m, name){
675 return values[name] !== undefined ? values[name] : "";
680 * Sets the HTML used as the template and optionally compiles it.
681 * @param {String} html
682 * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
683 * @return {Ext.Template} this
685 set : function(html, compile){
689 return compile ? me.compile() : me;
693 * Compiles the template into an internal function, eliminating the RegEx overhead.
694 * @return {Ext.Template} this
696 compile : function(){
698 sep = Ext.isGecko ? "+" : ",";
700 function fn(m, name){
701 name = "values['" + name + "']";
702 return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
705 eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
706 me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
707 (Ext.isGecko ? "';};" : "'].join('');};"));
712 * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
713 * @param {Mixed} el The context element
714 * @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'})
715 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
716 * @return {HTMLElement/Ext.Element} The new node or Element
718 insertFirst: function(el, values, returnElement){
719 return this.doInsert('afterBegin', el, values, returnElement);
723 * Applies the supplied values to the template and inserts the new node(s) before el.
724 * @param {Mixed} el The context element
725 * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
726 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
727 * @return {HTMLElement/Ext.Element} The new node or Element
729 insertBefore: function(el, values, returnElement){
730 return this.doInsert('beforeBegin', el, values, returnElement);
734 * Applies the supplied values to the template and inserts the new node(s) after el.
735 * @param {Mixed} el The context element
736 * @param {Object/Array} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
737 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
738 * @return {HTMLElement/Ext.Element} The new node or Element
740 insertAfter : function(el, values, returnElement){
741 return this.doInsert('afterEnd', el, values, returnElement);
745 * Applies the supplied <code>values</code> to the template and appends
746 * the new node(s) to the specified <code>el</code>.
747 * <p>For example usage {@link #Template see the constructor}.</p>
748 * @param {Mixed} el The context element
749 * @param {Object/Array} values
750 * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
751 * or an object (i.e. <code>{foo: 'bar'}</code>).
752 * @param {Boolean} returnElement (optional) true to return an Ext.Element (defaults to undefined)
753 * @return {HTMLElement/Ext.Element} The new node or Element
755 append : function(el, values, returnElement){
756 return this.doInsert('beforeEnd', el, values, returnElement);
759 doInsert : function(where, el, values, returnEl){
761 var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
762 return returnEl ? Ext.get(newNode, true) : newNode;
766 * Applies the supplied values to the template and overwrites the content of el with the new node(s).
767 * @param {Mixed} el The context element
768 * @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'})
769 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
770 * @return {HTMLElement/Ext.Element} The new node or Element
772 overwrite : function(el, values, returnElement){
774 el.innerHTML = this.applyTemplate(values);
775 return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
779 * Alias for {@link #applyTemplate}
780 * Returns an HTML fragment of this template with the specified <code>values</code> applied.
781 * @param {Object/Array} values
782 * The template values. Can be an array if the params are numeric (i.e. <code>{0}</code>)
783 * or an object (i.e. <code>{foo: 'bar'}</code>).
784 * @return {String} The HTML fragment
785 * @member Ext.Template
788 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
791 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
792 * @param {String/HTMLElement} el A DOM element or its id
793 * @param {Object} config A configuration object
794 * @return {Ext.Template} The created template
797 Ext.Template.from = function(el, config){
799 return new Ext.Template(el.value || el.innerHTML, config || '');
801 * @class Ext.Template
803 Ext.apply(Ext.Template.prototype, {
805 * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format
806 * functions in the template. If the template does not contain
807 * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>
808 * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.
810 var t = new Ext.Template(
811 '<div name="{id}">',
812 '<span class="{cls}">{name} {value}</span>',
815 compiled: true, // {@link #compile} immediately
816 disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting
820 * For a list of available format functions, see {@link Ext.util.Format}.
822 disableFormats : false,
824 * See <code>{@link #disableFormats}</code>.
826 * @property disableFormats
830 * The regular expression used to match template variables
835 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
838 * Returns an HTML fragment of this template with the specified values applied.
839 * @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'})
840 * @return {String} The HTML fragment
843 applyTemplate : function(values){
845 useF = me.disableFormats !== true,
846 fm = Ext.util.Format,
850 return me.compiled(values);
852 function fn(m, name, format, args){
853 if (format && useF) {
854 if (format.substr(0, 5) == "this.") {
855 return tpl.call(format.substr(5), values[name], values);
858 // quoted values are required for strings in compiled templates,
859 // but for non compiled we need to strip them
860 // quoted reversed for jsmin
861 var re = /^\s*['"](.*)["']\s*$/;
862 args = args.split(',');
863 for(var i = 0, len = args.length; i < len; i++){
864 args[i] = args[i].replace(re, "$1");
866 args = [values[name]].concat(args);
868 args = [values[name]];
870 return fm[format].apply(fm, args);
873 return values[name] !== undefined ? values[name] : "";
876 return me.html.replace(me.re, fn);
880 * Compiles the template into an internal function, eliminating the RegEx overhead.
881 * @return {Ext.Template} this
884 compile : function(){
886 fm = Ext.util.Format,
887 useF = me.disableFormats !== true,
888 sep = Ext.isGecko ? "+" : ",",
891 function fn(m, name, format, args){
893 args = args ? ',' + args : "";
894 if(format.substr(0, 5) != "this."){
895 format = "fm." + format + '(';
897 format = 'this.call("'+ format.substr(5) + '", ';
901 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
903 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
906 // branched to use + in gecko and [].join() in others
908 body = "this.compiled = function(values){ return '" +
909 me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
912 body = ["this.compiled = function(values){ return ['"];
913 body.push(me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
914 body.push("'].join('');};");
915 body = body.join('');
921 // private function used to call members
922 call : function(fnName, value, allValues){
923 return this[fnName](value, allValues);
926 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate; /*
927 * This is code is also distributed under MIT license for use
928 * with jQuery and prototype JavaScript libraries.
931 * @class Ext.DomQuery
932 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).
934 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>
937 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.
939 <h4>Element Selectors:</h4>
941 <li> <b>*</b> any element</li>
942 <li> <b>E</b> an element with the tag E</li>
943 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
944 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
945 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
946 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
948 <h4>Attribute Selectors:</h4>
949 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
951 <li> <b>E[foo]</b> has an attribute "foo"</li>
952 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
953 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
954 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
955 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
956 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
957 <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
959 <h4>Pseudo Classes:</h4>
961 <li> <b>E:first-child</b> E is the first child of its parent</li>
962 <li> <b>E:last-child</b> E is the last child of its parent</li>
963 <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>
964 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
965 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
966 <li> <b>E:only-child</b> E is the only child of its parent</li>
967 <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>
968 <li> <b>E:first</b> the first E in the resultset</li>
969 <li> <b>E:last</b> the last E in the resultset</li>
970 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
971 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
972 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
973 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
974 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
975 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
976 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
977 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
978 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
979 <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
981 <h4>CSS Value Selectors:</h4>
983 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
984 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
985 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
986 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
987 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
988 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
992 Ext.DomQuery = function(){
997 trimRe = /^\s+|\s+$/g,
998 tplRe = /\{(\d+)\}/g,
999 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
1000 tagTokenRe = /^(#)?([\w-\*]+)/,
1001 nthRe = /(\d*)n\+?(\d*)/,
1003 // This is for IE MSXML which does not support expandos.
1004 // IE runs the same speed using setAttribute, however FF slows way down
1005 // and Safari completely fails so they need to continue to use expandos.
1006 isIE = window.ActiveXObject ? true : false,
1009 // this eval is stop the compressor from
1010 // renaming the variable to something shorter
1011 eval("var batch = 30803;");
1013 // Retrieve the child node from a particular
1014 // parent at the specified index.
1015 function child(parent, index){
1017 n = parent.firstChild;
1019 if(n.nodeType == 1){
1029 // retrieve the next element node
1031 while((n = n.nextSibling) && n.nodeType != 1);
1035 // retrieve the previous element node
1037 while((n = n.previousSibling) && n.nodeType != 1);
1041 // Mark each child node with a nodeIndex skipping and
1042 // removing empty text nodes.
1043 function children(parent){
1044 var n = parent.firstChild,
1048 nextNode = n.nextSibling;
1049 // clean worthless empty nodes.
1050 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
1051 parent.removeChild(n);
1053 // add an expando nodeIndex
1054 n.nodeIndex = ++nodeIndex;
1062 // nodeSet - array of nodes
1064 function byClassName(nodeSet, cls){
1068 var result = [], ri = -1;
1069 for(var i = 0, ci; ci = nodeSet[i]; i++){
1070 if((' '+ci.className+' ').indexOf(cls) != -1){
1077 function attrValue(n, attr){
1078 // if its an array, use the first node.
1079 if(!n.tagName && typeof n.length != "undefined"){
1089 if(attr == "class" || attr == "className"){
1092 return n.getAttribute(attr) || n[attr];
1098 // mode - false, /, >, +, ~
1099 // tagName - defaults to "*"
1100 function getNodes(ns, mode, tagName){
1101 var result = [], ri = -1, cs;
1105 tagName = tagName || "*";
1107 if(typeof ns.getElementsByTagName != "undefined"){
1111 // no mode specified, grab all elements by tagName
1114 for(var i = 0, ni; ni = ns[i]; i++){
1115 cs = ni.getElementsByTagName(tagName);
1116 for(var j = 0, ci; ci = cs[j]; j++){
1120 // Direct Child mode (/ or >)
1121 // E > F or E/F all direct children elements of E that have the tag
1122 } else if(mode == "/" || mode == ">"){
1123 var utag = tagName.toUpperCase();
1124 for(var i = 0, ni, cn; ni = ns[i]; i++){
1126 for(var j = 0, cj; cj = cn[j]; j++){
1127 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
1132 // Immediately Preceding mode (+)
1133 // E + F all elements with the tag F that are immediately preceded by an element with the tag E
1134 }else if(mode == "+"){
1135 var utag = tagName.toUpperCase();
1136 for(var i = 0, n; n = ns[i]; i++){
1137 while((n = n.nextSibling) && n.nodeType != 1);
1138 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
1143 // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
1144 }else if(mode == "~"){
1145 var utag = tagName.toUpperCase();
1146 for(var i = 0, n; n = ns[i]; i++){
1147 while((n = n.nextSibling)){
1148 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
1157 function concat(a, b){
1161 for(var i = 0, l = b.length; i < l; i++){
1167 function byTag(cs, tagName){
1168 if(cs.tagName || cs == document){
1174 var result = [], ri = -1;
1175 tagName = tagName.toLowerCase();
1176 for(var i = 0, ci; ci = cs[i]; i++){
1177 if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
1184 function byId(cs, id){
1185 if(cs.tagName || cs == document){
1191 var result = [], ri = -1;
1192 for(var i = 0, ci; ci = cs[i]; i++){
1193 if(ci && ci.id == id){
1201 // operators are =, !=, ^=, $=, *=, %=, |= and ~=
1202 // custom can be "{"
1203 function byAttribute(cs, attr, value, op, custom){
1206 useGetStyle = custom == "{",
1207 fn = Ext.DomQuery.operators[op],
1210 for(var i = 0, ci; ci = cs[i]; i++){
1211 // skip non-element nodes.
1212 if(ci.nodeType != 1){
1216 innerHTML = ci.innerHTML;
1217 // we only need to change the property names if we're dealing with html nodes, not XML
1218 if(innerHTML !== null && innerHTML !== undefined){
1220 a = Ext.DomQuery.getStyle(ci, attr);
1221 } else if (attr == "class" || attr == "className"){
1223 } else if (attr == "for"){
1225 } else if (attr == "href"){
1226 // getAttribute href bug
1227 // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
1228 a = ci.getAttribute("href", 2);
1230 a = ci.getAttribute(attr);
1233 a = ci.getAttribute(attr);
1235 if((fn && fn(a, value)) || (!fn && a)){
1242 function byPseudo(cs, name, value){
1243 return Ext.DomQuery.pseudos[name](cs, value);
1246 function nodupIEXml(cs){
1249 cs[0].setAttribute("_nodup", d);
1251 for(var i = 1, len = cs.length; i < len; i++){
1253 if(!c.getAttribute("_nodup") != d){
1254 c.setAttribute("_nodup", d);
1258 for(var i = 0, len = cs.length; i < len; i++){
1259 cs[i].removeAttribute("_nodup");
1268 var len = cs.length, c, i, r = cs, cj, ri = -1;
1269 if(!len || typeof cs.nodeType != "undefined" || len == 1){
1272 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
1273 return nodupIEXml(cs);
1277 for(i = 1; c = cs[i]; i++){
1282 for(var j = 0; j < i; j++){
1285 for(j = i+1; cj = cs[j]; j++){
1297 function quickDiffIEXml(c1, c2){
1300 for(var i = 0, len = c1.length; i < len; i++){
1301 c1[i].setAttribute("_qdiff", d);
1303 for(var i = 0, len = c2.length; i < len; i++){
1304 if(c2[i].getAttribute("_qdiff") != d){
1305 r[r.length] = c2[i];
1308 for(var i = 0, len = c1.length; i < len; i++){
1309 c1[i].removeAttribute("_qdiff");
1314 function quickDiff(c1, c2){
1315 var len1 = c1.length,
1321 if(isIE && typeof c1[0].selectSingleNode != "undefined"){
1322 return quickDiffIEXml(c1, c2);
1324 for(var i = 0; i < len1; i++){
1327 for(var i = 0, len = c2.length; i < len; i++){
1328 if(c2[i]._qdiff != d){
1329 r[r.length] = c2[i];
1335 function quickId(ns, mode, root, id){
1337 var d = root.ownerDocument || root;
1338 return d.getElementById(id);
1340 ns = getNodes(ns, mode, "*");
1341 return byId(ns, id);
1345 getStyle : function(el, name){
1346 return Ext.fly(el).getStyle(name);
1349 * Compiles a selector/xpath query into a reusable function. The returned function
1350 * takes one parameter "root" (optional), which is the context node from where the query should start.
1351 * @param {String} selector The selector/xpath query
1352 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
1353 * @return {Function}
1355 compile : function(path, type){
1356 type = type || "select";
1358 // setup fn preamble
1359 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
1362 matchers = Ext.DomQuery.matchers,
1363 matchersLn = matchers.length,
1365 // accept leading mode switch
1366 lmode = path.match(modeRe);
1368 if(lmode && lmode[1]){
1369 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
1370 path = path.replace(lmode[1], "");
1373 // strip leading slashes
1374 while(path.substr(0, 1)=="/"){
1375 path = path.substr(1);
1378 while(path && lastPath != path){
1380 var tokenMatch = path.match(tagTokenRe);
1381 if(type == "select"){
1384 if(tokenMatch[1] == "#"){
1385 fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
1387 fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
1389 path = path.replace(tokenMatch[0], "");
1390 }else if(path.substr(0, 1) != '@'){
1391 fn[fn.length] = 'n = getNodes(n, mode, "*");';
1396 if(tokenMatch[1] == "#"){
1397 fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
1399 fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
1401 path = path.replace(tokenMatch[0], "");
1404 while(!(modeMatch = path.match(modeRe))){
1405 var matched = false;
1406 for(var j = 0; j < matchersLn; j++){
1407 var t = matchers[j];
1408 var m = path.match(t.re);
1410 fn[fn.length] = t.select.replace(tplRe, function(x, i){
1413 path = path.replace(m[0], "");
1418 // prevent infinite loop on bad selector
1420 throw 'Error parsing selector, parsing failed at "' + path + '"';
1424 fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
1425 path = path.replace(modeMatch[1], "");
1429 fn[fn.length] = "return nodup(n);\n}";
1431 // eval fn and return it
1437 * Selects a group of elements.
1438 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
1439 * @param {Node/String} root (optional) The start of the query (defaults to document).
1440 * @return {Array} An Array of DOM elements which match the selector. If there are
1441 * no matches, and empty Array is returned.
1443 jsSelect: function(path, root, type){
1444 // set root to doc if not specified.
1445 root = root || document;
1447 if(typeof root == "string"){
1448 root = document.getElementById(root);
1450 var paths = path.split(","),
1453 // loop over each selector
1454 for(var i = 0, len = paths.length; i < len; i++){
1455 var subPath = paths[i].replace(trimRe, "");
1456 // compile and place in cache
1457 if(!cache[subPath]){
1458 cache[subPath] = Ext.DomQuery.compile(subPath);
1459 if(!cache[subPath]){
1460 throw subPath + " is not a valid selector";
1463 var result = cache[subPath](root);
1464 if(result && result != document){
1465 results = results.concat(result);
1469 // if there were multiple selectors, make sure dups
1471 if(paths.length > 1){
1472 return nodup(results);
1476 isXml: function(el) {
1477 var docEl = (el ? el.ownerDocument || el : 0).documentElement;
1478 return docEl ? docEl.nodeName !== "HTML" : false;
1480 select : document.querySelectorAll ? function(path, root, type) {
1481 root = root || document;
1482 if (!Ext.DomQuery.isXml(root)) {
1484 var cs = root.querySelectorAll(path);
1485 return Ext.toArray(cs);
1489 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1490 } : function(path, root, type) {
1491 return Ext.DomQuery.jsSelect.call(this, path, root, type);
1495 * Selects a single element.
1496 * @param {String} selector The selector/xpath query
1497 * @param {Node} root (optional) The start of the query (defaults to document).
1498 * @return {Element} The DOM element which matched the selector.
1500 selectNode : function(path, root){
1501 return Ext.DomQuery.select(path, root)[0];
1505 * Selects the value of a node, optionally replacing null with the defaultValue.
1506 * @param {String} selector The selector/xpath query
1507 * @param {Node} root (optional) The start of the query (defaults to document).
1508 * @param {String} defaultValue
1511 selectValue : function(path, root, defaultValue){
1512 path = path.replace(trimRe, "");
1513 if(!valueCache[path]){
1514 valueCache[path] = Ext.DomQuery.compile(path, "select");
1516 var n = valueCache[path](root), v;
1517 n = n[0] ? n[0] : n;
1519 // overcome a limitation of maximum textnode size
1520 // Rumored to potentially crash IE6 but has not been confirmed.
1521 // http://reference.sitepoint.com/javascript/Node/normalize
1522 // https://developer.mozilla.org/En/DOM/Node.normalize
1523 if (typeof n.normalize == 'function') n.normalize();
1525 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
1526 return ((v === null||v === undefined||v==='') ? defaultValue : v);
1530 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
1531 * @param {String} selector The selector/xpath query
1532 * @param {Node} root (optional) The start of the query (defaults to document).
1533 * @param {Number} defaultValue
1536 selectNumber : function(path, root, defaultValue){
1537 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
1538 return parseFloat(v);
1542 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
1543 * @param {String/HTMLElement/Array} el An element id, element or array of elements
1544 * @param {String} selector The simple selector to test
1547 is : function(el, ss){
1548 if(typeof el == "string"){
1549 el = document.getElementById(el);
1551 var isArray = Ext.isArray(el),
1552 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
1553 return isArray ? (result.length == el.length) : (result.length > 0);
1557 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
1558 * @param {Array} el An array of elements to filter
1559 * @param {String} selector The simple selector to test
1560 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
1561 * the selector instead of the ones that match
1562 * @return {Array} An Array of DOM elements which match the selector. If there are
1563 * no matches, and empty Array is returned.
1565 filter : function(els, ss, nonMatches){
1566 ss = ss.replace(trimRe, "");
1567 if(!simpleCache[ss]){
1568 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
1570 var result = simpleCache[ss](els);
1571 return nonMatches ? quickDiff(result, els) : result;
1575 * Collection of matching regular expressions and code snippets.
1576 * Each capture group within () will be replace the {} in the select
1577 * statement as specified by their index.
1581 select: 'n = byClassName(n, " {1} ");'
1583 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
1584 select: 'n = byPseudo(n, "{1}", "{2}");'
1586 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
1587 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
1590 select: 'n = byId(n, "{1}");'
1593 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
1598 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
1599 * 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, > <.
1602 "=" : function(a, v){
1605 "!=" : function(a, v){
1608 "^=" : function(a, v){
1609 return a && a.substr(0, v.length) == v;
1611 "$=" : function(a, v){
1612 return a && a.substr(a.length-v.length) == v;
1614 "*=" : function(a, v){
1615 return a && a.indexOf(v) !== -1;
1617 "%=" : function(a, v){
1618 return (a % v) == 0;
1620 "|=" : function(a, v){
1621 return a && (a == v || a.substr(0, v.length+1) == v+'-');
1623 "~=" : function(a, v){
1624 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
1629 * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed
1630 * two parameters:</p><div class="mdetail-params"><ul>
1631 * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>
1632 * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>
1634 * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>
1635 * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,
1636 * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>
1637 * <p>For example, to filter <code><a></code> elements to only return links to <i>external</i> resources:</p>
1639 Ext.DomQuery.pseudos.external = function(c, v){
1640 var r = [], ri = -1;
1641 for(var i = 0, ci; ci = c[i]; i++){
1642 // Include in result set only if it's a link to an external resource
1643 if(ci.hostname != location.hostname){
1649 * Then external links could be gathered with the following statement:<code><pre>
1650 var externalLinks = Ext.select("a:external");
1654 "first-child" : function(c){
1655 var r = [], ri = -1, n;
1656 for(var i = 0, ci; ci = n = c[i]; i++){
1657 while((n = n.previousSibling) && n.nodeType != 1);
1665 "last-child" : function(c){
1666 var r = [], ri = -1, n;
1667 for(var i = 0, ci; ci = n = c[i]; i++){
1668 while((n = n.nextSibling) && n.nodeType != 1);
1676 "nth-child" : function(c, a) {
1677 var r = [], ri = -1,
1678 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
1679 f = (m[1] || 1) - 0, l = m[2] - 0;
1680 for(var i = 0, n; n = c[i]; i++){
1681 var pn = n.parentNode;
1682 if (batch != pn._batch) {
1684 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
1685 if(cn.nodeType == 1){
1692 if (l == 0 || n.nodeIndex == l){
1695 } else if ((n.nodeIndex + l) % f == 0){
1703 "only-child" : function(c){
1704 var r = [], ri = -1;;
1705 for(var i = 0, ci; ci = c[i]; i++){
1706 if(!prev(ci) && !next(ci)){
1713 "empty" : function(c){
1714 var r = [], ri = -1;
1715 for(var i = 0, ci; ci = c[i]; i++){
1716 var cns = ci.childNodes, j = 0, cn, empty = true;
1719 if(cn.nodeType == 1 || cn.nodeType == 3){
1731 "contains" : function(c, v){
1732 var r = [], ri = -1;
1733 for(var i = 0, ci; ci = c[i]; i++){
1734 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
1741 "nodeValue" : function(c, v){
1742 var r = [], ri = -1;
1743 for(var i = 0, ci; ci = c[i]; i++){
1744 if(ci.firstChild && ci.firstChild.nodeValue == v){
1751 "checked" : function(c){
1752 var r = [], ri = -1;
1753 for(var i = 0, ci; ci = c[i]; i++){
1754 if(ci.checked == true){
1761 "not" : function(c, ss){
1762 return Ext.DomQuery.filter(c, ss, true);
1765 "any" : function(c, selectors){
1766 var ss = selectors.split('|'),
1768 for(var i = 0, ci; ci = c[i]; i++){
1769 for(var j = 0; s = ss[j]; j++){
1770 if(Ext.DomQuery.is(ci, s)){
1779 "odd" : function(c){
1780 return this["nth-child"](c, "odd");
1783 "even" : function(c){
1784 return this["nth-child"](c, "even");
1787 "nth" : function(c, a){
1788 return c[a-1] || [];
1791 "first" : function(c){
1795 "last" : function(c){
1796 return c[c.length-1] || [];
1799 "has" : function(c, ss){
1800 var s = Ext.DomQuery.select,
1802 for(var i = 0, ci; ci = c[i]; i++){
1803 if(s(ss, ci).length > 0){
1810 "next" : function(c, ss){
1811 var is = Ext.DomQuery.is,
1813 for(var i = 0, ci; ci = c[i]; i++){
1822 "prev" : function(c, ss){
1823 var is = Ext.DomQuery.is,
1825 for(var i = 0, ci; ci = c[i]; i++){
1838 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
1839 * @param {String} path The selector/xpath query
1840 * @param {Node} root (optional) The start of the query (defaults to document).
1845 Ext.query = Ext.DomQuery.select;
1847 * @class Ext.util.DelayedTask
1848 * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
1849 * performing setTimeout where a new timeout cancels the old timeout. When called, the
1850 * task will wait the specified time period before executing. If durng that time period,
1851 * the task is called again, the original call will be cancelled. This continues so that
1852 * the function is only called a single time for each iteration.</p>
1853 * <p>This method is especially useful for things like detecting whether a user has finished
1854 * typing in a text field. An example would be performing validation on a keypress. You can
1855 * use this class to buffer the keypress events for a certain number of milliseconds, and
1856 * perform only if they stop for that amount of time. Usage:</p><pre><code>
1857 var task = new Ext.util.DelayedTask(function(){
1858 alert(Ext.getDom('myInputField').value.length);
1860 // Wait 500ms before calling our function. If the user presses another key
1861 // during that 500ms, it will be cancelled and we'll wait another 500ms.
1862 Ext.get('myInputField').on('keypress', function(){
1863 task.{@link #delay}(500);
1866 * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
1867 * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
1868 * also setup a delayed task for you to buffer events.</p>
1869 * @constructor The parameters to this constructor serve as defaults and are not required.
1870 * @param {Function} fn (optional) The default function to call.
1871 * @param {Object} scope The default scope (The <code><b>this</b></code> reference) in which the
1872 * function is called. If not specified, <code>this</code> will refer to the browser window.
1873 * @param {Array} args (optional) The default Array of arguments.
1875 Ext.util.DelayedTask = function(fn, scope, args){
1881 fn.apply(scope, args || []);
1885 * Cancels any pending timeout and queues a new one
1886 * @param {Number} delay The milliseconds to delay
1887 * @param {Function} newFn (optional) Overrides function passed to constructor
1888 * @param {Object} newScope (optional) Overrides scope passed to constructor. Remember that if no scope
1889 * is specified, <code>this</code> will refer to the browser window.
1890 * @param {Array} newArgs (optional) Overrides args passed to constructor
1892 me.delay = function(delay, newFn, newScope, newArgs){
1895 scope = newScope || scope;
1896 args = newArgs || args;
1897 id = setInterval(call, delay);
1901 * Cancel the last queued timeout
1903 me.cancel = function(){
1911 var EXTUTIL = Ext.util,
1912 TOARRAY = Ext.toArray,
1914 ISOBJECT = Ext.isObject,
1918 * @class Ext.util.Observable
1919 * Base class that provides a common interface for publishing events. Subclasses are expected to
1920 * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1921 * with configured listeners defined.<br>
1924 Employee = Ext.extend(Ext.util.Observable, {
1925 constructor: function(config){
1926 this.name = config.name;
1932 // Copy configured listeners into *this* object so that the base class's
1933 // constructor will add them.
1934 this.listeners = config.listeners;
1936 // Call our superclass constructor to complete construction process.
1937 Employee.superclass.constructor.call(this, config)
1941 * This could then be used like this:<pre><code>
1942 var newEmployee = new Employee({
1946 // By default, "this" will be the object that fired the event.
1947 alert(this.name + " has quit!");
1953 EXTUTIL.Observable = function(){
1955 * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1956 * object during initialization. This should be a valid listeners config object as specified in the
1957 * {@link #addListener} example for attaching multiple handlers at once.</p>
1958 * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1959 * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1960 * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1961 * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1962 * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1963 * has been rendered. A plugin can simplify this step:<pre><code>
1964 // Plugin is configured with a listeners config object.
1965 // The Component is appended to the argument list of all handler functions.
1966 Ext.DomObserver = Ext.extend(Object, {
1967 constructor: function(config) {
1968 this.listeners = config.listeners ? config.listeners : config;
1971 // Component passes itself into plugin's init method
1973 var p, l = this.listeners;
1975 if (Ext.isFunction(l[p])) {
1976 l[p] = this.createHandler(l[p], c);
1978 l[p].fn = this.createHandler(l[p].fn, c);
1982 // Add the listeners to the Element immediately following the render call
1983 c.render = c.render.{@link Function#createSequence createSequence}(function() {
1991 createHandler: function(fn, c) {
1992 return function(e) {
1993 fn.call(this, e, c);
1998 var combo = new Ext.form.ComboBox({
2000 // Collapse combo when its element is clicked on
2001 plugins: [ new Ext.DomObserver({
2002 click: function(evt, comp) {
2009 triggerAction: 'all'
2013 var me = this, e = me.events;
2015 me.on(me.listeners);
2016 delete me.listeners;
2018 me.events = e || {};
2021 EXTUTIL.Observable.prototype = {
2023 filterOptRe : /^(?:scope|delay|buffer|single)$/,
2026 * <p>Fires the specified event with the passed parameters (minus the event name).</p>
2027 * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
2028 * by calling {@link #enableBubble}.</p>
2029 * @param {String} eventName The name of the event to fire.
2030 * @param {Object...} args Variable number of parameters are passed to handlers.
2031 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
2033 fireEvent : function(){
2034 var a = TOARRAY(arguments),
2035 ename = a[0].toLowerCase(),
2038 ce = me.events[ename],
2041 if (me.eventsSuspended === TRUE) {
2042 if (q = me.eventQueue) {
2046 else if(ISOBJECT(ce) && ce.bubble){
2047 if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
2050 c = me.getBubbleTarget && me.getBubbleTarget();
2051 if(c && c.enableBubble) {
2052 if(!c.events[ename] || !Ext.isObject(c.events[ename]) || !c.events[ename].bubble) {
2053 c.enableBubble(ename);
2055 return c.fireEvent.apply(c, a);
2061 ret = ce.fire.apply(ce, a);
2068 * Appends an event handler to this object.
2069 * @param {String} eventName The name of the event to listen for.
2070 * @param {Function} handler The method the event invokes.
2071 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2072 * <b>If omitted, defaults to the object which fired the event.</b>
2073 * @param {Object} options (optional) An object containing handler configuration.
2074 * properties. This may contain any of the following properties:<ul>
2075 * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2076 * <b>If omitted, defaults to the object which fired the event.</b></div></li>
2077 * <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>
2078 * <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>
2079 * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2080 * by the specified number of milliseconds. If the event fires again within that time, the original
2081 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2082 * <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>
2083 * if the event was bubbled up from a child Observable.</div></li>
2086 * <b>Combining Options</b><br>
2087 * Using the options argument, it is possible to combine different types of listeners:<br>
2089 * A delayed, one-time listener.
2091 myDataView.on('click', this.onClick, this, {
2096 * <b>Attaching multiple handlers in 1 call</b><br>
2097 * The method also allows for a single argument to be passed which is a config object containing properties
2098 * which specify multiple handlers.
2108 fn: this.onMouseOver,
2112 fn: this.onMouseOut,
2117 * Or a shorthand syntax:<br>
2120 'click' : this.onClick,
2121 'mouseover' : this.onMouseOver,
2122 'mouseout' : this.onMouseOut,
2126 addListener : function(eventName, fn, scope, o){
2132 if (ISOBJECT(eventName)) {
2136 if (!me.filterOptRe.test(e)) {
2137 me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
2141 eventName = eventName.toLowerCase();
2142 ce = me.events[eventName] || TRUE;
2143 if (Ext.isBoolean(ce)) {
2144 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
2146 ce.addListener(fn, scope, ISOBJECT(o) ? o : {});
2151 * Removes an event handler.
2152 * @param {String} eventName The type of event the handler was associated with.
2153 * @param {Function} handler The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2154 * @param {Object} scope (optional) The scope originally specified for the handler.
2156 removeListener : function(eventName, fn, scope){
2157 var ce = this.events[eventName.toLowerCase()];
2159 ce.removeListener(fn, scope);
2164 * Removes all listeners for this object
2166 purgeListeners : function(){
2167 var events = this.events,
2173 evt.clearListeners();
2179 * Adds the specified events to the list of events which this Observable may fire.
2180 * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
2181 * or the first event name string if multiple event names are being passed as separate parameters.
2182 * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
2184 this.addEvents('storeloaded', 'storecleared');
2187 addEvents : function(o){
2189 me.events = me.events || {};
2190 if (Ext.isString(o)) {
2194 me.events[a[i]] = me.events[a[i]] || TRUE;
2197 Ext.applyIf(me.events, o);
2202 * Checks to see if this object has any listeners for a specified event
2203 * @param {String} eventName The name of the event to check for
2204 * @return {Boolean} True if the event is being listened for, else false
2206 hasListener : function(eventName){
2207 var e = this.events[eventName.toLowerCase()];
2208 return ISOBJECT(e) && e.listeners.length > 0;
2212 * Suspend the firing of all events. (see {@link #resumeEvents})
2213 * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
2214 * after the {@link #resumeEvents} call instead of discarding all suspended events;
2216 suspendEvents : function(queueSuspended){
2217 this.eventsSuspended = TRUE;
2218 if(queueSuspended && !this.eventQueue){
2219 this.eventQueue = [];
2224 * Resume firing events. (see {@link #suspendEvents})
2225 * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
2226 * events fired during event suspension will be sent to any listeners now.
2228 resumeEvents : function(){
2230 queued = me.eventQueue || [];
2231 me.eventsSuspended = FALSE;
2232 delete me.eventQueue;
2233 EACH(queued, function(e) {
2234 me.fireEvent.apply(me, e);
2239 var OBSERVABLE = EXTUTIL.Observable.prototype;
2241 * Appends an event handler to this object (shorthand for {@link #addListener}.)
2242 * @param {String} eventName The type of event to listen for
2243 * @param {Function} handler The method the event invokes
2244 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
2245 * <b>If omitted, defaults to the object which fired the event.</b>
2246 * @param {Object} options (optional) An object containing handler configuration.
2249 OBSERVABLE.on = OBSERVABLE.addListener;
2251 * Removes an event handler (shorthand for {@link #removeListener}.)
2252 * @param {String} eventName The type of event the handler was associated with.
2253 * @param {Function} handler The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2254 * @param {Object} scope (optional) The scope originally specified for the handler.
2257 OBSERVABLE.un = OBSERVABLE.removeListener;
2260 * Removes <b>all</b> added captures from the Observable.
2261 * @param {Observable} o The Observable to release
2264 EXTUTIL.Observable.releaseCapture = function(o){
2265 o.fireEvent = OBSERVABLE.fireEvent;
2268 function createTargeted(h, o, scope){
2270 if(o.target == arguments[0]){
2271 h.apply(scope, TOARRAY(arguments));
2276 function createBuffered(h, o, l, scope){
2277 l.task = new EXTUTIL.DelayedTask();
2279 l.task.delay(o.buffer, h, scope, TOARRAY(arguments));
2283 function createSingle(h, e, fn, scope){
2285 e.removeListener(fn, scope);
2286 return h.apply(scope, arguments);
2290 function createDelayed(h, o, l, scope){
2292 var task = new EXTUTIL.DelayedTask();
2297 task.delay(o.delay || 10, h, scope, TOARRAY(arguments));
2301 EXTUTIL.Event = function(obj, name){
2304 this.listeners = [];
2307 EXTUTIL.Event.prototype = {
2308 addListener : function(fn, scope, options){
2311 scope = scope || me.obj;
2312 if(!me.isListening(fn, scope)){
2313 l = me.createListener(fn, scope, options);
2314 if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2315 me.listeners = me.listeners.slice(0);
2317 me.listeners.push(l);
2321 createListener: function(fn, scope, o){
2322 o = o || {}, scope = scope || this.obj;
2329 h = createTargeted(h, o, scope);
2332 h = createDelayed(h, o, l, scope);
2335 h = createSingle(h, this, fn, scope);
2338 h = createBuffered(h, o, l, scope);
2344 findListener : function(fn, scope){
2345 var list = this.listeners,
2349 scope = scope || this.obj;
2353 if(l.fn == fn && l.scope == scope){
2361 isListening : function(fn, scope){
2362 return this.findListener(fn, scope) != -1;
2365 removeListener : function(fn, scope){
2371 if((index = me.findListener(fn, scope)) != -1){
2373 me.listeners = me.listeners.slice(0);
2375 l = me.listeners[index];
2380 k = l.tasks && l.tasks.length;
2383 l.tasks[k].cancel();
2387 me.listeners.splice(index, 1);
2393 // Iterate to stop any buffered/delayed events
2394 clearListeners : function(){
2399 me.removeListener(l[i].fn, l[i].scope);
2405 args = TOARRAY(arguments),
2406 listeners = me.listeners,
2407 len = listeners.length,
2413 for (; i < len; i++) {
2415 if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2416 return (me.firing = FALSE);
2425 * @class Ext.util.Observable
2427 Ext.apply(Ext.util.Observable.prototype, function(){
2428 // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
2429 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2431 function getMethodEvent(method){
2432 var e = (this.methodEvents = this.methodEvents ||
2433 {})[method], returnValue, v, cancel, obj = this;
2436 this.methodEvents[method] = e = {};
2437 e.originalFn = this[method];
2438 e.methodName = method;
2442 var makeCall = function(fn, scope, args){
2443 if (!Ext.isEmpty(v = fn.apply(scope || obj, args))) {
2444 if (Ext.isObject(v)) {
2445 returnValue = !Ext.isEmpty(v.returnValue) ? v.returnValue : v;
2446 cancel = !!v.cancel;
2458 this[method] = function(){
2459 var args = Ext.toArray(arguments);
2460 returnValue = v = undefined;
2463 Ext.each(e.before, function(b){
2464 makeCall(b.fn, b.scope, args);
2470 if (!Ext.isEmpty(v = e.originalFn.apply(obj, args))) {
2473 Ext.each(e.after, function(a){
2474 makeCall(a.fn, a.scope, args);
2486 // these are considered experimental
2487 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
2488 // adds an 'interceptor' called before the original method
2489 beforeMethod : function(method, fn, scope){
2490 getMethodEvent.call(this, method).before.push({
2496 // adds a 'sequence' called after the original method
2497 afterMethod : function(method, fn, scope){
2498 getMethodEvent.call(this, method).after.push({
2504 removeMethodListener: function(method, fn, scope){
2505 var e = getMethodEvent.call(this, method), found = false;
2506 Ext.each(e.before, function(b, i, arr){
2507 if (b.fn == fn && b.scope == scope) {
2514 Ext.each(e.after, function(a, i, arr){
2515 if (a.fn == fn && a.scope == scope) {
2524 * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
2525 * @param {Object} o The Observable whose events this object is to relay.
2526 * @param {Array} events Array of event names to relay.
2528 relayEvents : function(o, events){
2530 function createHandler(ename){
2532 return me.fireEvent.apply(me, [ename].concat(Ext.toArray(arguments)));
2535 Ext.each(events, function(ename){
2536 me.events[ename] = me.events[ename] || true;
2537 o.on(ename, createHandler(ename), me);
2542 * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
2543 * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
2544 * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default
2545 * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
2546 * access the required target more quickly.</p>
2547 * <p>Example:</p><pre><code>
2548 Ext.override(Ext.form.Field, {
2549 // Add functionality to Field's initComponent to enable the change event to bubble
2550 initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
2551 this.enableBubble('change');
2554 // We know that we want Field's events to bubble directly to the FormPanel.
2555 getBubbleTarget : function() {
2556 if (!this.formPanel) {
2557 this.formPanel = this.findParentByType('form');
2559 return this.formPanel;
2563 var myForm = new Ext.formPanel({
2564 title: 'User Details',
2569 change: function() {
2570 // Title goes red if form has been modified.
2571 myForm.header.setStyle('color', 'red');
2576 * @param {String/Array} events The event name to bubble, or an Array of event names.
2578 enableBubble : function(events){
2580 if(!Ext.isEmpty(events)){
2581 events = Ext.isArray(events) ? events : Ext.toArray(arguments);
2582 Ext.each(events, function(ename){
2583 ename = ename.toLowerCase();
2584 var ce = me.events[ename] || true;
2585 if (Ext.isBoolean(ce)) {
2586 ce = new Ext.util.Event(me, ename);
2587 me.events[ename] = ce;
2598 * Starts capture on the specified Observable. All events will be passed
2599 * to the supplied function with the event name + standard signature of the event
2600 * <b>before</b> the event is fired. If the supplied function returns false,
2601 * the event will not fire.
2602 * @param {Observable} o The Observable to capture events from.
2603 * @param {Function} fn The function to call when an event is fired.
2604 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
2607 Ext.util.Observable.capture = function(o, fn, scope){
2608 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
2613 * Sets observability on the passed class constructor.<p>
2614 * <p>This makes any event fired on any instance of the passed class also fire a single event through
2615 * the <i>class</i> allowing for central handling of events on many instances at once.</p>
2616 * <p>Usage:</p><pre><code>
2617 Ext.util.Observable.observeClass(Ext.data.Connection);
2618 Ext.data.Connection.on('beforerequest', function(con, options) {
2619 console.log('Ajax request made to ' + options.url);
2621 * @param {Function} c The class constructor to make observable.
2622 * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
2625 Ext.util.Observable.observeClass = function(c, listeners){
2628 Ext.apply(c, new Ext.util.Observable());
2629 Ext.util.Observable.capture(c.prototype, c.fireEvent, c);
2631 if(Ext.isObject(listeners)){
2637 * @class Ext.EventManager
2638 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
2639 * several useful events directly.
2640 * See {@link Ext.EventObject} for more details on normalized event objects.
2644 Ext.EventManager = function(){
2647 docReadyState = false,
2648 DETECT_NATIVE = Ext.isGecko || Ext.isWebKit || Ext.isSafari,
2653 DOMCONTENTLOADED = "DOMContentLoaded",
2654 COMPLETE = 'complete',
2655 propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
2657 * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep
2658 * a reference to them so we can look them up at a later point.
2660 specialElCache = [];
2665 len = specialElCache.length,
2670 if(el.getElementById || el.navigator){
2672 for(; i < len; ++i){
2673 o = specialElCache[i];
2680 // for browsers that support it, ensure that give the el the same id
2682 specialElCache.push({
2691 if(!Ext.elCache[id]){
2692 Ext.Element.addToCache(new Ext.Element(el), id);
2694 Ext.elCache[id].skipGC = true;
2701 /// There is some jquery work around stuff here that isn't needed in Ext Core.
2702 function addListener(el, ename, fn, task, wrap, scope){
2703 el = Ext.getDom(el);
2705 es = Ext.elCache[id].events,
2708 wfn = E.on(el, ename, wrap);
2709 es[ename] = es[ename] || [];
2711 /* 0 = Original Function,
2712 1 = Event Manager Wrapped Function,
2714 3 = Adapter Wrapped Function,
2717 es[ename].push([fn, wrap, scope, wfn, task]);
2719 // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
2720 // without breaking ExtJS.
2722 // workaround for jQuery
2723 if(el.addEventListener && ename == "mousewheel"){
2724 var args = ["DOMMouseScroll", wrap, false];
2725 el.addEventListener.apply(el, args);
2726 Ext.EventManager.addListener(WINDOW, 'unload', function(){
2727 el.removeEventListener.apply(el, args);
2731 // fix stopped mousedowns on the document
2732 if(el == DOC && ename == "mousedown"){
2733 Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
2737 function doScrollChk(){
2739 'doScroll' will NOT work in a IFRAME/FRAMESET.
2740 The method succeeds but, a DOM query done immediately after -- FAILS.
2747 DOC.documentElement.doScroll('left');
2756 * @return {Boolean} True if the document is in a 'complete' state (or was determined to
2757 * be true by other means). If false, the state is evaluated again until canceled.
2759 function checkReadyState(e){
2761 if(Ext.isIE && doScrollChk()){
2764 if(DOC.readyState == COMPLETE){
2768 docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2773 function checkStyleSheets(e){
2774 styles || (styles = Ext.query('style, link[rel=stylesheet]'));
2775 if(styles.length == DOC.styleSheets.length){
2779 docReadyState || (docReadyProcId = setTimeout(arguments.callee, 2));
2783 function OperaDOMContentLoaded(e){
2784 DOC.removeEventListener(DOMCONTENTLOADED, arguments.callee, false);
2788 function fireDocReady(e){
2790 docReadyState = true; //only attempt listener removal once
2793 clearTimeout(docReadyProcId);
2796 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
2798 if(Ext.isIE && checkReadyState.bindIE){ //was this was actually set ??
2799 DOC.detachEvent('onreadystatechange', checkReadyState);
2801 E.un(WINDOW, "load", arguments.callee);
2803 if(docReadyEvent && !Ext.isReady){
2805 docReadyEvent.fire();
2806 docReadyEvent.listeners = [];
2811 function initDocReady(){
2812 docReadyEvent || (docReadyEvent = new Ext.util.Event());
2813 if (DETECT_NATIVE) {
2814 DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
2817 * Handle additional (exceptional) detection strategies here
2820 //Use readystatechange as a backup AND primary detection mechanism for a FRAME/IFRAME
2821 //See if page is already loaded
2822 if(!checkReadyState()){
2823 checkReadyState.bindIE = true;
2824 DOC.attachEvent('onreadystatechange', checkReadyState);
2827 }else if(Ext.isOpera ){
2829 Opera needs special treatment needed here because CSS rules are NOT QUITE
2830 available after DOMContentLoaded is raised.
2833 //See if page is already loaded and all styleSheets are in place
2834 (DOC.readyState == COMPLETE && checkStyleSheets()) ||
2835 DOC.addEventListener(DOMCONTENTLOADED, OperaDOMContentLoaded, false);
2837 }else if (Ext.isWebKit){
2838 //Fallback for older Webkits without DOMCONTENTLOADED support
2841 // no matter what, make sure it fires on load
2842 E.on(WINDOW, "load", fireDocReady);
2845 function createTargeted(h, o){
2847 var args = Ext.toArray(arguments);
2848 if(o.target == Ext.EventObject.setEvent(args[0]).target){
2849 h.apply(this, args);
2854 function createBuffered(h, o, task){
2856 // create new event object impl so new events don't wipe out properties
2857 task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
2861 function createSingle(h, el, ename, fn, scope){
2863 Ext.EventManager.removeListener(el, ename, fn, scope);
2868 function createDelayed(h, o, fn){
2870 var task = new Ext.util.DelayedTask(h);
2874 fn.tasks.push(task);
2875 task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);
2879 function listen(element, ename, opt, fn, scope){
2880 var o = !Ext.isObject(opt) ? {} : opt,
2881 el = Ext.getDom(element), task;
2884 scope = scope || o.scope;
2887 throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
2890 // prevent errors while unload occurring
2891 if(!Ext){// !window[xname]){ ==> can't we do this?
2894 e = Ext.EventObject.setEvent(e);
2897 if(!(t = e.getTarget(o.delegate, el))){
2906 if (o.preventDefault) {
2909 if (o.stopPropagation) {
2910 e.stopPropagation();
2916 fn.call(scope || el, e, t, o);
2919 h = createTargeted(h, o);
2922 h = createDelayed(h, o, fn);
2925 h = createSingle(h, el, ename, fn, scope);
2928 task = new Ext.util.DelayedTask(h);
2929 h = createBuffered(h, o, task);
2932 addListener(el, ename, fn, task, h, scope);
2938 * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
2939 * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
2940 * @param {String/HTMLElement} el The html element or id to assign the event handler to.
2941 * @param {String} eventName The name of the event to listen for.
2942 * @param {Function} handler The handler function the event invokes. This function is passed
2943 * the following parameters:<ul>
2944 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
2945 * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
2946 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
2947 * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
2949 * @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>.
2950 * @param {Object} options (optional) An object containing handler configuration properties.
2951 * This may contain any of the following properties:<ul>
2952 * <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>
2953 * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
2954 * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
2955 * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
2956 * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
2957 * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
2958 * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
2959 * <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>
2960 * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2961 * by the specified number of milliseconds. If the event fires again within that time, the original
2962 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2963 * <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>
2965 * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
2967 addListener : function(element, eventName, fn, scope, options){
2968 if(Ext.isObject(eventName)){
2969 var o = eventName, e, val;
2972 if(!propRe.test(e)){
2973 if(Ext.isFunction(val)){
2975 listen(element, e, o, val, o.scope);
2977 // individual options
2978 listen(element, e, val);
2983 listen(element, eventName, options, fn, scope);
2988 * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
2989 * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
2990 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
2991 * @param {String} eventName The name of the event.
2992 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
2993 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
2994 * then this must refer to the same object.
2996 removeListener : function(el, eventName, fn, scope){
2997 el = Ext.getDom(el);
2999 f = el && (Ext.elCache[id].events)[eventName] || [],
3000 wrap, i, l, k, len, fnc;
3002 for (i = 0, len = f.length; i < len; i++) {
3004 /* 0 = Original Function,
3005 1 = Event Manager Wrapped Function,
3007 3 = Adapter Wrapped Function,
3010 if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) {
3014 k = fn.tasks && fn.tasks.length;
3017 fn.tasks[k].cancel();
3022 E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);
3024 // jQuery workaround that should be removed from Ext Core
3025 if(wrap && el.addEventListener && eventName == "mousewheel"){
3026 el.removeEventListener("DOMMouseScroll", wrap, false);
3029 // fix stopped mousedowns on the document
3030 if(wrap && el == DOC && eventName == "mousedown"){
3031 Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3035 if (f.length === 0) {
3036 delete Ext.elCache[id].events[eventName];
3038 for (k in Ext.elCache[id].events) {
3041 Ext.elCache[id].events = {};
3048 * Removes all event handers from an element. Typically you will use {@link Ext.Element#removeAllListeners}
3049 * directly on an Element in favor of calling this version.
3050 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
3052 removeAll : function(el){
3053 el = Ext.getDom(el);
3055 ec = Ext.elCache[id] || {},
3056 es = ec.events || {},
3057 f, i, len, ename, fn, k, wrap;
3060 if(es.hasOwnProperty(ename)){
3062 /* 0 = Original Function,
3063 1 = Event Manager Wrapped Function,
3065 3 = Adapter Wrapped Function,
3068 for (i = 0, len = f.length; i < len; i++) {
3073 if(fn[0].tasks && (k = fn[0].tasks.length)) {
3075 fn[0].tasks[k].cancel();
3080 E.un(el, ename, E.extAdapter ? fn[3] : wrap);
3082 // jQuery workaround that should be removed from Ext Core
3083 if(el.addEventListener && wrap && ename == "mousewheel"){
3084 el.removeEventListener("DOMMouseScroll", wrap, false);
3087 // fix stopped mousedowns on the document
3088 if(wrap && el == DOC && ename == "mousedown"){
3089 Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
3094 if (Ext.elCache[id]) {
3095 Ext.elCache[id].events = {};
3099 getListeners : function(el, eventName) {
3100 el = Ext.getDom(el);
3102 ec = Ext.elCache[id] || {},
3103 es = ec.events || {},
3105 if (es && es[eventName]) {
3106 return es[eventName];
3112 purgeElement : function(el, recurse, eventName) {
3113 el = Ext.getDom(el);
3115 ec = Ext.elCache[id] || {},
3116 es = ec.events || {},
3119 if (es && es.hasOwnProperty(eventName)) {
3121 for (i = 0, len = f.length; i < len; i++) {
3122 Ext.EventManager.removeListener(el, eventName, f[i][0]);
3126 Ext.EventManager.removeAll(el);
3128 if (recurse && el && el.childNodes) {
3129 for (i = 0, len = el.childNodes.length; i < len; i++) {
3130 Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);
3135 _unload : function() {
3137 for (el in Ext.elCache) {
3138 Ext.EventManager.removeAll(el);
3141 delete Ext.Element._flyweights;
3143 // Abort any outstanding Ajax requests
3147 ajax = Ext.lib.Ajax;
3148 (Ext.isObject(ajax.conn)) ? conn = ajax.conn : conn = {};
3152 ajax.abort({conn: c, tId: tid});
3157 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
3158 * accessed shorthanded as Ext.onReady().
3159 * @param {Function} fn The method the event invokes.
3160 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3161 * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3162 * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3164 onDocumentReady : function(fn, scope, options){
3165 if(Ext.isReady){ // if it already fired or document.body is present
3166 docReadyEvent || (docReadyEvent = new Ext.util.Event());
3167 docReadyEvent.addListener(fn, scope, options);
3168 docReadyEvent.fire();
3169 docReadyEvent.listeners = [];
3174 options = options || {};
3175 options.delay = options.delay || 1;
3176 docReadyEvent.addListener(fn, scope, options);
3181 * Forces a document ready state transition for the framework. Used when Ext is loaded
3182 * into a DOM structure AFTER initial page load (Google API or other dynamic load scenario.
3183 * Any pending 'onDocumentReady' handlers will be fired (if not already handled).
3185 fireDocReady : fireDocReady
3188 * Appends an event handler to an element. Shorthand for {@link #addListener}.
3189 * @param {String/HTMLElement} el The html element or id to assign the event handler to
3190 * @param {String} eventName The name of the event to listen for.
3191 * @param {Function} handler The handler function the event invokes.
3192 * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.
3193 * @param {Object} options (optional) An object containing standard {@link #addListener} options
3194 * @member Ext.EventManager
3197 pub.on = pub.addListener;
3199 * Removes an event handler from an element. Shorthand for {@link #removeListener}.
3200 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
3201 * @param {String} eventName The name of the event.
3202 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #on} call.</b>
3203 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
3204 * then this must refer to the same object.
3205 * @member Ext.EventManager
3208 pub.un = pub.removeListener;
3210 pub.stoppedMouseDownEvent = new Ext.util.Event();
3214 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
3215 * @param {Function} fn The method the event invokes.
3216 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3217 * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
3218 * <code>{single: true}</code> be used so that the handler is removed on first invocation.
3222 Ext.onReady = Ext.EventManager.onDocumentReady;
3225 //Initialize doc classes
3228 var initExtCss = function(){
3229 // find the body element
3230 var bd = document.body || document.getElementsByTagName('body')[0];
3231 if(!bd){ return false; }
3233 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
3234 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
3235 : Ext.isOpera ? "ext-opera"
3236 : Ext.isWebKit ? "ext-webkit" : ""];
3239 cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
3240 }else if(Ext.isChrome){
3241 cls.push("ext-chrome");
3245 cls.push("ext-mac");
3248 cls.push("ext-linux");
3251 if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
3252 var p = bd.parentNode;
3254 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';
3257 bd.className += cls.join(' ');
3262 Ext.onReady(initExtCss);
3268 * @class Ext.EventObject
3269 * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
3270 * wraps the browser's native event-object normalizing cross-browser differences,
3271 * such as which mouse button is clicked, keys pressed, mechanisms to stop
3272 * event-propagation along with a method to prevent default actions from taking place.
3273 * <p>For example:</p>
3275 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
3277 var target = e.getTarget(); // same as t (the target HTMLElement)
3280 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.Element}
3281 myDiv.on( // 'on' is shorthand for addListener
3282 "click", // perform an action on click of myDiv
3283 handleClick // reference to the action handler
3285 // other methods to do the same:
3286 Ext.EventManager.on("myDiv", 'click', handleClick);
3287 Ext.EventManager.addListener("myDiv", 'click', handleClick);
3291 Ext.EventObject = function(){
3292 var E = Ext.lib.Event,
3293 // safari keypress events for special keys return bad keycodes
3297 63235 : 39, // right
3300 63276 : 33, // page up
3301 63277 : 34, // page down
3302 63272 : 46, // delete
3306 // normalize button clicks
3307 btnMap = Ext.isIE ? {1:0,4:1,2:2} :
3308 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
3310 Ext.EventObjectImpl = function(e){
3312 this.setEvent(e.browserEvent || e);
3316 Ext.EventObjectImpl.prototype = {
3318 setEvent : function(e){
3320 if(e == me || (e && e.browserEvent)){ // already wrapped
3323 me.browserEvent = e;
3325 // normalize buttons
3326 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
3327 if(e.type == 'click' && me.button == -1){
3331 me.shiftKey = e.shiftKey;
3332 // mac metaKey behaves like ctrlKey
3333 me.ctrlKey = e.ctrlKey || e.metaKey || false;
3334 me.altKey = e.altKey;
3335 // in getKey these will be normalized for the mac
3336 me.keyCode = e.keyCode;
3337 me.charCode = e.charCode;
3338 // cache the target for the delayed and or buffered events
3339 me.target = E.getTarget(e);
3344 me.shiftKey = false;
3356 * Stop the event (preventDefault and stopPropagation)
3358 stopEvent : function(){
3360 if(me.browserEvent){
3361 if(me.browserEvent.type == 'mousedown'){
3362 Ext.EventManager.stoppedMouseDownEvent.fire(me);
3364 E.stopEvent(me.browserEvent);
3369 * Prevents the browsers default handling of the event.
3371 preventDefault : function(){
3372 if(this.browserEvent){
3373 E.preventDefault(this.browserEvent);
3378 * Cancels bubbling of the event.
3380 stopPropagation : function(){
3382 if(me.browserEvent){
3383 if(me.browserEvent.type == 'mousedown'){
3384 Ext.EventManager.stoppedMouseDownEvent.fire(me);
3386 E.stopPropagation(me.browserEvent);
3391 * Gets the character code for the event.
3394 getCharCode : function(){
3395 return this.charCode || this.keyCode;
3399 * Returns a normalized keyCode for the event.
3400 * @return {Number} The key code
3402 getKey : function(){
3403 return this.normalizeKey(this.keyCode || this.charCode)
3407 normalizeKey: function(k){
3408 return Ext.isSafari ? (safariKeys[k] || k) : k;
3412 * Gets the x coordinate of the event.
3415 getPageX : function(){
3420 * Gets the y coordinate of the event.
3423 getPageY : function(){
3428 * Gets the page coordinates of the event.
3429 * @return {Array} The xy values like [x, y]
3436 * Gets the target for the event.
3437 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
3438 * @param {Number/Mixed} maxDepth (optional) The max depth to
3439 search as a number or element (defaults to 10 || document.body)
3440 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
3441 * @return {HTMLelement}
3443 getTarget : function(selector, maxDepth, returnEl){
3444 return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
3448 * Gets the related target.
3449 * @return {HTMLElement}
3451 getRelatedTarget : function(){
3452 return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
3456 * Normalizes mouse wheel delta across browsers
3457 * @return {Number} The delta
3459 getWheelDelta : function(){
3460 var e = this.browserEvent;
3462 if(e.wheelDelta){ /* IE/Opera. */
3463 delta = e.wheelDelta/120;
3464 }else if(e.detail){ /* Mozilla case. */
3465 delta = -e.detail/3;
3471 * 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.
3472 * Example usage:<pre><code>
3473 // Handle click on any child of an element
3474 Ext.getBody().on('click', function(e){
3475 if(e.within('some-el')){
3476 alert('Clicked on a child of some-el!');
3480 // Handle click directly on an element, ignoring clicks on child nodes
3481 Ext.getBody().on('click', function(e,t){
3482 if((t.id == 'some-el') && !e.within(t, true)){
3483 alert('Clicked directly on some-el!');
3487 * @param {Mixed} el The id, DOM element or Ext.Element to check
3488 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
3489 * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
3492 within : function(el, related, allowEl){
3494 var t = this[related ? "getRelatedTarget" : "getTarget"]();
3495 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
3501 return new Ext.EventObjectImpl();
3504 * @class Ext.EventManager
3506 Ext.apply(Ext.EventManager, function(){
3512 propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
3515 // note 1: IE fires ONLY the keydown event on specialkey autorepeat
3516 // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
3517 // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
3518 useKeydown = Ext.isWebKit ?
3519 Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
3520 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
3524 doResizeEvent: function(){
3525 var h = D.getViewHeight(),
3526 w = D.getViewWidth();
3528 //whacky problem in IE where the resize event will fire even though the w/h are the same.
3529 if(curHeight != h || curWidth != w){
3530 resizeEvent.fire(curWidth = w, curHeight = h);
3535 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
3536 * passes new viewport width and height to handlers.
3537 * @param {Function} fn The handler function the window resize event invokes.
3538 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3539 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
3541 onWindowResize : function(fn, scope, options){
3543 resizeEvent = new Ext.util.Event();
3544 resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
3545 Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
3547 resizeEvent.addListener(fn, scope, options);
3550 // exposed only to allow manual firing
3551 fireWindowResize : function(){
3553 resizeTask.delay(100);
3558 * 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.
3559 * @param {Function} fn The function the event invokes.
3560 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3561 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
3563 onTextResize : function(fn, scope, options){
3565 textEvent = new Ext.util.Event();
3566 var textEl = new Ext.Element(document.createElement('div'));
3567 textEl.dom.className = 'x-text-resize';
3568 textEl.dom.innerHTML = 'X';
3569 textEl.appendTo(document.body);
3570 textSize = textEl.dom.offsetHeight;
3571 setInterval(function(){
3572 if(textEl.dom.offsetHeight != textSize){
3573 textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
3575 }, this.textResizeInterval);
3577 textEvent.addListener(fn, scope, options);
3581 * Removes the passed window resize listener.
3582 * @param {Function} fn The method the event invokes
3583 * @param {Object} scope The scope of handler
3585 removeResizeListener : function(fn, scope){
3587 resizeEvent.removeListener(fn, scope);
3592 fireResize : function(){
3594 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
3599 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
3601 textResizeInterval : 50,
3604 * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
3608 // protected for use inside the framework
3609 // detects whether we should use keydown or keypress based on the browser.
3610 useKeydown: useKeydown
3614 Ext.EventManager.on = Ext.EventManager.addListener;
3617 Ext.apply(Ext.EventObjectImpl.prototype, {
3618 /** Key constant @type Number */
3620 /** Key constant @type Number */
3622 /** Key constant @type Number */
3624 /** Key constant @type Number */
3626 /** Key constant @type Number */
3628 /** Key constant @type Number */
3630 /** Key constant @type Number */
3632 CONTROL : 17, // legacy
3633 /** Key constant @type Number */
3635 /** Key constant @type Number */
3637 /** Key constant @type Number */
3639 /** Key constant @type Number */
3641 /** Key constant @type Number */
3643 /** Key constant @type Number */
3645 PAGEUP : 33, // legacy
3646 /** Key constant @type Number */
3648 PAGEDOWN : 34, // legacy
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 /** Key constant @type Number */
3665 /** Key constant @type Number */
3667 /** Key constant @type Number */
3669 /** Key constant @type Number */
3671 /** Key constant @type Number */
3673 /** Key constant @type Number */
3675 /** Key constant @type Number */
3677 /** Key constant @type Number */
3679 /** Key constant @type Number */
3681 /** Key constant @type Number */
3683 /** Key constant @type Number */
3685 /** Key constant @type Number */
3687 /** Key constant @type Number */
3689 /** Key constant @type Number */
3691 /** Key constant @type Number */
3693 /** Key constant @type Number */
3695 /** Key constant @type Number */
3697 /** Key constant @type Number */
3699 /** Key constant @type Number */
3701 /** Key constant @type Number */
3703 /** Key constant @type Number */
3705 /** Key constant @type Number */
3707 /** Key constant @type Number */
3709 /** Key constant @type Number */
3711 /** Key constant @type Number */
3713 /** Key constant @type Number */
3715 /** Key constant @type Number */
3717 /** Key constant @type Number */
3719 /** Key constant @type Number */
3721 /** Key constant @type Number */
3723 /** Key constant @type Number */
3725 /** Key constant @type Number */
3727 /** Key constant @type Number */
3729 /** Key constant @type Number */
3731 /** Key constant @type Number */
3733 /** Key constant @type Number */
3735 /** Key constant @type Number */
3737 /** Key constant @type Number */
3739 /** Key constant @type Number */
3741 /** Key constant @type Number */
3743 /** Key constant @type Number */
3745 /** Key constant @type Number */
3747 /** Key constant @type Number */
3749 /** Key constant @type Number */
3751 /** Key constant @type Number */
3753 /** Key constant @type Number */
3755 /** Key constant @type Number */
3757 /** Key constant @type Number */
3759 /** Key constant @type Number */
3761 /** Key constant @type Number */
3763 /** Key constant @type Number */
3765 /** Key constant @type Number */
3767 /** Key constant @type Number */
3769 /** Key constant @type Number */
3771 /** Key constant @type Number */
3773 /** Key constant @type Number */
3775 /** Key constant @type Number */
3777 /** Key constant @type Number */
3779 /** Key constant @type Number */
3781 /** Key constant @type Number */
3783 /** Key constant @type Number */
3785 /** Key constant @type Number */
3787 /** Key constant @type Number */
3789 /** Key constant @type Number */
3791 /** Key constant @type Number */
3793 /** Key constant @type Number */
3797 isNavKeyPress : function(){
3799 k = this.normalizeKey(me.keyCode);
3800 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
3806 isSpecialKey : function(){
3807 var k = this.normalizeKey(this.keyCode);
3808 return (this.type == 'keypress' && this.ctrlKey) ||
3809 this.isNavKeyPress() ||
3810 (k == this.BACKSPACE) || // Backspace
3811 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
3812 (k >= 44 && k <= 46); // Print Screen, Insert, Delete
3815 getPoint : function(){
3816 return new Ext.lib.Point(this.xy[0], this.xy[1]);
3820 * Returns true if the control, meta, shift or alt key was pressed during this event.
3823 hasModifier : function(){
3824 return ((this.ctrlKey || this.altKey) || this.shiftKey);
3827 * @class Ext.Element
3828 * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
3829 * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
3830 * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
3831 * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
3832 * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
3836 var el = Ext.get("my-div");
3838 // by DOM element reference
3839 var el = Ext.get(myDivElement);
3841 * <b>Animations</b><br />
3842 * <p>When an element is manipulated, by default there is no animation.</p>
3844 var el = Ext.get("my-div");
3849 * <p>Many of the functions for manipulating an element have an optional "animate" parameter. This
3850 * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
3852 // default animation
3853 el.setWidth(100, true);
3856 * <p>To configure the effects, an object literal with animation options to use as the Element animation
3857 * configuration object can also be specified. Note that the supported Element animation configuration
3858 * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects. The supported
3859 * Element animation configuration options are:</p>
3861 Option Default Description
3862 --------- -------- ---------------------------------------------
3863 {@link Ext.Fx#duration duration} .35 The duration of the animation in seconds
3864 {@link Ext.Fx#easing easing} easeOut The easing method
3865 {@link Ext.Fx#callback callback} none A function to execute when the anim completes
3866 {@link Ext.Fx#scope scope} this The scope (this) of the callback function
3870 // Element animation options object
3872 {@link Ext.Fx#duration duration}: 1,
3873 {@link Ext.Fx#easing easing}: 'elasticIn',
3874 {@link Ext.Fx#callback callback}: this.foo,
3875 {@link Ext.Fx#scope scope}: this
3877 // animation with some options set
3878 el.setWidth(100, opt);
3880 * <p>The Element animation object being used for the animation will be set on the options
3881 * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
3883 // using the "anim" property to get the Anim object
3884 if(opt.anim.isAnimated()){
3888 * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
3889 * <p><b> Composite (Collections of) Elements</b></p>
3890 * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
3891 * @constructor Create a new Element directly.
3892 * @param {String/HTMLElement} element
3893 * @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).
3898 Ext.Element = function(element, forceNew){
3899 var dom = typeof element == "string" ?
3900 DOC.getElementById(element) : element,
3903 if(!dom) return null;
3907 if(!forceNew && id && Ext.elCache[id]){ // element object already exists
3908 return Ext.elCache[id].el;
3918 * The DOM element ID
3921 this.id = id || Ext.id(dom);
3924 var D = Ext.lib.Dom,
3933 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
3934 * @param {Object} o The object with the attributes
3935 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
3936 * @return {Ext.Element} this
3938 set : function(o, useSet){
3942 useSet = (useSet !== false) && !!el.setAttribute;
3945 if (o.hasOwnProperty(attr)) {
3947 if (attr == 'style') {
3948 DH.applyStyles(el, val);
3949 } else if (attr == 'cls') {
3951 } else if (useSet) {
3952 el.setAttribute(attr, val);
3964 * Fires when a mouse click is detected within the element.
3965 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3966 * @param {HtmlElement} t The target of the event.
3967 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3970 * @event contextmenu
3971 * Fires when a right click is detected within the element.
3972 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3973 * @param {HtmlElement} t The target of the event.
3974 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3978 * Fires when a mouse double click is detected within the element.
3979 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3980 * @param {HtmlElement} t The target of the event.
3981 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3985 * Fires when a mousedown is detected within the element.
3986 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3987 * @param {HtmlElement} t The target of the event.
3988 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3992 * Fires when a mouseup 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.
3999 * Fires when a mouseover 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 mousemove is detected with 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 mouseout is detected with 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 the mouse enters 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 the mouse leaves 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.
4036 * Fires when a keypress is detected within the element.
4037 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4038 * @param {HtmlElement} t The target of the event.
4039 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4043 * Fires when a keydown is detected within the element.
4044 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4045 * @param {HtmlElement} t The target of the event.
4046 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4050 * Fires when a keyup is detected within the element.
4051 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4052 * @param {HtmlElement} t The target of the event.
4053 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4057 // HTML frame/object events
4060 * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
4061 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4062 * @param {HtmlElement} t The target of the event.
4063 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4067 * 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.
4068 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4069 * @param {HtmlElement} t The target of the event.
4070 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4074 * Fires when an object/image is stopped from loading before completely loaded.
4075 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4076 * @param {HtmlElement} t The target of the event.
4077 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4081 * Fires when an object/image/frame cannot be loaded properly.
4082 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4083 * @param {HtmlElement} t The target of the event.
4084 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4088 * Fires when a document view is resized.
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 a document view is scrolled.
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.
4104 * Fires when a user selects some text in a text field, including input and textarea.
4105 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4106 * @param {HtmlElement} t The target of the event.
4107 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4111 * Fires when a control loses the input focus and its value has been modified since gaining focus.
4112 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4113 * @param {HtmlElement} t The target of the event.
4114 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4118 * Fires when a form is submitted.
4119 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4120 * @param {HtmlElement} t The target of the event.
4121 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4125 * Fires when a form is reset.
4126 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4127 * @param {HtmlElement} t The target of the event.
4128 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4132 * Fires when an element receives focus either via the pointing device or by tab navigation.
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 an element loses focus either via the pointing device or by tabbing navigation.
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.
4145 // User Interface events
4148 * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
4149 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4150 * @param {HtmlElement} t The target of the event.
4151 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4154 * @event DOMFocusOut
4155 * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
4156 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4157 * @param {HtmlElement} t The target of the event.
4158 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4161 * @event DOMActivate
4162 * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
4163 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4164 * @param {HtmlElement} t The target of the event.
4165 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4168 // DOM Mutation events
4170 * @event DOMSubtreeModified
4171 * Where supported. Fires when the subtree is modified.
4172 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4173 * @param {HtmlElement} t The target of the event.
4174 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4177 * @event DOMNodeInserted
4178 * Where supported. Fires when a node has been added as a child of another node.
4179 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4180 * @param {HtmlElement} t The target of the event.
4181 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4184 * @event DOMNodeRemoved
4185 * Where supported. Fires when a descendant node of the element is removed.
4186 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4187 * @param {HtmlElement} t The target of the event.
4188 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4191 * @event DOMNodeRemovedFromDocument
4192 * Where supported. Fires when a node is being removed from a document.
4193 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4194 * @param {HtmlElement} t The target of the event.
4195 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4198 * @event DOMNodeInsertedIntoDocument
4199 * Where supported. Fires when a node is being inserted into a document.
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 DOMAttrModified
4206 * Where supported. Fires when an attribute has been modified.
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 DOMCharacterDataModified
4213 * Where supported. Fires when the character data has been modified.
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.
4220 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
4226 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
4227 * @param {String} selector The simple selector to test
4228 * @return {Boolean} True if this element matches the selector, else false
4230 is : function(simpleSelector){
4231 return Ext.DomQuery.is(this.dom, simpleSelector);
4235 * Tries to focus the element. Any exceptions are caught and ignored.
4236 * @param {Number} defer (optional) Milliseconds to defer the focus
4237 * @return {Ext.Element} this
4239 focus : function(defer, /* private */ dom) {
4241 dom = dom || me.dom;
4244 me.focus.defer(defer, null, [null, dom]);
4253 * Tries to blur the element. Any exceptions are caught and ignored.
4254 * @return {Ext.Element} this
4264 * Returns the value of the "value" attribute
4265 * @param {Boolean} asNumber true to parse the value as a number
4266 * @return {String/Number}
4268 getValue : function(asNumber){
4269 var val = this.dom.value;
4270 return asNumber ? parseInt(val, 10) : val;
4274 * Appends an event handler to this element. The shorthand version {@link #on} is equivalent.
4275 * @param {String} eventName The name of event to handle.
4276 * @param {Function} fn The handler function the event invokes. This function is passed
4277 * the following parameters:<ul>
4278 * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
4279 * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
4280 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
4281 * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
4283 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4284 * <b>If omitted, defaults to this Element.</b>.
4285 * @param {Object} options (optional) An object containing handler configuration properties.
4286 * This may contain any of the following properties:<ul>
4287 * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4288 * <b>If omitted, defaults to this Element.</b></div></li>
4289 * <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>
4290 * <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>
4291 * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
4292 * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
4293 * <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>
4294 * <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>
4295 * <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>
4296 * <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>
4297 * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
4298 * by the specified number of milliseconds. If the event fires again within that time, the original
4299 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
4302 * <b>Combining Options</b><br>
4303 * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
4304 * addListener. The two are equivalent. Using the options argument, it is possible to combine different
4305 * types of listeners:<br>
4307 * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
4308 * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
4310 el.on('click', this.onClick, this, {
4315 });</code></pre></p>
4317 * <b>Attaching multiple handlers in 1 call</b><br>
4318 * The method also allows for a single argument to be passed which is a config object containing properties
4319 * which specify multiple handlers.</p>
4329 fn: this.onMouseOver,
4333 fn: this.onMouseOut,
4338 * Or a shorthand syntax:<br>
4339 * Code:<pre><code></p>
4341 'click' : this.onClick,
4342 'mouseover' : this.onMouseOver,
4343 'mouseout' : this.onMouseOut,
4347 * <p><b>delegate</b></p>
4348 * <p>This is a configuration option that you can pass along when registering a handler for
4349 * an event to assist with event delegation. Event delegation is a technique that is used to
4350 * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
4351 * for a container element as opposed to each element within a container. By setting this
4352 * configuration option to a simple selector, the target element will be filtered to look for
4353 * a descendant of the target.
4354 * For example:<pre><code>
4355 // using this markup:
4357 <p id='p1'>paragraph one</p>
4358 <p id='p2' class='clickable'>paragraph two</p>
4359 <p id='p3'>paragraph three</p>
4361 // utilize event delegation to registering just one handler on the container element:
4362 el = Ext.get('elId');
4367 console.info(t.id); // 'p2'
4371 // filter the target element to be a descendant with the class 'clickable'
4372 delegate: '.clickable'
4376 * @return {Ext.Element} this
4378 addListener : function(eventName, fn, scope, options){
4379 Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
4384 * Removes an event handler from this element. The shorthand version {@link #un} is equivalent.
4385 * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
4386 * listener, the same scope must be specified here.
4389 el.removeListener('click', this.handlerFn);
4391 el.un('click', this.handlerFn);
4393 * @param {String} eventName The name of the event from which to remove the handler.
4394 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4395 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4396 * then this must refer to the same object.
4397 * @return {Ext.Element} this
4399 removeListener : function(eventName, fn, scope){
4400 Ext.EventManager.removeListener(this.dom, eventName, fn, scope || this);
4405 * Removes all previous added listeners from this element
4406 * @return {Ext.Element} this
4408 removeAllListeners : function(){
4409 Ext.EventManager.removeAll(this.dom);
4414 * Recursively removes all previous added listeners from this element and its children
4415 * @return {Ext.Element} this
4417 purgeAllListeners : function() {
4418 Ext.EventManager.purgeElement(this, true);
4422 * @private Test if size has a unit, otherwise appends the default
4424 addUnits : function(size){
4425 if(size === "" || size == "auto" || size === undefined){
4427 } else if(!isNaN(size) || !unitPattern.test(size)){
4428 size = size + (this.defaultUnit || 'px');
4434 * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
4435 * 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>
4436 * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt><script></tt> elements. This is a browser restriction.</p>
4437 * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
4438 * exactly how to request the HTML.
4439 * @return {Ext.Element} this
4441 load : function(url, params, cb){
4442 Ext.Ajax.request(Ext.apply({
4444 url: url.url || url,
4447 indicatorText: url.indicatorText || ''
4448 }, Ext.isObject(url) ? url : {}));
4453 * Tests various css rules/browsers to determine if this element uses a border box
4456 isBorderBox : function(){
4457 return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
4461 * <p>Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode}</p>
4463 remove : function(){
4469 Ext.removeNode(dom);
4474 * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
4475 * @param {Function} overFn The function to call when the mouse enters the Element.
4476 * @param {Function} outFn The function to call when the mouse leaves the Element.
4477 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
4478 * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
4479 * @return {Ext.Element} this
4481 hover : function(overFn, outFn, scope, options){
4483 me.on('mouseenter', overFn, scope || me.dom, options);
4484 me.on('mouseleave', outFn, scope || me.dom, options);
4489 * Returns true if this element is an ancestor of the passed element
4490 * @param {HTMLElement/String} el The element to check
4491 * @return {Boolean} True if this element is an ancestor of el, else false
4493 contains : function(el){
4494 return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
4498 * Returns the value of a namespaced attribute from the element's underlying DOM node.
4499 * @param {String} namespace The namespace in which to look for the attribute
4500 * @param {String} name The attribute name
4501 * @return {String} The attribute value
4504 getAttributeNS : function(ns, name){
4505 return this.getAttribute(name, ns);
4509 * Returns the value of an attribute from the element's underlying DOM node.
4510 * @param {String} name The attribute name
4511 * @param {String} namespace (optional) The namespace in which to look for the attribute
4512 * @return {String} The attribute value
4514 getAttribute : Ext.isIE ? function(name, ns){
4516 type = typeof d[ns + ":" + name];
4518 if(['undefined', 'unknown'].indexOf(type) == -1){
4519 return d[ns + ":" + name];
4522 } : function(name, ns){
4524 return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
4528 * Update the innerHTML of this element
4529 * @param {String} html The new HTML
4530 * @return {Ext.Element} this
4532 update : function(html) {
4534 this.dom.innerHTML = html;
4540 var ep = El.prototype;
4542 El.addMethods = function(o){
4547 * Appends an event handler (shorthand for {@link #addListener}).
4548 * @param {String} eventName The name of event to handle.
4549 * @param {Function} fn The handler function the event invokes.
4550 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
4551 * @param {Object} options (optional) An object containing standard {@link #addListener} options
4552 * @member Ext.Element
4555 ep.on = ep.addListener;
4558 * Removes an event handler from this element (see {@link #removeListener} for additional notes).
4559 * @param {String} eventName The name of the event from which to remove the handler.
4560 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4561 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4562 * then this must refer to the same object.
4563 * @return {Ext.Element} this
4564 * @member Ext.Element
4567 ep.un = ep.removeListener;
4570 * true to automatically adjust width and height settings for box-model issues (default to true)
4572 ep.autoBoxAdjust = true;
4575 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
4583 * Retrieves Ext.Element objects.
4584 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4585 * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4586 * its ID, use {@link Ext.ComponentMgr#get}.</p>
4587 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4588 * object was recreated with the same id via AJAX or DOM.</p>
4589 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4590 * @return {Element} The Element object (or null if no matching element was found)
4592 * @member Ext.Element
4595 El.get = function(el){
4599 if(!el){ return null; }
4600 if (typeof el == "string") { // element id
4601 if (!(elm = DOC.getElementById(el))) {
4604 if (EC[el] && EC[el].el) {
4608 ex = El.addToCache(new El(elm));
4611 } else if (el.tagName) { // dom element
4615 if (EC[id] && EC[id].el) {
4619 ex = El.addToCache(new El(el));
4622 } else if (el instanceof El) {
4624 // refresh dom element in case no longer valid,
4625 // catch case where it hasn't been appended
4627 // If an el instance is passed, don't pass to getElementById without some kind of id
4628 if (Ext.isIE && (el.id == undefined || el.id == '')) {
4631 el.dom = DOC.getElementById(el.id) || el.dom;
4635 } else if(el.isComposite) {
4637 } else if(Ext.isArray(el)) {
4638 return El.select(el);
4639 } else if(el == DOC) {
4640 // create a bogus element object representing the document object
4642 var f = function(){};
4643 f.prototype = El.prototype;
4652 El.addToCache = function(el, id){
4662 // private method for getting and setting element data
4663 El.data = function(el, key, value){
4668 var c = EC[el.id].data;
4669 if(arguments.length == 2){
4672 return (c[key] = value);
4677 // Garbage collection - uncache elements/purge listeners on orphaned elements
4678 // so we don't hold a reference and cause the browser to retain them
4679 function garbageCollect(){
4680 if(!Ext.enableGarbageCollector){
4681 clearInterval(El.collectorThreadId);
4695 // -------------------------------------------------------
4696 // Determining what is garbage:
4697 // -------------------------------------------------------
4699 // dom node is null, definitely garbage
4700 // -------------------------------------------------------
4702 // no parentNode == direct orphan, definitely garbage
4703 // -------------------------------------------------------
4704 // !d.offsetParent && !document.getElementById(eid)
4705 // display none elements have no offsetParent so we will
4706 // also try to look it up by it's id. However, check
4707 // offsetParent first so we don't do unneeded lookups.
4708 // This enables collection of elements that are not orphans
4709 // directly, but somewhere up the line they have an orphan
4711 // -------------------------------------------------------
4712 if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
4713 if(Ext.enableListenerCollection){
4714 Ext.EventManager.removeAll(d);
4719 // Cleanup IE Object leaks
4725 EC = Ext.elCache = t;
4729 El.collectorThreadId = setInterval(garbageCollect, 30000);
4731 var flyFn = function(){};
4732 flyFn.prototype = El.prototype;
4735 El.Flyweight = function(dom){
4739 El.Flyweight.prototype = new flyFn();
4740 El.Flyweight.prototype.isFlyweight = true;
4741 El._flyweights = {};
4744 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4745 * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4746 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4747 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4748 * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4749 * @param {String/HTMLElement} el The dom node or id
4750 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4751 * (e.g. internally Ext uses "_global")
4752 * @return {Element} The shared Element object (or null if no matching element was found)
4753 * @member Ext.Element
4756 El.fly = function(el, named){
4758 named = named || '_global';
4760 if (el = Ext.getDom(el)) {
4761 (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
4762 ret = El._flyweights[named];
4768 * Retrieves Ext.Element objects.
4769 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4770 * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4771 * its ID, use {@link Ext.ComponentMgr#get}.</p>
4772 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4773 * object was recreated with the same id via AJAX or DOM.</p>
4774 * Shorthand of {@link Ext.Element#get}
4775 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4776 * @return {Element} The Element object (or null if no matching element was found)
4783 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4784 * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4785 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4786 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4787 * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4788 * @param {String/HTMLElement} el The dom node or id
4789 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4790 * (e.g. internally Ext uses "_global")
4791 * @return {Element} The shared Element object (or null if no matching element was found)
4797 // speedy lookup for elements never to box adjust
4798 var noBoxAdjust = Ext.isStrict ? {
4801 input:1, select:1, textarea:1
4803 if(Ext.isIE || Ext.isGecko){
4804 noBoxAdjust['button'] = 1;
4809 * @class Ext.Element
4811 Ext.Element.addMethods({
4813 * Stops the specified event(s) from bubbling and optionally prevents the default action
4814 * @param {String/Array} eventName an event / array of events to stop from bubbling
4815 * @param {Boolean} preventDefault (optional) true to prevent the default action too
4816 * @return {Ext.Element} this
4818 swallowEvent : function(eventName, preventDefault){
4821 e.stopPropagation();
4826 if(Ext.isArray(eventName)){
4827 Ext.each(eventName, function(e) {
4832 me.on(eventName, fn);
4837 * Create an event handler on this element such that when the event fires and is handled by this element,
4838 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
4839 * @param {String} eventName The type of event to relay
4840 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
4841 * for firing the relayed event
4843 relayEvent : function(eventName, observable){
4844 this.on(eventName, function(e){
4845 observable.fireEvent(eventName, e);
4850 * Removes worthless text nodes
4851 * @param {Boolean} forceReclean (optional) By default the element
4852 * keeps track if it has been cleaned already so
4853 * you can call this over and over. However, if you update the element and
4854 * need to force a reclean, you can pass true.
4856 clean : function(forceReclean){
4862 if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){
4867 var nx = n.nextSibling;
4868 if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){
4875 Ext.Element.data(dom, 'isCleaned', true);
4880 * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
4881 * parameter as {@link Ext.Updater#update}
4882 * @return {Ext.Element} this
4885 var um = this.getUpdater();
4886 um.update.apply(um, arguments);
4891 * Gets this element's {@link Ext.Updater Updater}
4892 * @return {Ext.Updater} The Updater
4894 getUpdater : function(){
4895 return this.updateManager || (this.updateManager = new Ext.Updater(this));
4899 * Update the innerHTML of this element, optionally searching for and processing scripts
4900 * @param {String} html The new HTML
4901 * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
4902 * @param {Function} callback (optional) For async script loading you can be notified when the update completes
4903 * @return {Ext.Element} this
4905 update : function(html, loadScripts, callback){
4911 if(loadScripts !== true){
4912 this.dom.innerHTML = html;
4913 if(Ext.isFunction(callback)){
4922 html += '<span id="' + id + '"></span>';
4924 Ext.lib.Event.onAvailable(id, function(){
4926 hd = DOC.getElementsByTagName("head")[0],
4927 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
4928 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
4929 typeRe = /\stype=([\'\"])(.*?)\1/i,
4937 while((match = re.exec(html))){
4939 srcMatch = attrs ? attrs.match(srcRe) : false;
4940 if(srcMatch && srcMatch[2]){
4941 s = DOC.createElement("script");
4942 s.src = srcMatch[2];
4943 typeMatch = attrs.match(typeRe);
4944 if(typeMatch && typeMatch[2]){
4945 s.type = typeMatch[2];
4948 }else if(match[2] && match[2].length > 0){
4949 if(window.execScript) {
4950 window.execScript(match[2]);
4952 window.eval(match[2]);
4956 el = DOC.getElementById(id);
4957 if(el){Ext.removeNode(el);}
4958 if(Ext.isFunction(callback)){
4962 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
4966 // inherit docs, overridden so we can add removeAnchor
4967 removeAllListeners : function(){
4968 this.removeAnchor();
4969 Ext.EventManager.removeAll(this.dom);
4974 * Creates a proxy element of this element
4975 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
4976 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
4977 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
4978 * @return {Ext.Element} The new proxy element
4980 createProxy : function(config, renderTo, matchBox){
4981 config = Ext.isObject(config) ? config : {tag : "div", cls: config};
4984 proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
4985 Ext.DomHelper.insertBefore(me.dom, config, true);
4987 if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded
4988 proxy.setBox(me.getBox());
4994 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
4996 * @class Ext.Element
4998 Ext.Element.addMethods({
5000 * Gets the x,y coordinates specified by the anchor position on the element.
5001 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo}
5002 * for details on supported anchor positions.
5003 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
5004 * of page coordinates
5005 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
5006 * {width: (target width), height: (target height)} (defaults to the element's current size)
5007 * @return {Array} [x, y] An array containing the element's x and y coordinates
5009 getAnchorXY : function(anchor, local, s){
5010 //Passing a different size is useful for pre-calculating anchors,
5011 //especially for anchored animations that change the el size.
5012 anchor = (anchor || "tl").toLowerCase();
5016 vp = me.dom == document.body || me.dom == document,
5017 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
5018 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),
5022 scroll = me.getScroll(),
5023 extraX = vp ? scroll.left : !local ? o[0] : 0,
5024 extraY = vp ? scroll.top : !local ? o[1] : 0,
5026 c : [r(w * 0.5), r(h * 0.5)],
5027 t : [r(w * 0.5), 0],
5028 l : [0, r(h * 0.5)],
5029 r : [w, r(h * 0.5)],
5030 b : [r(w * 0.5), h],
5038 return [xy[0] + extraX, xy[1] + extraY];
5042 * Anchors an element to another element and realigns it when the window is resized.
5043 * @param {Mixed} element The element to align to.
5044 * @param {String} position The position to align to.
5045 * @param {Array} offsets (optional) Offset the positioning by [x, y]
5046 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
5047 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
5048 * is a number, it is used as the buffer delay (defaults to 50ms).
5049 * @param {Function} callback The function to call after the animation finishes
5050 * @return {Ext.Element} this
5052 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
5055 scroll = !Ext.isEmpty(monitorScroll),
5056 action = function(){
5057 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
5058 Ext.callback(callback, Ext.fly(dom));
5060 anchor = this.getAnchor();
5062 // previous listener anchor, remove it
5063 this.removeAnchor();
5069 Ext.EventManager.onWindowResize(action, null);
5072 Ext.EventManager.on(window, 'scroll', action, null,
5073 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
5075 action.call(me); // align immediately
5080 * Remove any anchor to this element. See {@link #anchorTo}.
5081 * @return {Ext.Element} this
5083 removeAnchor : function(){
5085 anchor = this.getAnchor();
5087 if(anchor && anchor.fn){
5088 Ext.EventManager.removeResizeListener(anchor.fn);
5090 Ext.EventManager.un(window, 'scroll', anchor.fn);
5098 getAnchor : function(){
5099 var data = Ext.Element.data,
5104 var anchor = data(dom, '_anchor');
5107 anchor = data(dom, '_anchor', {});
5113 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
5114 * supported position values.
5115 * @param {Mixed} element The element to align to.
5116 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5117 * @param {Array} offsets (optional) Offset the positioning by [x, y]
5118 * @return {Array} [x, y]
5120 getAlignToXY : function(el, p, o){
5124 throw "Element.alignToXY with an element that doesn't exist";
5128 p = (!p || p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
5136 //constrain the aligned el to viewport if necessary
5140 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
5141 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
5149 docElement = doc.documentElement,
5151 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
5152 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
5153 c = false, //constrain to viewport
5156 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
5159 throw "Element.alignTo with an invalid alignment " + p;
5166 //Subtract the aligned el's internal xy from the target's offset xy
5167 //plus custom offset to get the aligned el's new offset xy
5168 a1 = me.getAnchorXY(p1, true);
5169 a2 = el.getAnchorXY(p2, false);
5171 x = a2[0] - a1[0] + o[0];
5172 y = a2[1] - a1[1] + o[1];
5178 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
5179 //perpendicular to the vp border, allow the aligned el to slide on that border,
5180 //otherwise swap the aligned el to the opposite border of the target.
5182 p1x = p1.charAt(p1.length-1);
5184 p2x = p2.charAt(p2.length-1);
5185 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
5186 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
5189 if (x + w > dw + scrollX) {
5190 x = swapX ? r.left-w : dw+scrollX-w;
5193 x = swapX ? r.right : scrollX;
5195 if (y + h > dh + scrollY) {
5196 y = swapY ? r.top-h : dh+scrollY-h;
5199 y = swapY ? r.bottom : scrollY;
5206 * Aligns this element with another element relative to the specified anchor points. If the other element is the
5207 * document it aligns it to the viewport.
5208 * The position parameter is optional, and can be specified in any one of the following formats:
5210 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
5211 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
5212 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
5213 * deprecated in favor of the newer two anchor syntax below</i>.</li>
5214 * <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
5215 * element's anchor point, and the second value is used as the target's anchor point.</li>
5217 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
5218 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
5219 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
5220 * that specified in order to enforce the viewport constraints.
5221 * Following are all of the supported anchor positions:
5224 ----- -----------------------------
5225 tl The top left corner (default)
5226 t The center of the top edge
5227 tr The top right corner
5228 l The center of the left edge
5229 c In the center of the element
5230 r The center of the right edge
5231 bl The bottom left corner
5232 b The center of the bottom edge
5233 br The bottom right corner
5237 // align el to other-el using the default positioning ("tl-bl", non-constrained)
5238 el.alignTo("other-el");
5240 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
5241 el.alignTo("other-el", "tr?");
5243 // align the bottom right corner of el with the center left edge of other-el
5244 el.alignTo("other-el", "br-l?");
5246 // align the center of el with the bottom left corner of other-el and
5247 // adjust the x position by -6 pixels (and the y position by 0)
5248 el.alignTo("other-el", "c-bl", [-6, 0]);
5250 * @param {Mixed} element The element to align to.
5251 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5252 * @param {Array} offsets (optional) Offset the positioning by [x, y]
5253 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5254 * @return {Ext.Element} this
5256 alignTo : function(element, position, offsets, animate){
5258 return me.setXY(me.getAlignToXY(element, position, offsets),
5259 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
5262 // private ==> used outside of core
5263 adjustForConstraints : function(xy, parent, offsets){
5264 return this.getConstrainToXY(parent || document, false, offsets, xy) || xy;
5267 // private ==> used outside of core
5268 getConstrainToXY : function(el, local, offsets, proposedXY){
5269 var os = {top:0, left:0, bottom:0, right: 0};
5271 return function(el, local, offsets, proposedXY){
5273 offsets = offsets ? Ext.applyIf(offsets, os) : os;
5275 var vw, vh, vx = 0, vy = 0;
5276 if(el.dom == document.body || el.dom == document){
5277 vw =Ext.lib.Dom.getViewWidth();
5278 vh = Ext.lib.Dom.getViewHeight();
5280 vw = el.dom.clientWidth;
5281 vh = el.dom.clientHeight;
5283 var vxy = el.getXY();
5289 var s = el.getScroll();
5291 vx += offsets.left + s.left;
5292 vy += offsets.top + s.top;
5294 vw -= offsets.right;
5295 vh -= offsets.bottom;
5300 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
5301 var x = xy[0], y = xy[1];
5302 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
5304 // only move it if it needs it
5307 // first validate right/bottom
5316 // then make sure top/left isn't negative
5325 return moved ? [x, y] : false;
5331 // el = Ext.get(el);
5332 // offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
5336 // s = el.getScroll(),
5337 // vxy = el.getXY(),
5338 // vx = offsets.left + s.left,
5339 // vy = offsets.top + s.top,
5340 // vw = -offsets.right,
5341 // vh = -offsets.bottom,
5344 // xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
5347 // w = me.dom.offsetWidth, h = me.dom.offsetHeight,
5348 // moved = false; // only move it if it needs it
5351 // if(el.dom == doc.body || el.dom == doc){
5352 // vw += Ext.lib.Dom.getViewWidth();
5353 // vh += Ext.lib.Dom.getViewHeight();
5355 // vw += el.dom.clientWidth;
5356 // vh += el.dom.clientHeight;
5363 // // first validate right/bottom
5364 // if(x + w > vx + vw){
5368 // if(y + h > vy + vh){
5372 // // then make sure top/left isn't negative
5381 // return moved ? [x, y] : false;
5385 * Calculates the x, y to center this element on the screen
5386 * @return {Array} The x, y values [x, y]
5388 getCenterXY : function(){
5389 return this.getAlignToXY(document, 'c-c');
5393 * Centers the Element in either the viewport, or another Element.
5394 * @param {Mixed} centerIn (optional) The element in which to center the element.
5396 center : function(centerIn){
5397 return this.alignTo(centerIn || document, 'c-c');
5401 * @class Ext.Element
5403 Ext.Element.addMethods(function(){
5404 var PARENTNODE = 'parentNode',
5405 NEXTSIBLING = 'nextSibling',
5406 PREVIOUSSIBLING = 'previousSibling',
5412 * 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)
5413 * @param {String} selector The simple selector to test
5414 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
5415 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5416 * @return {HTMLElement} The matching DOM node (or null if no match was found)
5418 findParent : function(simpleSelector, maxDepth, returnEl){
5423 if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {
5426 maxDepth = maxDepth || 50;
5427 if (isNaN(maxDepth)) {
5428 stopEl = Ext.getDom(maxDepth);
5429 maxDepth = Number.MAX_VALUE;
5431 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
5432 if(DQ.is(p, simpleSelector)){
5433 return returnEl ? GET(p) : p;
5442 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
5443 * @param {String} selector The simple selector to test
5444 * @param {Number/Mixed} maxDepth (optional) The max depth to
5445 search as a number or element (defaults to 10 || document.body)
5446 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
5447 * @return {HTMLElement} The matching DOM node (or null if no match was found)
5449 findParentNode : function(simpleSelector, maxDepth, returnEl){
5450 var p = Ext.fly(this.dom.parentNode, '_internal');
5451 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
5455 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
5456 * This is a shortcut for findParentNode() that always returns an Ext.Element.
5457 * @param {String} selector The simple selector to test
5458 * @param {Number/Mixed} maxDepth (optional) The max depth to
5459 search as a number or element (defaults to 10 || document.body)
5460 * @return {Ext.Element} The matching DOM node (or null if no match was found)
5462 up : function(simpleSelector, maxDepth){
5463 return this.findParentNode(simpleSelector, maxDepth, true);
5467 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5468 * @param {String} selector The CSS selector
5469 * @return {CompositeElement/CompositeElementLite} The composite element
5471 select : function(selector){
5472 return Ext.Element.select(selector, this.dom);
5476 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
5477 * @param {String} selector The CSS selector
5478 * @return {Array} An array of the matched nodes
5480 query : function(selector){
5481 return DQ.select(selector, this.dom);
5485 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
5486 * @param {String} selector The CSS selector
5487 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5488 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5490 child : function(selector, returnDom){
5491 var n = DQ.selectNode(selector, this.dom);
5492 return returnDom ? n : GET(n);
5496 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
5497 * @param {String} selector The CSS selector
5498 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
5499 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
5501 down : function(selector, returnDom){
5502 var n = DQ.selectNode(" > " + selector, this.dom);
5503 return returnDom ? n : GET(n);
5507 * Gets the parent node for this element, optionally chaining up trying to match a selector
5508 * @param {String} selector (optional) Find a parent node that matches the passed simple selector
5509 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5510 * @return {Ext.Element/HTMLElement} The parent node or null
5512 parent : function(selector, returnDom){
5513 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);
5517 * Gets the next sibling, skipping text nodes
5518 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5519 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5520 * @return {Ext.Element/HTMLElement} The next sibling or null
5522 next : function(selector, returnDom){
5523 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);
5527 * Gets the previous sibling, skipping text nodes
5528 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5529 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5530 * @return {Ext.Element/HTMLElement} The previous sibling or null
5532 prev : function(selector, returnDom){
5533 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);
5538 * Gets the first child, skipping text nodes
5539 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
5540 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5541 * @return {Ext.Element/HTMLElement} The first child or null
5543 first : function(selector, returnDom){
5544 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);
5548 * Gets the last child, skipping text nodes
5549 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
5550 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
5551 * @return {Ext.Element/HTMLElement} The last child or null
5553 last : function(selector, returnDom){
5554 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);
5557 matchNode : function(dir, start, selector, returnDom){
5558 var n = this.dom[start];
5560 if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){
5561 return !returnDom ? GET(n) : n;
5569 * @class Ext.Element
5571 Ext.Element.addMethods({
5573 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
5574 * @param {String} selector The CSS selector
5575 * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)
5576 * @return {CompositeElement/CompositeElementLite} The composite element
5578 select : function(selector, unique){
5579 return Ext.Element.select(selector, unique, this.dom);
5582 * @class Ext.Element
5584 Ext.Element.addMethods(
5586 var GETDOM = Ext.getDom,
5592 * Appends the passed element(s) to this element
5593 * @param {String/HTMLElement/Array/Element/CompositeElement} el
5594 * @return {Ext.Element} this
5596 appendChild: function(el){
5597 return GET(el).appendTo(this);
5601 * Appends this element to the passed element
5602 * @param {Mixed} el The new parent element
5603 * @return {Ext.Element} this
5605 appendTo: function(el){
5606 GETDOM(el).appendChild(this.dom);
5611 * Inserts this element before the passed element in the DOM
5612 * @param {Mixed} el The element before which this element will be inserted
5613 * @return {Ext.Element} this
5615 insertBefore: function(el){
5616 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);
5621 * Inserts this element after the passed element in the DOM
5622 * @param {Mixed} el The element to insert after
5623 * @return {Ext.Element} this
5625 insertAfter: function(el){
5626 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);
5631 * Inserts (or creates) an element (or DomHelper config) as the first child of this element
5632 * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
5633 * @return {Ext.Element} The new child
5635 insertFirst: function(el, returnDom){
5637 if(el.nodeType || el.dom || typeof el == 'string'){ // element
5639 this.dom.insertBefore(el, this.dom.firstChild);
5640 return !returnDom ? GET(el) : el;
5642 return this.createChild(el, this.dom.firstChild, returnDom);
5647 * Replaces the passed element with this element
5648 * @param {Mixed} el The element to replace
5649 * @return {Ext.Element} this
5651 replace: function(el){
5653 this.insertBefore(el);
5659 * Replaces this element with the passed element
5660 * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
5661 * @return {Ext.Element} this
5663 replaceWith: function(el){
5666 if(el.nodeType || el.dom || typeof el == 'string'){
5668 me.dom.parentNode.insertBefore(el, me.dom);
5670 el = DH.insertBefore(me.dom, el);
5673 delete Ext.elCache[me.id];
5674 Ext.removeNode(me.dom);
5675 me.id = Ext.id(me.dom = el);
5676 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);
5681 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
5682 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
5683 * automatically generated with the specified attributes.
5684 * @param {HTMLElement} insertBefore (optional) a child element of this element
5685 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
5686 * @return {Ext.Element} The new child element
5688 createChild: function(config, insertBefore, returnDom){
5689 config = config || {tag:'div'};
5690 return insertBefore ?
5691 DH.insertBefore(insertBefore, config, returnDom !== true) :
5692 DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config, returnDom !== true);
5696 * Creates and wraps this element with another element
5697 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
5698 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5699 * @return {HTMLElement/Element} The newly created wrapper element
5701 wrap: function(config, returnDom){
5702 var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
5703 newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
5708 * Inserts an html fragment into this element
5709 * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
5710 * @param {String} html The HTML fragment
5711 * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
5712 * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
5714 insertHtml : function(where, html, returnEl){
5715 var el = DH.insertHtml(where, this.dom, html);
5716 return returnEl ? Ext.get(el) : el;
5720 * @class Ext.Element
5722 Ext.apply(Ext.Element.prototype, function() {
5723 var GETDOM = Ext.getDom,
5729 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
5730 * @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.
5731 * @param {String} where (optional) 'before' or 'after' defaults to before
5732 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
5733 * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
5735 insertSibling: function(el, where, returnDom){
5738 isAfter = (where || 'before').toLowerCase() == 'after',
5741 if(Ext.isArray(el)){
5743 Ext.each(el, function(e) {
5744 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
5754 if(el.nodeType || el.dom){
5755 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);
5760 if (isAfter && !me.dom.nextSibling) {
5761 rt = DH.append(me.dom.parentNode, el, !returnDom);
5763 rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
5770 * @class Ext.Element
5772 Ext.Element.addMethods(function(){
5773 // local style camelizing for speed
5775 camelRe = /(-[a-z])/gi,
5777 view = document.defaultView,
5778 propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
5779 opacityRe = /alpha\(opacity=(.*)\)/i,
5780 trimRe = /^\s+|\s+$/g,
5782 PADDING = "padding",
5792 ISCLIPPED = 'isClipped',
5793 OVERFLOW = 'overflow',
5794 OVERFLOWX = 'overflow-x',
5795 OVERFLOWY = 'overflow-y',
5796 ORIGINALCLIP = 'originalClip',
5797 // special markup used throughout Ext when box wrapping elements
5798 borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
5799 paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
5800 margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
5801 data = Ext.Element.data;
5805 function camelFn(m, a) {
5806 return a.charAt(1).toUpperCase();
5809 function chkCache(prop) {
5810 return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
5814 // private ==> used by Fx
5815 adjustWidth : function(width) {
5817 var isNum = Ext.isNumber(width);
5818 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5819 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
5821 return (isNum && width < 0) ? 0 : width;
5824 // private ==> used by Fx
5825 adjustHeight : function(height) {
5827 var isNum = Ext.isNumber(height);
5828 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5829 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
5831 return (isNum && height < 0) ? 0 : height;
5836 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
5837 * @param {String/Array} className The CSS class to add, or an array of classes
5838 * @return {Ext.Element} this
5840 addClass : function(className){
5841 var me = this, i, len, v;
5842 className = Ext.isArray(className) ? className : [className];
5843 for (i=0, len = className.length; i < len; i++) {
5846 me.dom.className += (!me.hasClass(v) && v ? " " + v : "");
5853 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
5854 * @param {String/Array} className The CSS class to add, or an array of classes
5855 * @return {Ext.Element} this
5857 radioClass : function(className){
5858 var cn = this.dom.parentNode.childNodes, v;
5859 className = Ext.isArray(className) ? className : [className];
5860 for (var i=0, len = cn.length; i < len; i++) {
5862 if(v && v.nodeType == 1) {
5863 Ext.fly(v, '_internal').removeClass(className);
5866 return this.addClass(className);
5870 * Removes one or more CSS classes from the element.
5871 * @param {String/Array} className The CSS class to remove, or an array of classes
5872 * @return {Ext.Element} this
5874 removeClass : function(className){
5876 className = Ext.isArray(className) ? className : [className];
5877 if (me.dom && me.dom.className) {
5878 for (var i=0, len=className.length; i < len; i++) {
5881 me.dom.className = me.dom.className.replace(
5882 classReCache[v] = classReCache[v] || new RegExp('(?:^|\\s+)' + v + '(?:\\s+|$)', "g"), " "
5891 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
5892 * @param {String} className The CSS class to toggle
5893 * @return {Ext.Element} this
5895 toggleClass : function(className){
5896 return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
5900 * Checks if the specified CSS class exists on this element's DOM node.
5901 * @param {String} className The CSS class to check for
5902 * @return {Boolean} True if the class exists, else false
5904 hasClass : function(className){
5905 return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
5909 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
5910 * @param {String} oldClassName The CSS class to replace
5911 * @param {String} newClassName The replacement CSS class
5912 * @return {Ext.Element} this
5914 replaceClass : function(oldClassName, newClassName){
5915 return this.removeClass(oldClassName).addClass(newClassName);
5918 isStyle : function(style, val) {
5919 return this.getStyle(style) == val;
5923 * Normalizes currentStyle and computedStyle.
5924 * @param {String} property The style property whose value is returned.
5925 * @return {String} The current value of the style property for this element.
5927 getStyle : function(){
5928 return view && view.getComputedStyle ?
5941 prop = chkCache(prop);
5942 // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
5943 if(wk && /marginRight/.test(prop)){
5944 display = this.getStyle('display');
5945 el.style.display = 'inline-block';
5947 out = (v = el.style[prop]) ? v :
5948 (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
5950 // Webkit returns rgb values for transparent.
5952 if(out == 'rgba(0, 0, 0, 0)'){
5953 out = 'transparent';
5955 el.style.display = display;
5965 if(el == document) return null;
5966 if (prop == 'opacity') {
5967 if (el.style.filter.match) {
5968 if(m = el.style.filter.match(opacityRe)){
5969 var fv = parseFloat(m[1]);
5971 return fv ? fv / 100 : 0;
5977 prop = chkCache(prop);
5978 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
5983 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
5984 * are convert to standard 6 digit hex color.
5985 * @param {String} attr The css attribute
5986 * @param {String} defaultValue The default value to use when a valid color isn't found
5987 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
5990 getColor : function(attr, defaultValue, prefix){
5991 var v = this.getStyle(attr),
5992 color = Ext.isDefined(prefix) ? prefix : '#',
5995 if(!v || /transparent|inherit/.test(v)){
5996 return defaultValue;
5999 Ext.each(v.slice(4, v.length -1).split(','), function(s){
6000 h = parseInt(s, 10);
6001 color += (h < 16 ? '0' : '') + h.toString(16);
6004 v = v.replace('#', '');
6005 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
6007 return(color.length > 5 ? color.toLowerCase() : defaultValue);
6011 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
6012 * @param {String/Object} property The style property to be set, or an object of multiple styles.
6013 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
6014 * @return {Ext.Element} this
6016 setStyle : function(prop, value){
6020 if (!Ext.isObject(prop)) {
6025 for (style in prop) {
6026 value = prop[style];
6027 style == 'opacity' ?
6028 this.setOpacity(value) :
6029 this.dom.style[chkCache(style)] = value;
6035 * Set the opacity of the element
6036 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
6037 * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
6038 * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
6039 * @return {Ext.Element} this
6041 setOpacity : function(opacity, animate){
6045 if(!animate || !me.anim){
6047 var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
6048 val = s.filter.replace(opacityRe, '').replace(trimRe, '');
6051 s.filter = val + (val.length > 0 ? ' ' : '') + opac;
6053 s.opacity = opacity;
6056 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
6062 * Clears any opacity settings from this element. Required in some cases for IE.
6063 * @return {Ext.Element} this
6065 clearOpacity : function(){
6066 var style = this.dom.style;
6068 if(!Ext.isEmpty(style.filter)){
6069 style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
6072 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
6078 * Returns the offset height of the element
6079 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
6080 * @return {Number} The element's height
6082 getHeight : function(contentHeight){
6085 hidden = Ext.isIE && me.isStyle('display', 'none'),
6086 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
6088 h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
6089 return h < 0 ? 0 : h;
6093 * Returns the offset width of the element
6094 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
6095 * @return {Number} The element's width
6097 getWidth : function(contentWidth){
6100 hidden = Ext.isIE && me.isStyle('display', 'none'),
6101 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
6102 w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
6103 return w < 0 ? 0 : w;
6107 * Set the width of this Element.
6108 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6109 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6110 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6112 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6113 * @return {Ext.Element} this
6115 setWidth : function(width, animate){
6117 width = me.adjustWidth(width);
6118 !animate || !me.anim ?
6119 me.dom.style.width = me.addUnits(width) :
6120 me.anim({width : {to : width}}, me.preanim(arguments, 1));
6125 * Set the height of this Element.
6127 // change the height to 200px and animate with default configuration
6128 Ext.fly('elementId').setHeight(200, true);
6130 // change the height to 150px and animate with a custom configuration
6131 Ext.fly('elId').setHeight(150, {
6132 duration : .5, // animation will have a duration of .5 seconds
6133 // will change the content to "finished"
6134 callback: function(){ this.{@link #update}("finished"); }
6137 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6138 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
6139 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6141 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6142 * @return {Ext.Element} this
6144 setHeight : function(height, animate){
6146 height = me.adjustHeight(height);
6147 !animate || !me.anim ?
6148 me.dom.style.height = me.addUnits(height) :
6149 me.anim({height : {to : height}}, me.preanim(arguments, 1));
6154 * Gets the width of the border(s) for the specified side(s)
6155 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6156 * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
6157 * @return {Number} The width of the sides passed added together
6159 getBorderWidth : function(side){
6160 return this.addStyles(side, borders);
6164 * Gets the width of the padding(s) for the specified side(s)
6165 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6166 * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
6167 * @return {Number} The padding of the sides passed added together
6169 getPadding : function(side){
6170 return this.addStyles(side, paddings);
6174 * Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
6175 * @return {Ext.Element} this
6181 if(!data(dom, ISCLIPPED)){
6182 data(dom, ISCLIPPED, true);
6183 data(dom, ORIGINALCLIP, {
6184 o: me.getStyle(OVERFLOW),
6185 x: me.getStyle(OVERFLOWX),
6186 y: me.getStyle(OVERFLOWY)
6188 me.setStyle(OVERFLOW, HIDDEN);
6189 me.setStyle(OVERFLOWX, HIDDEN);
6190 me.setStyle(OVERFLOWY, HIDDEN);
6196 * Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
6197 * @return {Ext.Element} this
6199 unclip : function(){
6203 if(data(dom, ISCLIPPED)){
6204 data(dom, ISCLIPPED, false);
6205 var o = data(dom, ORIGINALCLIP);
6207 me.setStyle(OVERFLOW, o.o);
6210 me.setStyle(OVERFLOWX, o.x);
6213 me.setStyle(OVERFLOWY, o.y);
6220 addStyles : function(sides, styles){
6222 m = sides.match(/\w/g),
6224 for (var i=0, len=m.length; i<len; i++) {
6225 s = m[i] && parseInt(this.getStyle(styles[m[i]]), 10);
6238 * @class Ext.Element
6241 // special markup used throughout Ext when box wrapping elements
6242 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>';
6244 Ext.Element.addMethods(function(){
6245 var INTERNAL = "_internal",
6246 pxMatch = /(\d+\.?\d+)px/;
6249 * More flexible version of {@link #setStyle} for setting style properties.
6250 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
6251 * a function which returns such a specification.
6252 * @return {Ext.Element} this
6254 applyStyles : function(style){
6255 Ext.DomHelper.applyStyles(this.dom, style);
6260 * Returns an object with properties matching the styles requested.
6261 * For example, el.getStyles('color', 'font-size', 'width') might return
6262 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
6263 * @param {String} style1 A style name
6264 * @param {String} style2 A style name
6265 * @param {String} etc.
6266 * @return {Object} The style object
6268 getStyles : function(){
6270 Ext.each(arguments, function(v) {
6271 ret[v] = this.getStyle(v);
6277 // private ==> used by ext full
6278 setOverflow : function(v){
6280 if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
6281 dom.style.overflow = 'hidden';
6282 (function(){dom.style.overflow = 'auto';}).defer(1);
6284 dom.style.overflow = v;
6289 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
6290 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
6291 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
6292 * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}). The markup
6293 * is of this form:</p>
6295 Ext.Element.boxMarkup =
6296 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
6297 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
6298 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
6300 * <p>Example usage:</p>
6303 Ext.get("foo").boxWrap();
6305 // You can also add a custom class and use CSS inheritance rules to customize the box look.
6306 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
6307 // for how to create a custom box wrap style.
6308 Ext.get("foo").boxWrap().addClass("x-box-blue");
6310 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
6311 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
6312 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
6313 * also supply all of the necessary rules.
6314 * @return {Ext.Element} The outermost wrapping element of the created box structure.
6316 boxWrap : function(cls){
6317 cls = cls || 'x-box';
6318 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)));
6319 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
6324 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
6325 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6326 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6327 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6328 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
6330 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6331 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6332 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6334 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6335 * @return {Ext.Element} this
6337 setSize : function(width, height, animate){
6339 if(Ext.isObject(width)){ // in case of object from getSize()
6340 height = width.height;
6341 width = width.width;
6343 width = me.adjustWidth(width);
6344 height = me.adjustHeight(height);
6345 if(!animate || !me.anim){
6346 me.dom.style.width = me.addUnits(width);
6347 me.dom.style.height = me.addUnits(height);
6349 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
6355 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
6356 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
6357 * if a height has not been set using CSS.
6360 getComputedHeight : function(){
6362 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
6364 h = parseFloat(me.getStyle('height')) || 0;
6365 if(!me.isBorderBox()){
6366 h += me.getFrameWidth('tb');
6373 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
6374 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
6375 * if a width has not been set using CSS.
6378 getComputedWidth : function(){
6379 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
6381 w = parseFloat(this.getStyle('width')) || 0;
6382 if(!this.isBorderBox()){
6383 w += this.getFrameWidth('lr');
6390 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
6391 for more information about the sides.
6392 * @param {String} sides
6395 getFrameWidth : function(sides, onlyContentBox){
6396 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
6400 * Sets up event handlers to add and remove a css class when the mouse is over this element
6401 * @param {String} className
6402 * @return {Ext.Element} this
6404 addClassOnOver : function(className){
6407 Ext.fly(this, INTERNAL).addClass(className);
6410 Ext.fly(this, INTERNAL).removeClass(className);
6417 * Sets up event handlers to add and remove a css class when this element has the focus
6418 * @param {String} className
6419 * @return {Ext.Element} this
6421 addClassOnFocus : function(className){
6422 this.on("focus", function(){
6423 Ext.fly(this, INTERNAL).addClass(className);
6425 this.on("blur", function(){
6426 Ext.fly(this, INTERNAL).removeClass(className);
6432 * 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)
6433 * @param {String} className
6434 * @return {Ext.Element} this
6436 addClassOnClick : function(className){
6438 this.on("mousedown", function(){
6439 Ext.fly(dom, INTERNAL).addClass(className);
6440 var d = Ext.getDoc(),
6442 Ext.fly(dom, INTERNAL).removeClass(className);
6443 d.removeListener("mouseup", fn);
6445 d.on("mouseup", fn);
6451 * <p>Returns the dimensions of the element available to lay content out in.<p>
6452 * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
6453 * example:<pre><code>
6454 var vpSize = Ext.getBody().getViewSize();
6456 // all Windows created afterwards will have a default value of 90% height and 95% width
6457 Ext.Window.override({
6458 width: vpSize.width * 0.9,
6459 height: vpSize.height * 0.95
6461 // To handle window resizing you would have to hook onto onWindowResize.
6464 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
6465 * To obtain the size including scrollbars, use getStyleSize
6467 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6470 getViewSize : function(){
6473 isDoc = (d == doc || d == doc.body);
6475 // If the body, use Ext.lib.Dom
6477 var extdom = Ext.lib.Dom;
6479 width : extdom.getViewWidth(),
6480 height : extdom.getViewHeight()
6483 // Else use clientHeight/clientWidth
6486 width : d.clientWidth,
6487 height : d.clientHeight
6493 * <p>Returns the dimensions of the element available to lay content out in.<p>
6495 * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
6496 * To obtain the size excluding scrollbars, use getViewSize
6498 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
6501 getStyleSize : function(){
6506 isDoc = (d == doc || d == doc.body),
6509 // If the body, use Ext.lib.Dom
6511 var extdom = Ext.lib.Dom;
6513 width : extdom.getViewWidth(),
6514 height : extdom.getViewHeight()
6517 // Use Styles if they are set
6518 if(s.width && s.width != 'auto'){
6519 w = parseFloat(s.width);
6520 if(me.isBorderBox()){
6521 w -= me.getFrameWidth('lr');
6524 // Use Styles if they are set
6525 if(s.height && s.height != 'auto'){
6526 h = parseFloat(s.height);
6527 if(me.isBorderBox()){
6528 h -= me.getFrameWidth('tb');
6531 // Use getWidth/getHeight if style not set.
6532 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
6536 * Returns the size of the element.
6537 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
6538 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
6540 getSize : function(contentSize){
6541 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
6545 * Forces the browser to repaint this element
6546 * @return {Ext.Element} this
6548 repaint : function(){
6550 this.addClass("x-repaint");
6551 setTimeout(function(){
6552 Ext.fly(dom).removeClass("x-repaint");
6558 * Disables text selection for this element (normalized across browsers)
6559 * @return {Ext.Element} this
6561 unselectable : function(){
6562 this.dom.unselectable = "on";
6563 return this.swallowEvent("selectstart", true).
6564 applyStyles("-moz-user-select:none;-khtml-user-select:none;").
6565 addClass("x-unselectable");
6569 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
6570 * then it returns the calculated width of the sides (see getPadding)
6571 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
6572 * @return {Object/Number}
6574 getMargins : function(side){
6577 hash = {t:"top", l:"left", r:"right", b: "bottom"},
6581 for (key in me.margins){
6582 o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
6586 return me.addStyles.call(me, side, me.margins);
6592 * @class Ext.Element
6595 var D = Ext.lib.Dom,
6600 POSITION = "position",
6602 RELATIVE = "relative",
6606 Ext.Element.addMethods({
6608 * 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).
6609 * @return {Number} The X position of the element
6612 return D.getX(this.dom);
6616 * 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).
6617 * @return {Number} The Y position of the element
6620 return D.getY(this.dom);
6624 * 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).
6625 * @return {Array} The XY position of the element
6628 return D.getXY(this.dom);
6632 * 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.
6633 * @param {Mixed} element The element to get the offsets from.
6634 * @return {Array} The XY page offsets (e.g. [100, -200])
6636 getOffsetsTo : function(el){
6637 var o = this.getXY(),
6638 e = Ext.fly(el, '_internal').getXY();
6639 return [o[0]-e[0],o[1]-e[1]];
6643 * 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).
6644 * @param {Number} The X position of the element
6645 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6646 * @return {Ext.Element} this
6648 setX : function(x, animate){
6649 return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));
6653 * 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).
6654 * @param {Number} The Y position of the element
6655 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6656 * @return {Ext.Element} this
6658 setY : function(y, animate){
6659 return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));
6663 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
6664 * @param {String} left The left CSS property value
6665 * @return {Ext.Element} this
6667 setLeft : function(left){
6668 this.setStyle(LEFT, this.addUnits(left));
6673 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
6674 * @param {String} top The top CSS property value
6675 * @return {Ext.Element} this
6677 setTop : function(top){
6678 this.setStyle(TOP, this.addUnits(top));
6683 * Sets the element's CSS right style.
6684 * @param {String} right The right CSS property value
6685 * @return {Ext.Element} this
6687 setRight : function(right){
6688 this.setStyle(RIGHT, this.addUnits(right));
6693 * Sets the element's CSS bottom style.
6694 * @param {String} bottom The bottom CSS property value
6695 * @return {Ext.Element} this
6697 setBottom : function(bottom){
6698 this.setStyle(BOTTOM, this.addUnits(bottom));
6703 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6704 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6705 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
6706 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6707 * @return {Ext.Element} this
6709 setXY : function(pos, animate){
6711 if(!animate || !me.anim){
6712 D.setXY(me.dom, pos);
6714 me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');
6720 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6721 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6722 * @param {Number} x X value for new position (coordinates are page-based)
6723 * @param {Number} y Y value for new position (coordinates are page-based)
6724 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6725 * @return {Ext.Element} this
6727 setLocation : function(x, y, animate){
6728 return this.setXY([x, y], this.animTest(arguments, animate, 2));
6732 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
6733 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
6734 * @param {Number} x X value for new position (coordinates are page-based)
6735 * @param {Number} y Y value for new position (coordinates are page-based)
6736 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
6737 * @return {Ext.Element} this
6739 moveTo : function(x, y, animate){
6740 return this.setXY([x, y], this.animTest(arguments, animate, 2));
6744 * Gets the left X coordinate
6745 * @param {Boolean} local True to get the local css position instead of page coordinate
6748 getLeft : function(local){
6749 return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
6753 * Gets the right X coordinate of the element (element X position + element width)
6754 * @param {Boolean} local True to get the local css position instead of page coordinate
6757 getRight : function(local){
6759 return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
6763 * Gets the top Y coordinate
6764 * @param {Boolean} local True to get the local css position instead of page coordinate
6767 getTop : function(local) {
6768 return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
6772 * Gets the bottom Y coordinate of the element (element Y position + element height)
6773 * @param {Boolean} local True to get the local css position instead of page coordinate
6776 getBottom : function(local){
6778 return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
6782 * Initializes positioning on this element. If a desired position is not passed, it will make the
6783 * the element positioned relative IF it is not already positioned.
6784 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
6785 * @param {Number} zIndex (optional) The zIndex to apply
6786 * @param {Number} x (optional) Set the page X position
6787 * @param {Number} y (optional) Set the page Y position
6789 position : function(pos, zIndex, x, y){
6792 if(!pos && me.isStyle(POSITION, STATIC)){
6793 me.setStyle(POSITION, RELATIVE);
6795 me.setStyle(POSITION, pos);
6798 me.setStyle(ZINDEX, zIndex);
6800 if(x || y) me.setXY([x || false, y || false]);
6804 * Clear positioning back to the default when the document was loaded
6805 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
6806 * @return {Ext.Element} this
6808 clearPositioning : function(value){
6809 value = value || '';
6822 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
6823 * snapshot before performing an update and then restoring the element.
6826 getPositioning : function(){
6827 var l = this.getStyle(LEFT);
6828 var t = this.getStyle(TOP);
6830 "position" : this.getStyle(POSITION),
6832 "right" : l ? "" : this.getStyle(RIGHT),
6834 "bottom" : t ? "" : this.getStyle(BOTTOM),
6835 "z-index" : this.getStyle(ZINDEX)
6840 * Set positioning with an object returned by getPositioning().
6841 * @param {Object} posCfg
6842 * @return {Ext.Element} this
6844 setPositioning : function(pc){
6846 style = me.dom.style;
6850 if(pc.right == AUTO){
6853 if(pc.bottom == AUTO){
6861 * Translates the passed page coordinates into left/top css values for this element
6862 * @param {Number/Array} x The page x or an array containing [x, y]
6863 * @param {Number} y (optional) The page y, required if x is not an array
6864 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
6866 translatePoints : function(x, y){
6867 y = isNaN(x[1]) ? y : x[1];
6868 x = isNaN(x[0]) ? x : x[0];
6870 relative = me.isStyle(POSITION, RELATIVE),
6872 l = parseInt(me.getStyle(LEFT), 10),
6873 t = parseInt(me.getStyle(TOP), 10);
6875 l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
6876 t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);
6878 return {left: (x - o[0] + l), top: (y - o[1] + t)};
6881 animTest : function(args, animate, i) {
6882 return !!animate && this.preanim ? this.preanim(args, i) : false;
6886 * @class Ext.Element
6888 Ext.Element.addMethods({
6890 * 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.
6891 * @param {Object} box The box to fill {x, y, width, height}
6892 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
6893 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6894 * @return {Ext.Element} this
6896 setBox : function(box, adjust, animate){
6900 if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
6901 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
6902 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
6904 me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
6909 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
6910 * set another Element's size/location to match this element.
6911 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
6912 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
6913 * @return {Object} box An object in the format<pre><code>
6915 x: <Element's X position>,
6916 y: <Element's Y position>,
6917 width: <Element's width>,
6918 height: <Element's height>,
6919 bottom: <Element's lower bound>,
6920 right: <Element's rightmost bound>
6923 * The returned object may also be addressed as an Array where index 0 contains the X position
6924 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
6926 getBox : function(contentBox, local) {
6931 getBorderWidth = me.getBorderWidth,
6932 getPadding = me.getPadding,
6940 left = parseInt(me.getStyle("left"), 10) || 0;
6941 top = parseInt(me.getStyle("top"), 10) || 0;
6944 var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
6946 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
6948 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
6949 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
6950 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
6951 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
6952 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)};
6954 bx.right = bx.x + bx.width;
6955 bx.bottom = bx.y + bx.height;
6960 * Move this element relative to its current position.
6961 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
6962 * @param {Number} distance How far to move the element in pixels
6963 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6964 * @return {Ext.Element} this
6966 move : function(direction, distance, animate){
6971 left = [x - distance, y],
6972 right = [x + distance, y],
6973 top = [x, y - distance],
6974 bottom = [x, y + distance],
6988 direction = direction.toLowerCase();
6989 me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
6993 * Quick set left and top adding default units
6994 * @param {String} left The left CSS property value
6995 * @param {String} top The top CSS property value
6996 * @return {Ext.Element} this
6998 setLeftTop : function(left, top){
7000 style = me.dom.style;
7001 style.left = me.addUnits(left);
7002 style.top = me.addUnits(top);
7007 * Returns the region of the given element.
7008 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7009 * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
7011 getRegion : function(){
7012 return Ext.lib.Dom.getRegion(this.dom);
7016 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7017 * @param {Number} x X value for new position (coordinates are page-based)
7018 * @param {Number} y Y value for new position (coordinates are page-based)
7019 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
7020 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7021 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
7023 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
7024 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
7025 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
7027 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7028 * @return {Ext.Element} this
7030 setBounds : function(x, y, width, height, animate){
7032 if (!animate || !me.anim) {
7033 me.setSize(width, height);
7034 me.setLocation(x, y);
7036 me.anim({points: {to: [x, y]},
7037 width: {to: me.adjustWidth(width)},
7038 height: {to: me.adjustHeight(height)}},
7039 me.preanim(arguments, 4),
7046 * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
7047 * @param {Ext.lib.Region} region The region to fill
7048 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7049 * @return {Ext.Element} this
7051 setRegion : function(region, animate) {
7052 return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
7055 * @class Ext.Element
7057 Ext.Element.addMethods({
7059 * Returns true if this element is scrollable.
7062 isScrollable : function(){
7064 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
7068 * 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().
7069 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7070 * @param {Number} value The new scroll value.
7071 * @return {Element} this
7073 scrollTo : function(side, value){
7074 this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;
7079 * Returns the current scroll position of the element.
7080 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
7082 getScroll : function(){
7086 docElement = doc.documentElement,
7091 if(d == doc || d == body){
7092 if(Ext.isIE && Ext.isStrict){
7093 l = docElement.scrollLeft;
7094 t = docElement.scrollTop;
7096 l = window.pageXOffset;
7097 t = window.pageYOffset;
7099 ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
7101 ret = {left: d.scrollLeft, top: d.scrollTop};
7106 * @class Ext.Element
7108 Ext.Element.addMethods({
7110 * 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().
7111 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
7112 * @param {Number} value The new scroll value
7113 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7114 * @return {Element} this
7116 scrollTo : function(side, value, animate){
7117 var top = /top/i.test(side), //check if we're scrolling top or left
7121 if (!animate || !me.anim) {
7122 prop = 'scroll' + (top ? 'Top' : 'Left'), // just setting the value, so grab the direction
7125 prop = 'scroll' + (top ? 'Left' : 'Top'), // if scrolling top, we need to grab scrollLeft, if left, scrollTop
7126 me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}},
7127 me.preanim(arguments, 2), 'scroll');
7133 * Scrolls this element into view within the passed container.
7134 * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a
7135 * string (id), dom node, or Ext.Element.
7136 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7137 * @return {Ext.Element} this
7139 scrollIntoView : function(container, hscroll){
7140 var c = Ext.getDom(container) || Ext.getBody().dom,
7142 o = this.getOffsetsTo(c),
7143 l = o[0] + c.scrollLeft,
7144 t = o[1] + c.scrollTop,
7145 b = t + el.offsetHeight,
7146 r = l + el.offsetWidth,
7147 ch = c.clientHeight,
7148 ct = parseInt(c.scrollTop, 10),
7149 cl = parseInt(c.scrollLeft, 10),
7151 cr = cl + c.clientWidth;
7153 if (el.offsetHeight > ch || t < ct) {
7158 c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore
7160 if(hscroll !== false){
7161 if(el.offsetWidth > c.clientWidth || l < cl){
7164 c.scrollLeft = r - c.clientWidth;
7166 c.scrollLeft = c.scrollLeft;
7172 scrollChildIntoView : function(child, hscroll){
7173 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7177 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
7178 * within this element's scrollable range.
7179 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
7180 * @param {Number} distance How far to scroll the element in pixels
7181 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7182 * @return {Boolean} Returns true if a scroll was triggered or false if the element
7183 * was scrolled as far as it could go.
7185 scroll : function(direction, distance, animate){
7186 if(!this.isScrollable()){
7190 l = el.scrollLeft, t = el.scrollTop,
7191 w = el.scrollWidth, h = el.scrollHeight,
7192 cw = el.clientWidth, ch = el.clientHeight,
7193 scrolled = false, v,
7195 l: Math.min(l + distance, w-cw),
7196 r: v = Math.max(l - distance, 0),
7197 t: Math.max(t - distance, 0),
7198 b: Math.min(t + distance, h-ch)
7203 direction = direction.substr(0, 1);
7204 if((v = hash[direction]) > -1){
7206 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
7211 * @class Ext.Element
7214 * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
7218 Ext.Element.VISIBILITY = 1;
7220 * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
7224 Ext.Element.DISPLAY = 2;
7226 Ext.Element.addMethods(function(){
7227 var VISIBILITY = "visibility",
7228 DISPLAY = "display",
7230 OFFSETS = "offsets",
7232 ORIGINALDISPLAY = 'originalDisplay',
7233 VISMODE = 'visibilityMode',
7234 ELDISPLAY = Ext.Element.DISPLAY,
7235 data = Ext.Element.data,
7236 getDisplay = function(dom){
7237 var d = data(dom, ORIGINALDISPLAY);
7238 if(d === undefined){
7239 data(dom, ORIGINALDISPLAY, d = '');
7243 getVisMode = function(dom){
7244 var m = data(dom, VISMODE);
7245 if(m === undefined){
7246 data(dom, VISMODE, m = 1);
7253 * The element's default display mode (defaults to "")
7256 originalDisplay : "",
7260 * Sets the element's visibility mode. When setVisible() is called it
7261 * will use this to determine whether to set the visibility or the display property.
7262 * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
7263 * @return {Ext.Element} this
7265 setVisibilityMode : function(visMode){
7266 data(this.dom, VISMODE, visMode);
7271 * Perform custom animation on this element.
7272 * <div><ul class="mdetail-params">
7273 * <li><u>Animation Properties</u></li>
7275 * <p>The Animation Control Object enables gradual transitions for any member of an
7276 * element's style object that takes a numeric value including but not limited to
7277 * these properties:</p><div><ul class="mdetail-params">
7278 * <li><tt>bottom, top, left, right</tt></li>
7279 * <li><tt>height, width</tt></li>
7280 * <li><tt>margin, padding</tt></li>
7281 * <li><tt>borderWidth</tt></li>
7282 * <li><tt>opacity</tt></li>
7283 * <li><tt>fontSize</tt></li>
7284 * <li><tt>lineHeight</tt></li>
7288 * <li><u>Animation Property Attributes</u></li>
7290 * <p>Each Animation Property is a config object with optional properties:</p>
7291 * <div><ul class="mdetail-params">
7292 * <li><tt>by</tt>* : relative change - start at current value, change by this value</li>
7293 * <li><tt>from</tt> : ignore current value, start from this value</li>
7294 * <li><tt>to</tt>* : start at current value, go to this value</li>
7295 * <li><tt>unit</tt> : any allowable unit specification</li>
7296 * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>
7299 * <li><u>Animation Types</u></li>
7301 * <p>The supported animation types:</p><div><ul class="mdetail-params">
7302 * <li><tt>'run'</tt> : Default
7304 var el = Ext.get('complexEl');
7306 // animation control object
7308 borderWidth: {to: 3, from: 0},
7309 opacity: {to: .3, from: 1},
7310 height: {to: 50, from: el.getHeight()},
7311 width: {to: 300, from: el.getWidth()},
7312 top : {by: - 100, unit: 'px'},
7314 0.35, // animation duration
7316 'easeOut', // easing method
7317 'run' // animation type ('run','color','motion','scroll')
7321 * <li><tt>'color'</tt>
7322 * <p>Animates transition of background, text, or border colors.</p>
7325 // animation control object
7327 color: { to: '#06e' },
7328 backgroundColor: { to: '#e06' }
7330 0.35, // animation duration
7332 'easeOut', // easing method
7333 'color' // animation type ('run','color','motion','scroll')
7338 * <li><tt>'motion'</tt>
7339 * <p>Animates the motion of an element to/from specific points using optional bezier
7340 * way points during transit.</p>
7343 // animation control object
7345 borderWidth: {to: 3, from: 0},
7346 opacity: {to: .3, from: 1},
7347 height: {to: 50, from: el.getHeight()},
7348 width: {to: 300, from: el.getWidth()},
7349 top : {by: - 100, unit: 'px'},
7351 to: [50, 100], // go to this point
7352 control: [ // optional bezier way points
7358 3000, // animation duration (milliseconds!)
7360 'easeOut', // easing method
7361 'motion' // animation type ('run','color','motion','scroll')
7365 * <li><tt>'scroll'</tt>
7366 * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>
7369 // animation control object
7371 scroll: {to: [400, 300]}
7373 0.35, // animation duration
7375 'easeOut', // easing method
7376 'scroll' // animation type ('run','color','motion','scroll')
7384 * @param {Object} args The animation control args
7385 * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)
7386 * @param {Function} onComplete (optional) Function to call when animation completes
7387 * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)
7388 * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,
7389 * <tt>'motion'</tt>, or <tt>'scroll'</tt>
7390 * @return {Ext.Element} this
7392 animate : function(args, duration, onComplete, easing, animType){
7393 this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7398 * @private Internal animation call
7400 anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7401 animType = animType || 'run';
7404 anim = Ext.lib.Anim[animType](
7407 (opt.duration || defaultDur) || .35,
7408 (opt.easing || defaultEase) || 'easeOut',
7411 if(opt.callback) opt.callback.call(opt.scope || me, me, opt);
7419 // private legacy anim prep
7420 preanim : function(a, i){
7421 return !a[i] ? false : (Ext.isObject(a[i]) ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7425 * Checks whether the element is currently visible using both visibility and display properties.
7426 * @return {Boolean} True if the element is currently visible, else false
7428 isVisible : function() {
7429 return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);
7433 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7434 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7435 * @param {Boolean} visible Whether the element is visible
7436 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7437 * @return {Ext.Element} this
7439 setVisible : function(visible, animate){
7440 var me = this, isDisplay, isVisible, isOffsets,
7443 // hideMode string override
7444 if (Ext.isString(animate)){
7445 isDisplay = animate == DISPLAY;
7446 isVisible = animate == VISIBILITY;
7447 isOffsets = animate == OFFSETS;
7450 isDisplay = getVisMode(this.dom) == ELDISPLAY;
7451 isVisible = !isDisplay;
7454 if (!animate || !me.anim) {
7456 me.setDisplayed(visible);
7457 } else if (isOffsets){
7459 me.hideModeStyles = {
7460 position: me.getStyle('position'),
7461 top: me.getStyle('top'),
7462 left: me.getStyle('left')
7465 me.applyStyles({position: 'absolute', top: '-10000px', left: '-10000px'});
7467 me.applyStyles(me.hideModeStyles || {position: '', top: '', left: ''});
7471 dom.style.visibility = visible ? "visible" : HIDDEN;
7474 // closure for composites
7477 me.setVisible(true);
7479 me.anim({opacity: { to: (visible?1:0) }},
7480 me.preanim(arguments, 1),
7486 dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;
7487 Ext.fly(dom).setOpacity(1);
7495 * Toggles the element's visibility or display, depending on visibility mode.
7496 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7497 * @return {Ext.Element} this
7499 toggle : function(animate){
7501 me.setVisible(!me.isVisible(), me.preanim(arguments, 0));
7506 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7507 * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
7508 * @return {Ext.Element} this
7510 setDisplayed : function(value) {
7511 if(typeof value == "boolean"){
7512 value = value ? getDisplay(this.dom) : NONE;
7514 this.setStyle(DISPLAY, value);
7519 fixDisplay : function(){
7521 if(me.isStyle(DISPLAY, NONE)){
7522 me.setStyle(VISIBILITY, HIDDEN);
7523 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
7524 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
7525 me.setStyle(DISPLAY, "block");
7531 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7532 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7533 * @return {Ext.Element} this
7535 hide : function(animate){
7536 // hideMode override
7537 if (Ext.isString(animate)){
7538 this.setVisible(false, animate);
7541 this.setVisible(false, this.preanim(arguments, 0));
7546 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
7547 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7548 * @return {Ext.Element} this
7550 show : function(animate){
7551 // hideMode override
7552 if (Ext.isString(animate)){
7553 this.setVisible(true, animate);
7556 this.setVisible(true, this.preanim(arguments, 0));
7561 * @class Ext.Element
7563 Ext.Element.addMethods(
7565 var VISIBILITY = "visibility",
7566 DISPLAY = "display",
7569 XMASKED = "x-masked",
7570 XMASKEDRELATIVE = "x-masked-relative",
7571 data = Ext.Element.data;
7575 * Checks whether the element is currently visible using both visibility and display properties.
7576 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7577 * @return {Boolean} True if the element is currently visible, else false
7579 isVisible : function(deep) {
7580 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),
7581 p = this.dom.parentNode;
7582 if(deep !== true || !vis){
7585 while(p && !/^body/i.test(p.tagName)){
7586 if(!Ext.fly(p, '_isVisible').isVisible()){
7595 * Returns true if display is not "none"
7598 isDisplayed : function() {
7599 return !this.isStyle(DISPLAY, NONE);
7603 * Convenience method for setVisibilityMode(Element.DISPLAY)
7604 * @param {String} display (optional) What to set display to when visible
7605 * @return {Ext.Element} this
7607 enableDisplayMode : function(display){
7608 this.setVisibilityMode(Ext.Element.DISPLAY);
7609 if(!Ext.isEmpty(display)){
7610 data(this.dom, 'originalDisplay', display);
7616 * Puts a mask over this element to disable user interaction. Requires core.css.
7617 * This method can only be applied to elements which accept child nodes.
7618 * @param {String} msg (optional) A message to display in the mask
7619 * @param {String} msgCls (optional) A css class to apply to the msg element
7620 * @return {Element} The mask element
7622 mask : function(msg, msgCls){
7626 EXTELMASKMSG = "ext-el-mask-msg",
7630 if(!/^body/i.test(dom.tagName) && me.getStyle('position') == 'static'){
7631 me.addClass(XMASKEDRELATIVE);
7633 if((el = data(dom, 'maskMsg'))){
7636 if((el = data(dom, 'mask'))){
7640 mask = dh.append(dom, {cls : "ext-el-mask"}, true);
7641 data(dom, 'mask', mask);
7643 me.addClass(XMASKED);
7644 mask.setDisplayed(true);
7645 if(typeof msg == 'string'){
7646 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
7647 data(dom, 'maskMsg', mm);
7648 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
7649 mm.dom.firstChild.innerHTML = msg;
7650 mm.setDisplayed(true);
7653 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically
7654 mask.setSize(undefined, me.getHeight());
7660 * Removes a previously applied mask.
7662 unmask : function(){
7665 mask = data(dom, 'mask'),
7666 maskMsg = data(dom, 'maskMsg');
7670 data(dom, 'maskMsg', undefined);
7673 data(dom, 'mask', undefined);
7675 me.removeClass([XMASKED, XMASKEDRELATIVE]);
7679 * Returns true if this element is masked
7682 isMasked : function(){
7683 var m = data(this.dom, 'mask');
7684 return m && m.isVisible();
7688 * Creates an iframe shim for this element to keep selects and other windowed objects from
7690 * @return {Ext.Element} The new shim element
7692 createShim : function(){
7693 var el = document.createElement('iframe'),
7695 el.frameBorder = '0';
7696 el.className = 'ext-shim';
7697 el.src = Ext.SSL_SECURE_URL;
7698 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
7699 shim.autoBoxAdjust = false;
7704 * @class Ext.Element
7706 Ext.Element.addMethods({
7708 * Convenience method for constructing a KeyMap
7709 * @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:
7710 * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
7711 * @param {Function} fn The function to call
7712 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
7713 * @return {Ext.KeyMap} The KeyMap created
7715 addKeyListener : function(key, fn, scope){
7717 if(!Ext.isObject(key) || Ext.isArray(key)){
7733 return new Ext.KeyMap(this, config);
7737 * Creates a KeyMap for this element
7738 * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
7739 * @return {Ext.KeyMap} The KeyMap created
7741 addKeyMap : function(config){
7742 return new Ext.KeyMap(this, config);
7747 UNDEFINED = undefined,
7761 ABSOLUTE = "absolute",
7762 VISIBLE = "visible",
7764 POSITION = "position",
7765 EASEOUT = "easeOut",
7767 * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element
7769 flyEl = new Ext.Element.Flyweight(),
7771 getObject = function(o){
7774 fly = function(dom){
7776 flyEl.id = Ext.id(dom);
7780 * Queueing now stored outside of the element due to closure issues
7782 getQueue = function(id){
7788 setQueue = function(id, value){
7792 //Notifies Element that fx methods are available
7793 Ext.enableFx = TRUE;
7797 * <p>A class to provide basic animation and visual effects support. <b>Note:</b> This class is automatically applied
7798 * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.
7799 * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be
7800 * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>
7802 * <p><b><u>Method Chaining</u></b></p>
7803 * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
7804 * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
7805 * method chain. The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
7806 * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately. For this reason,
7807 * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
7808 * expected results and should be done with care. Also see <tt>{@link #callback}</tt>.</p><br/>
7810 * <p><b><u>Anchor Options for Motion Effects</u></b></p>
7811 * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
7812 * that will serve as either the start or end point of the animation. Following are all of the supported anchor positions:</p>
7815 ----- -----------------------------
7816 tl The top left corner
7817 t The center of the top edge
7818 tr The top right corner
7819 l The center of the left edge
7820 r The center of the right edge
7821 bl The bottom left corner
7822 b The center of the bottom edge
7823 br The bottom right corner
7825 * <b>Note</b>: some Fx methods accept specific custom config parameters. The options shown in the Config Options
7826 * section below are common options that can be passed to any Fx method unless otherwise noted.</b>
7828 * @cfg {Function} callback A function called when the effect is finished. Note that effects are queued internally by the
7829 * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together
7830 * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>
7831 * el.slideIn().highlight();
7833 * The callback is intended for any additional code that should run once a particular effect has completed. The Element
7834 * being operated upon is passed as the first parameter.
7836 * @cfg {Object} scope The scope (<code>this</code> reference) in which the <tt>{@link #callback}</tt> function is executed. Defaults to the browser window.
7838 * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>
7839 * <li><b><tt>backBoth</tt></b></li>
7840 * <li><b><tt>backIn</tt></b></li>
7841 * <li><b><tt>backOut</tt></b></li>
7842 * <li><b><tt>bounceBoth</tt></b></li>
7843 * <li><b><tt>bounceIn</tt></b></li>
7844 * <li><b><tt>bounceOut</tt></b></li>
7845 * <li><b><tt>easeBoth</tt></b></li>
7846 * <li><b><tt>easeBothStrong</tt></b></li>
7847 * <li><b><tt>easeIn</tt></b></li>
7848 * <li><b><tt>easeInStrong</tt></b></li>
7849 * <li><b><tt>easeNone</tt></b></li>
7850 * <li><b><tt>easeOut</tt></b></li>
7851 * <li><b><tt>easeOutStrong</tt></b></li>
7852 * <li><b><tt>elasticBoth</tt></b></li>
7853 * <li><b><tt>elasticIn</tt></b></li>
7854 * <li><b><tt>elasticOut</tt></b></li>
7857 * @cfg {String} afterCls A css class to apply after the effect
7858 * @cfg {Number} duration The length of time (in seconds) that the effect should last
7860 * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between
7861 * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.
7863 * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
7864 * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to
7865 * effects that end with the element being visually hidden, ignored otherwise)
7866 * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object
7867 * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the
7868 * Element after the effect finishes.
7869 * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
7870 * @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
7871 * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)
7875 // private - calls the function taking arguments from the argHash based on the key. Returns the return value of the function.
7876 // this is useful for replacing switch statements (for example).
7877 switchStatements : function(key, fn, argHash){
7878 return fn.apply(this, argHash[key]);
7882 * Slides the element into view. An anchor point can be optionally passed to set the point of
7883 * origin for the slide effect. This function automatically handles wrapping the element with
7884 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
7887 // default: slide the element in from the top
7890 // custom: slide the element in from the right with a 2-second duration
7891 el.slideIn('r', { duration: 2 });
7893 // common config options shown with default values
7899 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
7900 * @param {Object} options (optional) Object literal with any of the Fx config options
7901 * @return {Ext.Element} The Element
7903 slideIn : function(anchor, o){
7919 anchor = anchor || "t";
7921 me.queueFx(o, function(){
7922 xy = fly(dom).getXY();
7923 // fix display to visibility
7924 fly(dom).fixDisplay();
7926 // restore values after effect
7927 r = fly(dom).getFxRestore();
7928 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
7929 b.right = b.x + b.width;
7930 b.bottom = b.y + b.height;
7932 // fixed size for slide
7933 fly(dom).setWidth(b.width).setHeight(b.height);
7936 wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);
7938 st.visibility = VISIBLE;
7939 st.position = ABSOLUTE;
7941 // clear out temp styles after slide and unwrap
7943 fly(dom).fxUnwrap(wrap, r.pos, o);
7945 st.height = r.height;
7946 fly(dom).afterFx(o);
7949 // time to calculate the positions
7950 pt = {to: [b.x, b.y]};
7952 bh = {to: b.height};
7954 function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){
7956 fly(wrap).setWidth(ww).setHeight(wh);
7958 fly(wrap)[sXY](sXYval);
7960 style[s1] = style[s2] = "0";
7973 args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
7974 t : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],
7975 l : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],
7976 r : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],
7977 b : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],
7978 tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],
7979 bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],
7980 br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],
7981 tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]
7984 st.visibility = VISIBLE;
7987 arguments.callee.anim = fly(wrap).fxanim(args,
7998 * Slides the element out of view. An anchor point can be optionally passed to set the end point
7999 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
8000 * 'hidden') but block elements will still take up space in the document. The element must be removed
8001 * from the DOM using the 'remove' config option if desired. This function automatically handles
8002 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
8005 // default: slide the element out to the top
8008 // custom: slide the element out to the right with a 2-second duration
8009 el.slideOut('r', { duration: 2 });
8011 // common config options shown with default values
8019 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
8020 * @param {Object} options (optional) Object literal with any of the Fx config options
8021 * @return {Ext.Element} The Element
8023 slideOut : function(anchor, o){
8035 anchor = anchor || "t";
8037 me.queueFx(o, function(){
8039 // restore values after effect
8040 r = fly(dom).getFxRestore();
8041 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
8042 b.right = b.x + b.width;
8043 b.bottom = b.y + b.height;
8045 // fixed size for slide
8046 fly(dom).setWidth(b.width).setHeight(b.height);
8049 wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);
8051 st.visibility = VISIBLE;
8052 st.position = ABSOLUTE;
8053 fly(wrap).setWidth(b.width).setHeight(b.height);
8056 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8057 fly(dom).fxUnwrap(wrap, r.pos, o);
8059 st.height = r.height;
8060 fly(dom).afterFx(o);
8063 function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){
8066 style[s1] = style[s2] = "0";
8078 a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
8079 t : [st, LEFT, BOTTOM, HEIGHT, zero],
8080 l : [st, RIGHT, TOP, WIDTH, zero],
8081 r : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],
8082 b : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8083 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],
8084 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
8085 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],
8086 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]
8089 arguments.callee.anim = fly(wrap).fxanim(a,
8100 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
8101 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
8102 * The element must be removed from the DOM using the 'remove' config option if desired.
8108 // common config options shown with default values
8116 * @param {Object} options (optional) Object literal with any of the Fx config options
8117 * @return {Ext.Element} The Element
8128 me.queueFx(o, function(){
8129 width = fly(dom).getWidth();
8130 height = fly(dom).getHeight();
8131 fly(dom).clearOpacity();
8134 // restore values after effect
8135 r = fly(dom).getFxRestore();
8138 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8139 fly(dom).clearOpacity();
8140 fly(dom).setPositioning(r.pos);
8142 st.height = r.height;
8144 fly(dom).afterFx(o);
8147 arguments.callee.anim = fly(dom).fxanim({
8148 width : {to : fly(dom).adjustWidth(width * 2)},
8149 height : {to : fly(dom).adjustHeight(height * 2)},
8150 points : {by : [-width * .5, -height * .5]},
8152 fontSize: {to : 200, unit: "%"}
8164 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
8165 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
8166 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
8172 // all config options shown with default values
8180 * @param {Object} options (optional) Object literal with any of the Fx config options
8181 * @return {Ext.Element} The Element
8183 switchOff : function(o){
8190 me.queueFx(o, function(){
8191 fly(dom).clearOpacity();
8194 // restore values after effect
8195 r = fly(dom).getFxRestore();
8198 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8199 fly(dom).clearOpacity();
8200 fly(dom).setPositioning(r.pos);
8202 st.height = r.height;
8203 fly(dom).afterFx(o);
8206 fly(dom).fxanim({opacity : {to : 0.3}},
8212 fly(dom).clearOpacity();
8216 points : {by : [0, fly(dom).getHeight() * .5]}
8230 * Highlights the Element by setting a color (applies to the background-color by default, but can be
8231 * changed using the "attr" config option) and then fading back to the original color. If no original
8232 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
8235 // default: highlight background to yellow
8238 // custom: highlight foreground text to blue for 2 seconds
8239 el.highlight("0000ff", { attr: 'color', duration: 2 });
8241 // common config options shown with default values
8242 el.highlight("ffff9c", {
8243 attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
8244 endColor: (current color) or "ffffff",
8249 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
8250 * @param {Object} options (optional) Object literal with any of the Fx config options
8251 * @return {Ext.Element} The Element
8253 highlight : function(color, o){
8257 attr = o.attr || "backgroundColor",
8261 me.queueFx(o, function(){
8262 fly(dom).clearOpacity();
8266 dom.style[attr] = restore;
8267 fly(dom).afterFx(o);
8269 restore = dom.style[attr];
8270 a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};
8271 arguments.callee.anim = fly(dom).fxanim(a,
8282 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
8285 // default: a single light blue ripple
8288 // custom: 3 red ripples lasting 3 seconds total
8289 el.frame("ff0000", 3, { duration: 3 });
8291 // common config options shown with default values
8292 el.frame("C3DAF9", 1, {
8293 duration: 1 //duration of each individual ripple.
8294 // Note: Easing is not configurable and will be ignored if included
8297 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
8298 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
8299 * @param {Object} options (optional) Object literal with any of the Fx config options
8300 * @return {Ext.Element} The Element
8302 frame : function(color, count, o){
8309 me.queueFx(o, function(){
8310 color = color || '#C3DAF9';
8311 if(color.length == 6){
8312 color = '#' + color;
8317 var xy = fly(dom).getXY(),
8318 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},
8320 proxy = fly(document.body || document.documentElement).createChild({
8322 position : ABSOLUTE,
8323 'z-index': 35000, // yee haw
8324 border : '0px solid ' + color
8327 return proxy.queueFx({}, animFn);
8331 arguments.callee.anim = {
8340 var scale = Ext.isBorderBox ? 2 : 1;
8341 active = proxy.anim({
8342 top : {from : b.y, to : b.y - 20},
8343 left : {from : b.x, to : b.x - 20},
8344 borderWidth : {from : 0, to : 10},
8345 opacity : {from : 1, to : 0},
8346 height : {from : b.height, to : b.height + 20 * scale},
8347 width : {from : b.width, to : b.width + 20 * scale}
8349 duration: o.duration || 1,
8350 callback: function() {
8352 --count > 0 ? queue() : fly(dom).afterFx(o);
8355 arguments.callee.anim = {
8368 * Creates a pause before any subsequent queued effects begin. If there are
8369 * no effects queued after the pause it will have no effect.
8374 * @param {Number} seconds The length of time to pause (in seconds)
8375 * @return {Ext.Element} The Element
8377 pause : function(seconds){
8381 this.queueFx({}, function(){
8382 t = setTimeout(function(){
8383 fly(dom).afterFx({});
8385 arguments.callee.anim = {
8389 fly(dom).afterFx({});
8397 * Fade an element in (from transparent to opaque). The ending opacity can be specified
8398 * using the <tt>{@link #endOpacity}</tt> config option.
8401 // default: fade in from opacity 0 to 100%
8404 // custom: fade in from opacity 0 to 75% over 2 seconds
8405 el.fadeIn({ endOpacity: .75, duration: 2});
8407 // common config options shown with default values
8409 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
8414 * @param {Object} options (optional) Object literal with any of the Fx config options
8415 * @return {Ext.Element} The Element
8417 fadeIn : function(o){
8421 to = o.endOpacity || 1;
8423 me.queueFx(o, function(){
8424 fly(dom).setOpacity(0);
8425 fly(dom).fixDisplay();
8426 dom.style.visibility = VISIBLE;
8427 arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},
8428 o, NULL, .5, EASEOUT, function(){
8430 fly(dom).clearOpacity();
8432 fly(dom).afterFx(o);
8439 * Fade an element out (from opaque to transparent). The ending opacity can be specified
8440 * using the <tt>{@link #endOpacity}</tt> config option. Note that IE may require
8441 * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
8444 // default: fade out from the element's current opacity to 0
8447 // custom: fade out from the element's current opacity to 25% over 2 seconds
8448 el.fadeOut({ endOpacity: .25, duration: 2});
8450 // common config options shown with default values
8452 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
8459 * @param {Object} options (optional) Object literal with any of the Fx config options
8460 * @return {Ext.Element} The Element
8462 fadeOut : function(o){
8467 to = o.endOpacity || 0;
8469 me.queueFx(o, function(){
8470 arguments.callee.anim = fly(dom).fxanim({
8471 opacity : {to : to}},
8478 Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ?
8479 style.display = "none" :
8480 style.visibility = HIDDEN;
8482 fly(dom).clearOpacity();
8484 fly(dom).afterFx(o);
8491 * Animates the transition of an element's dimensions from a starting height/width
8492 * to an ending height/width. This method is a convenience implementation of {@link shift}.
8495 // change height and width to 100x100 pixels
8498 // common config options shown with default values. The height and width will default to
8499 // the element's existing values if passed as null.
8501 [element's width],
8502 [element's height], {
8508 * @param {Number} width The new width (pass undefined to keep the original width)
8509 * @param {Number} height The new height (pass undefined to keep the original height)
8510 * @param {Object} options (optional) Object literal with any of the Fx config options
8511 * @return {Ext.Element} The Element
8513 scale : function(w, h, o){
8514 this.shift(Ext.apply({}, o, {
8522 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
8523 * Any of these properties not specified in the config object will not be changed. This effect
8524 * requires that at least one new dimension, position or opacity setting must be passed in on
8525 * the config object in order for the function to have any effect.
8528 // slide the element horizontally to x position 200 while changing the height and opacity
8529 el.shift({ x: 200, height: 50, opacity: .8 });
8531 // common config options shown with default values.
8533 width: [element's width],
8534 height: [element's height],
8535 x: [element's x position],
8536 y: [element's y position],
8537 opacity: [element's opacity],
8542 * @param {Object} options Object literal with any of the Fx config options
8543 * @return {Ext.Element} The Element
8545 shift : function(o){
8550 this.queueFx(o, function(){
8551 for (var prop in o) {
8552 if (o[prop] != UNDEFINED) {
8553 a[prop] = {to : o[prop]};
8557 a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;
8558 a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;
8560 if (a.x || a.y || a.xy) {
8562 {to : [ a.x ? a.x.to : fly(dom).getX(),
8563 a.y ? a.y.to : fly(dom).getY()]};
8566 arguments.callee.anim = fly(dom).fxanim(a,
8572 fly(dom).afterFx(o);
8579 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
8580 * ending point of the effect.
8583 // default: slide the element downward while fading out
8586 // custom: slide the element out to the right with a 2-second duration
8587 el.ghost('r', { duration: 2 });
8589 // common config options shown with default values
8597 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
8598 * @param {Object} options (optional) Object literal with any of the Fx config options
8599 * @return {Ext.Element} The Element
8601 ghost : function(anchor, o){
8606 a = {opacity: {to: 0}, points: {}},
8612 anchor = anchor || "b";
8614 me.queueFx(o, function(){
8615 // restore values after effect
8616 r = fly(dom).getFxRestore();
8617 w = fly(dom).getWidth();
8618 h = fly(dom).getHeight();
8621 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
8622 fly(dom).clearOpacity();
8623 fly(dom).setPositioning(r.pos);
8625 st.height = r.height;
8626 fly(dom).afterFx(o);
8629 pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {
8640 arguments.callee.anim = fly(dom).fxanim(a,
8650 * Ensures that all effects queued after syncFx is called on the element are
8651 * run concurrently. This is the opposite of {@link #sequenceFx}.
8652 * @return {Ext.Element} The Element
8654 syncFx : function(){
8656 me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8665 * Ensures that all effects queued after sequenceFx is called on the element are
8666 * run in sequence. This is the opposite of {@link #syncFx}.
8667 * @return {Ext.Element} The Element
8669 sequenceFx : function(){
8671 me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
8680 nextFx : function(){
8681 var ef = getQueue(this.dom.id)[0];
8688 * Returns true if the element has any effects actively running or queued, else returns false.
8689 * @return {Boolean} True if element has active effects, else false
8691 hasActiveFx : function(){
8692 return getQueue(this.dom.id)[0];
8696 * Stops any running effects and clears the element's internal effects queue if it contains
8697 * any additional effects that haven't started yet.
8698 * @return {Ext.Element} The Element
8700 stopFx : function(finish){
8703 if(me.hasActiveFx()){
8704 var cur = getQueue(id)[0];
8705 if(cur && cur.anim){
8706 if(cur.anim.isAnimated){
8707 setQueue(id, [cur]); //clear
8708 cur.anim.stop(finish !== undefined ? finish : TRUE);
8718 beforeFx : function(o){
8719 if(this.hasActiveFx() && !o.concurrent){
8730 * Returns true if the element is currently blocking so that no other effect can be queued
8731 * until this effect is finished, else returns false if blocking is not set. This is commonly
8732 * used to ensure that an effect initiated by a user action runs to completion prior to the
8733 * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
8734 * @return {Boolean} True if blocking, else false
8736 hasFxBlock : function(){
8737 var q = getQueue(this.dom.id);
8738 return q && q[0] && q[0].block;
8742 queueFx : function(o, fn){
8743 var me = fly(this.dom);
8744 if(!me.hasFxBlock()){
8745 Ext.applyIf(o, me.fxDefaults);
8747 var run = me.beforeFx(o);
8749 getQueue(me.dom.id).push(fn);
8761 fxWrap : function(pos, o, vis){
8765 if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){
8767 wrapXY = fly(dom).getXY();
8769 var div = document.createElement("div");
8770 div.style.visibility = vis;
8771 wrap = dom.parentNode.insertBefore(div, dom);
8772 fly(wrap).setPositioning(pos);
8773 if(fly(wrap).isStyle(POSITION, "static")){
8774 fly(wrap).position("relative");
8776 fly(dom).clearPositioning('auto');
8778 wrap.appendChild(dom);
8780 fly(wrap).setXY(wrapXY);
8787 fxUnwrap : function(wrap, pos, o){
8789 fly(dom).clearPositioning();
8790 fly(dom).setPositioning(pos);
8792 var pn = fly(wrap).dom.parentNode;
8793 pn.insertBefore(dom, wrap);
8799 getFxRestore : function(){
8800 var st = this.dom.style;
8801 return {pos: this.getPositioning(), width: st.width, height : st.height};
8805 afterFx : function(o){
8809 fly(dom).setStyle(o.afterStyle);
8812 fly(dom).addClass(o.afterCls);
8814 if(o.remove == TRUE){
8818 o.callback.call(o.scope, fly(dom));
8821 getQueue(id).shift();
8827 fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
8828 animType = animType || 'run';
8830 var anim = Ext.lib.Anim[animType](
8833 (opt.duration || defaultDur) || .35,
8834 (opt.easing || defaultEase) || EASEOUT,
8844 Ext.Fx.resize = Ext.Fx.scale;
8846 //When included, Ext.Fx is automatically applied to Element so that all basic
8847 //effects are available directly via the Element API
8848 Ext.Element.addMethods(Ext.Fx);
8851 * @class Ext.CompositeElementLite
8852 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
8853 * members, or to perform collective actions upon the whole set.</p>
8854 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
8855 * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
8856 * Example:<pre><code>
8857 var els = Ext.select("#some-el div.some-class");
8858 // or select directly from an existing element
8859 var el = Ext.get('some-el');
8860 el.select('div.some-class');
8862 els.setWidth(100); // all elements become 100 width
8863 els.hide(true); // all elements fade out and hide
8865 els.setWidth(100).hide(true);
8868 Ext.CompositeElementLite = function(els, root){
8870 * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
8871 * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
8872 * to augment the capabilities of the CompositeElementLite class may use it when adding
8873 * methods to the class.</p>
8874 * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
8875 * following siblings of selected elements, the code would be</p><code><pre>
8876 Ext.override(Ext.CompositeElementLite, {
8877 nextAll: function() {
8878 var els = this.elements, i, l = els.length, n, r = [], ri = -1;
8880 // Loop through all elements in this Composite, accumulating
8881 // an Array of all siblings.
8882 for (i = 0; i < l; i++) {
8883 for (n = els[i].nextSibling; n; n = n.nextSibling) {
8888 // Add all found siblings to this Composite
8893 * @property elements
8896 this.add(els, root);
8897 this.el = new Ext.Element.Flyweight();
8900 Ext.CompositeElementLite.prototype = {
8904 getElement : function(el){
8905 // Set the shared flyweight dom property to the current element
8913 transformElement : function(el){
8914 return Ext.getDom(el);
8918 * Returns the number of elements in this Composite.
8921 getCount : function(){
8922 return this.elements.length;
8925 * Adds elements to this Composite object.
8926 * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
8927 * @return {CompositeElement} This Composite object.
8929 add : function(els, root){
8931 elements = me.elements;
8935 if(Ext.isString(els)){
8936 els = Ext.Element.selectorFunction(els, root);
8937 }else if(els.isComposite){
8939 }else if(!Ext.isIterable(els)){
8943 for(var i = 0, len = els.length; i < len; ++i){
8944 elements.push(me.transformElement(els[i]));
8949 invoke : function(fn, args){
8956 for(i = 0; i < len; i++) {
8959 Ext.Element.prototype[fn].apply(me.getElement(e), args);
8965 * Returns a flyweight Element of the dom element object at the specified index
8966 * @param {Number} index
8967 * @return {Ext.Element}
8969 item : function(index){
8971 el = me.elements[index],
8975 out = me.getElement(el);
8980 // fixes scope with flyweight
8981 addListener : function(eventName, handler, scope, opt){
8982 var els = this.elements,
8986 for(i = 0; i<len; i++) {
8989 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
8995 * <p>Calls the passed function for each element in this composite.</p>
8996 * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
8997 * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
8998 * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
8999 * a reference to the dom node, use el.dom.</b></div></li>
9000 * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
9001 * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
9003 * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
9004 * @return {CompositeElement} this
9006 each : function(fn, scope){
9012 for(i = 0; i<len; i++) {
9015 e = this.getElement(e);
9016 if(fn.call(scope || e, e, me, i) === false){
9025 * Clears this Composite and adds the elements passed.
9026 * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
9027 * @return {CompositeElement} this
9029 fill : function(els){
9037 * Filters this composite to only elements that match the passed selector.
9038 * @param {String/Function} selector A string CSS selector or a comparison function.
9039 * The comparison function will be called with the following arguments:<ul>
9040 * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
9041 * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
9043 * @return {CompositeElement} this
9045 filter : function(selector){
9048 elements = me.elements,
9049 fn = Ext.isFunction(selector) ? selector
9051 return el.is(selector);
9055 me.each(function(el, self, i){
9056 if(fn(el, i) !== false){
9057 els[els.length] = me.transformElement(el);
9065 * Find the index of the passed element within the composite collection.
9066 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9067 * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
9069 indexOf : function(el){
9070 return this.elements.indexOf(this.transformElement(el));
9074 * Replaces the specified element with the passed element.
9075 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9077 * @param {Mixed} replacement The id of an element or the Element itself.
9078 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
9079 * @return {CompositeElement} this
9081 replaceElement : function(el, replacement, domReplace){
9082 var index = !isNaN(el) ? el : this.indexOf(el),
9085 replacement = Ext.getDom(replacement);
9087 d = this.elements[index];
9088 d.parentNode.insertBefore(replacement, d);
9091 this.elements.splice(index, 1, replacement);
9097 * Removes all elements.
9104 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
9108 ElProto = Ext.Element.prototype,
9109 CelProto = Ext.CompositeElementLite.prototype;
9111 for(fnName in ElProto){
9112 if(Ext.isFunction(ElProto[fnName])){
9114 CelProto[fnName] = CelProto[fnName] || function(){
9115 return this.invoke(fnName, arguments);
9117 }).call(CelProto, fnName);
9124 Ext.Element.selectorFunction = Ext.DomQuery.select;
9128 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9129 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9130 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9131 * @param {String/Array} selector The CSS selector or an array of elements
9132 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9133 * @return {CompositeElementLite/CompositeElement}
9134 * @member Ext.Element
9137 Ext.Element.select = function(selector, root){
9139 if(typeof selector == "string"){
9140 els = Ext.Element.selectorFunction(selector, root);
9141 }else if(selector.length !== undefined){
9144 throw "Invalid selector";
9146 return new Ext.CompositeElementLite(els);
9149 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9150 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9151 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9152 * @param {String/Array} selector The CSS selector or an array of elements
9153 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9154 * @return {CompositeElementLite/CompositeElement}
9158 Ext.select = Ext.Element.select;/**
9159 * @class Ext.CompositeElementLite
9161 Ext.apply(Ext.CompositeElementLite.prototype, {
9162 addElements : function(els, root){
9166 if(typeof els == "string"){
9167 els = Ext.Element.selectorFunction(els, root);
9169 var yels = this.elements;
9170 Ext.each(els, function(e) {
9171 yels.push(Ext.get(e));
9177 * Returns the first Element
9178 * @return {Ext.Element}
9181 return this.item(0);
9185 * Returns the last Element
9186 * @return {Ext.Element}
9189 return this.item(this.getCount()-1);
9193 * Returns true if this composite contains the passed element
9194 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
9197 contains : function(el){
9198 return this.indexOf(el) != -1;
9202 * Removes the specified element(s).
9203 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
9204 * or an array of any of those.
9205 * @param {Boolean} removeDom (optional) True to also remove the element from the document
9206 * @return {CompositeElement} this
9208 removeElement : function(keys, removeDom){
9210 els = this.elements,
9212 Ext.each(keys, function(val){
9213 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
9228 * @class Ext.CompositeElement
9229 * @extends Ext.CompositeElementLite
9230 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
9231 * members, or to perform collective actions upon the whole set.</p>
9232 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
9233 * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
9234 * <p>All methods return <i>this</i> and can be chained.</p>
9237 var els = Ext.select("#some-el div.some-class", true);
9238 // or select directly from an existing element
9239 var el = Ext.get('some-el');
9240 el.select('div.some-class', true);
9242 els.setWidth(100); // all elements become 100 width
9243 els.hide(true); // all elements fade out and hide
9245 els.setWidth(100).hide(true);
9248 Ext.CompositeElement = function(els, root){
9250 this.add(els, root);
9253 Ext.extend(Ext.CompositeElement, Ext.CompositeElementLite, {
9256 getElement : function(el){
9257 // In this case just return it, since we already have a reference to it
9262 transformElement : function(el){
9267 * Adds elements to this composite.
9268 * @param {String/Array} els A string CSS selector, an array of elements or an element
9269 * @return {CompositeElement} this
9273 * Returns the Element object at the specified index
9274 * @param {Number} index
9275 * @return {Ext.Element}
9279 * Iterates each <code>element</code> in this <code>composite</code>
9280 * calling the supplied function using {@link Ext#each}.
9281 * @param {Function} fn The function to be called with each
9282 * <code>element</code>. If the supplied function returns <tt>false</tt>,
9283 * iteration stops. This function is called with the following arguments:
9284 * <div class="mdetail-params"><ul>
9285 * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>
9286 * in the <code>composite</code></div></li>
9287 * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>
9288 * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>
9290 * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.
9291 * Defaults to the <code>element</code> at the current <code>index</code>
9292 * within the composite.
9293 * @return {CompositeElement} this
9298 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9299 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9300 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9301 * @param {String/Array} selector The CSS selector or an array of elements
9302 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9303 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9304 * @return {CompositeElementLite/CompositeElement}
9305 * @member Ext.Element
9308 Ext.Element.select = function(selector, unique, root){
9310 if(typeof selector == "string"){
9311 els = Ext.Element.selectorFunction(selector, root);
9312 }else if(selector.length !== undefined){
9315 throw "Invalid selector";
9318 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
9322 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
9323 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
9324 * {@link Ext.CompositeElementLite CompositeElementLite} object.
9325 * @param {String/Array} selector The CSS selector or an array of elements
9326 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
9327 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9328 * @return {CompositeElementLite/CompositeElement}
9329 * @member Ext.Element
9332 Ext.select = Ext.Element.select;(function(){
9333 var BEFOREREQUEST = "beforerequest",
9334 REQUESTCOMPLETE = "requestcomplete",
9335 REQUESTEXCEPTION = "requestexception",
9336 UNDEFINED = undefined,
9343 * @class Ext.data.Connection
9344 * @extends Ext.util.Observable
9345 * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made
9346 * either to a configured URL, or to a URL specified at request time.</p>
9347 * <p>Requests made by this class are asynchronous, and will return immediately. No data from
9348 * the server will be available to the statement immediately following the {@link #request} call.
9349 * To process returned data, use a
9350 * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>
9351 * in the request options object,
9352 * or an {@link #requestcomplete event listener}.</p>
9353 * <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
9354 * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
9355 * manner with the DOM <tt><form></tt> element temporarily modified to have its
9356 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9357 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
9358 * but removed after the return data has been gathered.</p>
9359 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9360 * server is using JSON to send the return object, then the
9361 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9362 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9363 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
9364 * "<" as "&lt;", "&" as "&amp;" etc.</p>
9365 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9366 * is created containing a <tt>responseText</tt> property in order to conform to the
9367 * requirements of event handlers and callbacks.</p>
9368 * <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>
9369 * and some server technologies (notably JEE) may require some custom processing in order to
9370 * retrieve parameter names and parameter values from the packet content.</p>
9372 * @param {Object} config a configuration object.
9374 Ext.data.Connection = function(config){
9375 Ext.apply(this, config);
9378 * @event beforerequest
9379 * Fires before a network request is made to retrieve a data object.
9380 * @param {Connection} conn This Connection object.
9381 * @param {Object} options The options config object passed to the {@link #request} method.
9385 * @event requestcomplete
9386 * Fires if the request was successfully completed.
9387 * @param {Connection} conn This Connection object.
9388 * @param {Object} response The XHR object containing the response data.
9389 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9391 * @param {Object} options The options config object passed to the {@link #request} method.
9395 * @event requestexception
9396 * Fires if an error HTTP status was returned from the server.
9397 * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
9398 * for details of HTTP status codes.
9399 * @param {Connection} conn This Connection object.
9400 * @param {Object} response The XHR object containing the response data.
9401 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
9403 * @param {Object} options The options config object passed to the {@link #request} method.
9407 Ext.data.Connection.superclass.constructor.call(this);
9410 Ext.extend(Ext.data.Connection, Ext.util.Observable, {
9412 * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>
9413 * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope
9414 * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>
9417 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9418 * extra parameters to each request made by this object. (defaults to undefined)
9421 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9422 * to each request made by this object. (defaults to undefined)
9425 * @cfg {String} method (Optional) The default HTTP method to be used for requests.
9426 * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;
9427 * otherwise, GET will be used.)
9430 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9434 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9440 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9443 disableCaching: true,
9446 * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
9447 * through a cache buster. Defaults to '_dc'
9450 disableCachingParam: '_dc',
9453 * <p>Sends an HTTP request to a remote server.</p>
9454 * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
9455 * return before the response has been received. Process any returned data
9456 * in a callback function.</p>
9459 url: 'ajax_demo/sample.json',
9460 success: function(response, opts) {
9461 var obj = Ext.decode(response.responseText);
9464 failure: function(response, opts) {
9465 console.log('server-side failure with status code ' + response.status);
9469 * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
9470 * @param {Object} options An object which may contain the following properties:<ul>
9471 * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
9472 * which to send the request, or a function to call which returns a URL string. The scope of the
9473 * function is specified by the <tt>scope</tt> option. Defaults to the configured
9474 * <tt>{@link #url}</tt>.</div></li>
9475 * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
9476 * An object containing properties which are used as parameters to the
9477 * request, a url encoded string or a function to call to get either. The scope of the function
9478 * is specified by the <tt>scope</tt> option.</div></li>
9479 * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
9480 * for the request. Defaults to the configured method, or if no method was configured,
9481 * "GET" if no parameters are being sent, and "POST" if parameters are being sent. Note that
9482 * the method name is case-sensitive and should be all caps.</div></li>
9483 * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
9484 * function to be called upon receipt of the HTTP response. The callback is
9485 * called regardless of success or failure and is passed the following
9487 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9488 * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
9489 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
9490 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
9491 * accessing elements of the response.</div></li>
9493 * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
9494 * to be called upon success of the request. The callback is passed the following
9496 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9497 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9499 * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
9500 * to be called upon failure of the request. The callback is passed the
9501 * following parameters:<ul>
9502 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
9503 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
9505 * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
9506 * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
9507 * specified as functions from which to draw values, then this also serves as the scope for those function calls.
9508 * Defaults to the browser window.</div></li>
9509 * <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>
9510 * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt><form></tt>
9511 * Element or the id of the <tt><form></tt> to pull parameters from.</div></li>
9512 * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
9513 * with the <tt>form</tt> option</b>.
9514 * <p>True if the form object is a file upload (will be set automatically if the form was
9515 * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
9516 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
9517 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
9518 * DOM <tt><form></tt> element temporarily modified to have its
9519 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9520 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
9521 * but removed after the return data has been gathered.</p>
9522 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
9523 * server is using JSON to send the return object, then the
9524 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
9525 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
9526 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
9527 * is created containing a <tt>responseText</tt> property in order to conform to the
9528 * requirements of event handlers and callbacks.</p>
9529 * <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>
9530 * and some server technologies (notably JEE) may require some custom processing in order to
9531 * retrieve parameter names and parameter values from the packet content.</p>
9533 * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
9534 * headers to set for the request.</div></li>
9535 * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
9536 * to use for the post. Note: This will be used instead of params for the post
9537 * data. Any params will be appended to the URL.</div></li>
9538 * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
9539 * data to use as the post. Note: This will be used instead of params for the post
9540 * data. Any params will be appended to the URL.</div></li>
9541 * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
9542 * to add a unique cache-buster param to GET requests.</div></li>
9544 * <p>The options object may also contain any other property which might be needed to perform
9545 * postprocessing in a callback because it is passed to callback functions.</p>
9546 * @return {Number} transactionId The id of the server transaction. This may be used
9547 * to cancel the request.
9549 request : function(o){
9551 if(me.fireEvent(BEFOREREQUEST, me, o)){
9553 if(!Ext.isEmpty(o.indicatorText)){
9554 me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";
9556 if(me.indicatorText) {
9557 Ext.getDom(o.el).innerHTML = me.indicatorText;
9559 o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {
9560 Ext.getDom(o.el).innerHTML = response.responseText;
9565 url = o.url || me.url,
9567 cb = {success: me.handleResponse,
9568 failure: me.handleFailure,
9570 argument: {options: o},
9571 timeout : o.timeout || me.timeout
9577 if (Ext.isFunction(p)) {
9578 p = p.call(o.scope||WINDOW, o);
9581 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);
9583 if (Ext.isFunction(url)) {
9584 url = url.call(o.scope || WINDOW, o);
9587 if((form = Ext.getDom(o.form))){
9588 url = url || form.action;
9589 if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) {
9590 return me.doFormUpload.call(me, o, p, url);
9592 serForm = Ext.lib.Ajax.serializeForm(form);
9593 p = p ? (p + '&' + serForm) : serForm;
9596 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);
9598 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
9599 var dcp = o.disableCachingParam || me.disableCachingParam;
9600 url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));
9603 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});
9605 if(o.autoAbort === true || me.autoAbort) {
9609 if((method == GET || o.xmlData || o.jsonData) && p){
9610 url = Ext.urlAppend(url, p);
9613 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));
9615 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;
9620 * Determine whether this object has a request outstanding.
9621 * @param {Number} transactionId (Optional) defaults to the last transaction
9622 * @return {Boolean} True if there is an outstanding request.
9624 isLoading : function(transId){
9625 return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;
9629 * Aborts any outstanding request.
9630 * @param {Number} transactionId (Optional) defaults to the last transaction
9632 abort : function(transId){
9633 if(transId || this.isLoading()){
9634 Ext.lib.Ajax.abort(transId || this.transId);
9639 handleResponse : function(response){
9640 this.transId = false;
9641 var options = response.argument.options;
9642 response.argument = options ? options.argument : null;
9643 this.fireEvent(REQUESTCOMPLETE, this, response, options);
9644 if(options.success){
9645 options.success.call(options.scope, response, options);
9647 if(options.callback){
9648 options.callback.call(options.scope, options, true, response);
9653 handleFailure : function(response, e){
9654 this.transId = false;
9655 var options = response.argument.options;
9656 response.argument = options ? options.argument : null;
9657 this.fireEvent(REQUESTEXCEPTION, this, response, options, e);
9658 if(options.failure){
9659 options.failure.call(options.scope, response, options);
9661 if(options.callback){
9662 options.callback.call(options.scope, options, false, response);
9667 doFormUpload : function(o, ps, url){
9670 frame = doc.createElement('iframe'),
9671 form = Ext.getDom(o.form),
9674 encoding = 'multipart/form-data',
9676 target: form.target,
9677 method: form.method,
9678 encoding: form.encoding,
9679 enctype: form.enctype,
9683 Ext.fly(frame).set({
9690 doc.body.appendChild(frame);
9692 //Reset the Frame to neutral domain
9693 Ext.fly(frame).set({
9694 src : Ext.SSL_SECURE_URL
9697 // This is required so that IE doesn't pop the response up in a new window.
9699 document.frames[id].name = id;
9708 action: url || buf.action
9711 // add dynamic params
9712 Ext.iterate(Ext.urlDecode(ps, false), function(k, v){
9713 hd = doc.createElement('input');
9719 form.appendChild(hd);
9725 // bogus response object
9726 r = {responseText : '',
9728 argument : o.argument},
9733 doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
9736 if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea
9737 r.responseText = firstChild.value;
9739 r.responseText = doc.body.innerHTML;
9742 //in IE the document may still have a body even if returns XML.
9743 r.responseXML = doc.XMLDocument || doc;
9748 Ext.EventManager.removeListener(frame, LOAD, cb, me);
9750 me.fireEvent(REQUESTCOMPLETE, me, r, o);
9752 function runCallback(fn, scope, args){
9753 if(Ext.isFunction(fn)){
9754 fn.apply(scope, args);
9758 runCallback(o.success, o.scope, [r, o]);
9759 runCallback(o.callback, o.scope, [o, true, r]);
9761 if(!me.debugUploads){
9762 setTimeout(function(){Ext.removeNode(frame);}, 100);
9766 Ext.EventManager.on(frame, LOAD, cb, this);
9769 Ext.fly(form).set(buf);
9770 Ext.each(hiddens, function(h) {
9779 * @extends Ext.data.Connection
9780 * <p>The global Ajax request class that provides a simple way to make Ajax requests
9781 * with maximum flexibility.</p>
9782 * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once
9783 * and override them at the request function level only if necessary.</p>
9784 * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>
9785 * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>
9786 * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>
9787 * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>
9790 // Default headers to pass in every request
9791 Ext.Ajax.defaultHeaders = {
9796 * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>
9797 * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>
9798 * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>
9799 * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>
9802 // Example: show a spinner during all Ajax requests
9803 Ext.Ajax.on('beforerequest', this.showSpinner, this);
9804 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
9805 Ext.Ajax.on('requestexception', this.hideSpinner, this);
9808 * <p>An example request:</p>
9811 Ext.Ajax.{@link Ext.data.Connection#request request}({
9818 params: { foo: 'bar' }
9821 // Simple ajax form submission
9822 Ext.Ajax.{@link Ext.data.Connection#request request}({
9830 Ext.Ajax = new Ext.data.Connection({
9832 * @cfg {String} url @hide
9835 * @cfg {Object} extraParams @hide
9838 * @cfg {Object} defaultHeaders @hide
9841 * @cfg {String} method (Optional) @hide
9844 * @cfg {Number} timeout (Optional) @hide
9847 * @cfg {Boolean} autoAbort (Optional) @hide
9851 * @cfg {Boolean} disableCaching (Optional) @hide
9855 * @property disableCaching
9856 * True to add a unique cache-buster param to GET requests. (defaults to true)
9861 * The default URL to be used for requests to the server. (defaults to undefined)
9862 * If the server receives all requests through one URL, setting this once is easier than
9863 * entering it on every request.
9867 * @property extraParams
9868 * An object containing properties which are used as extra parameters to each request made
9869 * by this object (defaults to undefined). Session information and other data that you need
9870 * to pass with each request are commonly put here.
9874 * @property defaultHeaders
9875 * An object containing request headers which are added to each request made by this object
9876 * (defaults to undefined).
9881 * The default HTTP method to be used for requests. Note that this is case-sensitive and
9882 * should be all caps (defaults to undefined; if not set but params are present will use
9883 * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
9888 * The timeout in milliseconds to be used for requests. (defaults to 30000)
9893 * @property autoAbort
9894 * Whether a new request should abort any pending requests. (defaults to false)
9900 * Serialize the passed form into a url encoded string
9901 * @param {String/HTMLElement} form
9904 serializeForm : function(form){
9905 return Ext.lib.Ajax.serializeForm(form);
9909 * @class Ext.Updater
9910 * @extends Ext.util.Observable
9911 * Provides AJAX-style update capabilities for Element objects. Updater can be used to {@link #update}
9912 * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9913 * {@link Ext.Element Element} on a specific interval.<br><br>
9916 * var el = Ext.get("foo"); // Get Ext.Element object
9917 * var mgr = el.getUpdater();
9919 url: "http://myserver.com/index.php",
9926 * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
9928 * // or directly (returns the same Updater instance)
9929 * var mgr = new Ext.Updater("myElementId");
9930 * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
9931 * mgr.on("update", myFcnNeedsToKnow);
9933 * // short handed call directly from the element object
9934 * Ext.get("foo").load({
9937 params: "param1=foo&param2=bar",
9938 text: "Loading Foo..."
9942 * Create new Updater directly.
9943 * @param {Mixed} el The element to update
9944 * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
9945 * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
9947 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
9949 var BEFOREUPDATE = "beforeupdate",
9951 FAILURE = "failure";
9954 function processSuccess(response){
9956 me.transaction = null;
9957 if (response.argument.form && response.argument.reset) {
9958 try { // put in try/catch since some older FF releases had problems with this
9959 response.argument.form.reset();
9962 if (me.loadScripts) {
9963 me.renderer.render(me.el, response, me,
9964 updateComplete.createDelegate(me, [response]));
9966 me.renderer.render(me.el, response, me);
9967 updateComplete.call(me, response);
9972 function updateComplete(response, type, success){
9973 this.fireEvent(type || UPDATE, this.el, response);
9974 if(Ext.isFunction(response.argument.callback)){
9975 response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
9980 function processFailure(response){
9981 updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
9985 constructor: function(el, forceNew){
9988 if(!forceNew && el.updateManager){
9989 return el.updateManager;
9992 * The Element object
9997 * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
10000 me.defaultUrl = null;
10004 * @event beforeupdate
10005 * Fired before an update is made, return false from your handler and the update is cancelled.
10006 * @param {Ext.Element} el
10007 * @param {String/Object/Function} url
10008 * @param {String/Object} params
10013 * Fired after successful update is made.
10014 * @param {Ext.Element} el
10015 * @param {Object} oResponseObject The response Object
10020 * Fired on update failure.
10021 * @param {Ext.Element} el
10022 * @param {Object} oResponseObject The response Object
10027 Ext.apply(me, Ext.Updater.defaults);
10029 * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
10030 * @property sslBlankUrl
10034 * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
10035 * @property disableCaching
10039 * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
10040 * @property indicatorText
10044 * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
10045 * @property showLoadIndicator
10049 * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
10050 * @property timeout
10054 * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
10055 * @property loadScripts
10060 * Transaction object of the current executing transaction, or null if there is no active transaction.
10062 me.transaction = null;
10064 * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
10067 me.refreshDelegate = me.refresh.createDelegate(me);
10069 * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
10072 me.updateDelegate = me.update.createDelegate(me);
10074 * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
10077 me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
10080 * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
10082 me.renderer = me.renderer || me.getDefaultRenderer();
10084 Ext.Updater.superclass.constructor.call(me);
10088 * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10089 * @param {Object} renderer The object implementing the render() method
10091 setRenderer : function(renderer){
10092 this.renderer = renderer;
10096 * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
10099 getRenderer : function(){
10100 return this.renderer;
10104 * This is an overrideable method which returns a reference to a default
10105 * renderer class if none is specified when creating the Ext.Updater.
10106 * Defaults to {@link Ext.Updater.BasicRenderer}
10108 getDefaultRenderer: function() {
10109 return new Ext.Updater.BasicRenderer();
10113 * Sets the default URL used for updates.
10114 * @param {String/Function} defaultUrl The url or a function to call to get the url
10116 setDefaultUrl : function(defaultUrl){
10117 this.defaultUrl = defaultUrl;
10121 * Get the Element this Updater is bound to
10122 * @return {Ext.Element} The element
10124 getEl : function(){
10129 * Performs an <b>asynchronous</b> request, updating this element with the response.
10130 * If params are specified it uses POST, otherwise it uses GET.<br><br>
10131 * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
10132 * will not have been fully updated when the function returns. To post-process the returned
10133 * data, use the callback option, or an <b><code>update</code></b> event handler.
10134 * @param {Object} options A config object containing any of the following options:<ul>
10135 * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
10136 * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
10137 * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
10138 * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
10139 * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
10140 * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
10141 * string, or as an object containing properties which represent parameters,
10142 * or as a function, which returns such an object.</p></li>
10143 * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
10144 * any <script> tags embedded in the response text will be extracted
10145 * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
10146 * the callback will be called <i>after</i> the execution of the scripts.</p></li>
10147 * <li>callback : <b>Function</b><p class="sub-desc">A function to
10148 * be called when the response from the server arrives. The following
10149 * parameters are passed:<ul>
10150 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10151 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10152 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
10153 * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
10155 * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
10156 * to execute the callback (The callback's <code>this</code> reference.) If the
10157 * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
10158 * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
10159 * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
10160 * calls. To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
10161 * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
10162 * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
10163 * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
10164 * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...'). To replace the entire div, not
10165 * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
10166 * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
10167 * requests, this option causes an extra, auto-generated parameter to be appended to the request
10168 * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
10173 url: "your-url.php",
10174 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
10175 callback: yourFunction,
10176 scope: yourObject, //(optional scope)
10179 text: "Loading...",
10181 scripts: false // Save time by avoiding RegExp execution.
10185 update : function(url, params, callback, discardUrl){
10190 if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
10191 if(Ext.isObject(url)){ // must be config object
10194 params = params || cfg.params;
10195 callback = callback || cfg.callback;
10196 discardUrl = discardUrl || cfg.discardUrl;
10197 callerScope = cfg.scope;
10198 if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
10199 if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
10200 if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
10201 if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
10206 me.defaultUrl = url;
10208 if(Ext.isFunction(url)){
10209 url = url.call(me);
10212 var o = Ext.apply({}, {
10214 params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
10215 success: processSuccess,
10216 failure: processFailure,
10218 callback: undefined,
10219 timeout: (me.timeout*1000),
10220 disableCaching: me.disableCaching,
10225 "callback": callback,
10226 "scope": callerScope || window,
10231 me.transaction = Ext.Ajax.request(o);
10236 * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
10237 * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
10238 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
10239 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
10240 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
10241 * DOM <code><form></code> element temporarily modified to have its
10242 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
10243 * to a dynamically generated, hidden <code><iframe></code> which is inserted into the document
10244 * but removed after the return data has been gathered.</p>
10245 * <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>
10246 * and some server technologies (notably JEE) may require some custom processing in order to
10247 * retrieve parameter names and parameter values from the packet content.</p>
10248 * @param {String/HTMLElement} form The form Id or form element
10249 * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
10250 * @param {Boolean} reset (optional) Whether to try to reset the form after the update
10251 * @param {Function} callback (optional) Callback when transaction is complete. The following
10252 * parameters are passed:<ul>
10253 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10254 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10255 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
10257 formUpdate : function(form, url, reset, callback){
10259 if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
10260 if(Ext.isFunction(url)){
10261 url = url.call(me);
10263 form = Ext.getDom(form);
10264 me.transaction = Ext.Ajax.request({
10267 success: processSuccess,
10268 failure: processFailure,
10270 timeout: (me.timeout*1000),
10274 "callback": callback,
10278 me.showLoading.defer(1, me);
10283 * Set this element to auto refresh. Can be canceled by calling {@link #stopAutoRefresh}.
10284 * @param {Number} interval How often to update (in seconds).
10285 * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
10286 * supported by {@link #load}, or a function to call to get the url (defaults to the last used url). Note that while
10287 * the url used in a load call can be reused by this method, other load config options will not be reused and must be
10288 * sepcified as part of a config object passed as this paramter if needed.
10289 * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
10290 * "¶m1=1¶m2=2" or as an object {param1: 1, param2: 2}
10291 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10292 * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
10294 startAutoRefresh : function(interval, url, params, callback, refreshNow){
10297 me.update(url || me.defaultUrl, params, callback, true);
10299 if(me.autoRefreshProcId){
10300 clearInterval(me.autoRefreshProcId);
10302 me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
10306 * Stop auto refresh on this element.
10308 stopAutoRefresh : function(){
10309 if(this.autoRefreshProcId){
10310 clearInterval(this.autoRefreshProcId);
10311 delete this.autoRefreshProcId;
10316 * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
10318 isAutoRefreshing : function(){
10319 return !!this.autoRefreshProcId;
10323 * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
10324 * method may be overridden to perform a custom action while this Updater is actively updating its contents.
10326 showLoading : function(){
10327 if(this.showLoadIndicator){
10328 this.el.dom.innerHTML = this.indicatorText;
10333 * Aborts the currently executing transaction, if any.
10335 abort : function(){
10336 if(this.transaction){
10337 Ext.Ajax.abort(this.transaction);
10342 * Returns true if an update is in progress, otherwise false.
10343 * @return {Boolean}
10345 isUpdating : function(){
10346 return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
10350 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
10351 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10353 refresh : function(callback){
10354 if(this.defaultUrl){
10355 this.update(this.defaultUrl, null, callback, true);
10362 * @class Ext.Updater.defaults
10363 * The defaults collection enables customizing the default properties of Updater
10365 Ext.Updater.defaults = {
10367 * Timeout for requests or form posts in seconds (defaults to 30 seconds).
10372 * True to append a unique parameter to GET requests to disable caching (defaults to false).
10375 disableCaching : false,
10377 * Whether or not to show {@link #indicatorText} during loading (defaults to true).
10380 showLoadIndicator : true,
10382 * Text for loading indicator (defaults to '<div class="loading-indicator">Loading...</div>').
10385 indicatorText : '<div class="loading-indicator">Loading...</div>',
10387 * True to process scripts by default (defaults to false).
10390 loadScripts : false,
10392 * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
10395 sslBlankUrl : Ext.SSL_SECURE_URL
10400 * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
10402 * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
10403 * @param {Mixed} el The element to update
10404 * @param {String} url The url
10405 * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
10406 * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
10407 * example: {disableCaching:true, indicatorText: "Loading data..."}
10410 * @member Ext.Updater
10412 Ext.Updater.updateElement = function(el, url, params, options){
10413 var um = Ext.get(el).getUpdater();
10414 Ext.apply(um, options);
10415 um.update(url, params, options ? options.callback : null);
10419 * @class Ext.Updater.BasicRenderer
10420 * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
10421 * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
10422 * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
10424 Ext.Updater.BasicRenderer = function(){};
10426 Ext.Updater.BasicRenderer.prototype = {
10428 * This method is called when an Ajax response is received, and an Element needs updating.
10429 * @param {Ext.Element} el The element being rendered
10430 * @param {Object} xhr The XMLHttpRequest object
10431 * @param {Updater} updateManager The calling update manager
10432 * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
10434 render : function(el, response, updateManager, callback){
10435 el.update(response.responseText, updateManager.loadScripts, callback);
10440 * The date parsing and formatting syntax contains a subset of
10441 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
10442 * supported will provide results equivalent to their PHP versions.
10444 * The following is a list of all currently supported formats:
10446 Format Description Example returned values
10447 ------ ----------------------------------------------------------------------- -----------------------
10448 d Day of the month, 2 digits with leading zeros 01 to 31
10449 D A short textual representation of the day of the week Mon to Sun
10450 j Day of the month without leading zeros 1 to 31
10451 l A full textual representation of the day of the week Sunday to Saturday
10452 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
10453 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
10454 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
10455 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
10456 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
10457 F A full textual representation of a month, such as January or March January to December
10458 m Numeric representation of a month, with leading zeros 01 to 12
10459 M A short textual representation of a month Jan to Dec
10460 n Numeric representation of a month, without leading zeros 1 to 12
10461 t Number of days in the given month 28 to 31
10462 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
10463 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
10464 belongs to the previous or next year, that year is used instead)
10465 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
10466 y A two digit representation of a year Examples: 99 or 03
10467 a Lowercase Ante meridiem and Post meridiem am or pm
10468 A Uppercase Ante meridiem and Post meridiem AM or PM
10469 g 12-hour format of an hour without leading zeros 1 to 12
10470 G 24-hour format of an hour without leading zeros 0 to 23
10471 h 12-hour format of an hour with leading zeros 01 to 12
10472 H 24-hour format of an hour with leading zeros 00 to 23
10473 i Minutes, with leading zeros 00 to 59
10474 s Seconds, with leading zeros 00 to 59
10475 u Decimal fraction of a second Examples:
10476 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
10477 100 (i.e. 0.100s) or
10478 999 (i.e. 0.999s) or
10479 999876543210 (i.e. 0.999876543210s)
10480 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
10481 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
10482 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
10483 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
10486 1) If unspecified, the month / day defaults to the current month / day, 1991 or
10487 the time defaults to midnight, while the timezone defaults to the 1992-10 or
10488 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
10489 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
10490 are optional. 1995-07-18T17:21:28-02:00 or
10491 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
10492 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
10493 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
10494 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
10495 date-time granularity which are supported, or see 2000-02-13T21:25:33
10496 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
10497 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
10498 M$ Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
10499 \/Date(1238606590509+0800)\/
10502 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
10505 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
10507 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
10508 document.write(dt.format('Y-m-d')); // 2007-01-10
10509 document.write(dt.format('F j, Y, g:i a')); // January 10, 2007, 3:05 pm
10510 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
10513 * Here are some standard date/time patterns that you might find helpful. They
10514 * are not part of the source of Date.js, but to use them you can simply copy this
10515 * block of code into any script that is included after Date.js and they will also become
10516 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
10519 ISO8601Long:"Y-m-d H:i:s",
10520 ISO8601Short:"Y-m-d",
10521 ShortDate: "n/j/Y",
10522 LongDate: "l, F d, Y",
10523 FullDateTime: "l, F d, Y g:i:s A",
10525 ShortTime: "g:i A",
10526 LongTime: "g:i:s A",
10527 SortableDateTime: "Y-m-d\\TH:i:s",
10528 UniversalSortableDateTime: "Y-m-d H:i:sO",
10535 var dt = new Date();
10536 document.write(dt.format(Date.patterns.ShortDate));
10538 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
10539 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
10543 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
10544 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
10545 * They generate precompiled functions from format patterns instead of parsing and
10546 * processing each pattern every time a date is formatted. These functions are available
10547 * on every Date object.
10553 * Global flag which determines if strict date parsing should be used.
10554 * Strict date parsing will not roll-over invalid dates, which is the
10555 * default behaviour of javascript Date objects.
10556 * (see {@link #parseDate} for more information)
10557 * Defaults to <tt>false</tt>.
10561 Date.useStrict = false;
10564 // create private copy of Ext's String.format() method
10565 // - to remove unnecessary dependency
10566 // - to resolve namespace conflict with M$-Ajax's implementation
10567 function xf(format) {
10568 var args = Array.prototype.slice.call(arguments, 1);
10569 return format.replace(/\{(\d+)\}/g, function(m, i) {
10576 Date.formatCodeToRegex = function(character, currentGroup) {
10577 // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
10578 var p = Date.parseCodes[character];
10581 p = typeof p == 'function'? p() : p;
10582 Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
10585 return p ? Ext.applyIf({
10586 c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
10590 s:Ext.escapeRe(character) // treat unrecognised characters as literals
10594 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
10595 var $f = Date.formatCodeToRegex;
10599 * <p>An object hash in which each property is a date parsing function. The property name is the
10600 * format string which that function parses.</p>
10601 * <p>This object is automatically populated with date parsing functions as
10602 * date formats are requested for Ext standard formatting strings.</p>
10603 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
10604 * may be used as a format string to {@link #parseDate}.<p>
10605 * <p>Example:</p><pre><code>
10606 Date.parseFunctions['x-date-format'] = myDateParser;
10608 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10609 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
10610 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
10611 * (i.e. prevent javascript Date "rollover") (The default must be false).
10612 * Invalid date strings should return null when parsed.</div></li>
10614 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
10615 * formatting function must be placed into the {@link #formatFunctions} property.
10616 * @property parseFunctions
10621 "M$": function(input, strict) {
10622 // note: the timezone offset is ignored since the M$ Ajax server sends
10623 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
10624 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
10625 var r = (input || '').match(re);
10626 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
10632 * <p>An object hash in which each property is a date formatting function. The property name is the
10633 * format string which corresponds to the produced formatted date string.</p>
10634 * <p>This object is automatically populated with date formatting functions as
10635 * date formats are requested for Ext standard formatting strings.</p>
10636 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
10637 * may be used as a format string to {@link #format}. Example:</p><pre><code>
10638 Date.formatFunctions['x-date-format'] = myDateFormatter;
10640 * <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>
10641 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
10643 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
10644 * parsing function must be placed into the {@link #parseFunctions} property.
10645 * @property formatFunctions
10651 // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
10652 return '\\/Date(' + this.getTime() + ')\\/';
10659 * Date interval constant
10666 * Date interval constant
10673 * Date interval constant
10679 /** Date interval constant
10686 * Date interval constant
10693 * Date interval constant
10700 * Date interval constant
10707 * <p>An object hash containing default date values used during date parsing.</p>
10708 * <p>The following properties are available:<div class="mdetail-params"><ul>
10709 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
10710 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
10711 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
10712 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
10713 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
10714 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
10715 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
10717 * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
10718 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
10719 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
10720 * It is the responsiblity of the developer to account for this.</b></p>
10723 // set default day value to the first day of the month
10724 Date.defaults.d = 1;
10726 // parse a February date string containing only year and month values.
10727 // setting the default day value to 1 prevents weird date rollover issues
10728 // when attempting to parse the following date string on, for example, March 31st 2009.
10729 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
10731 * @property defaults
10738 * An array of textual day names.
10739 * Override these values for international dates.
10743 'SundayInYourLang',
10744 'MondayInYourLang',
10762 * An array of textual month names.
10763 * Override these values for international dates.
10766 Date.monthNames = [
10791 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
10792 * Override these values for international dates.
10795 Date.monthNumbers = {
10796 'ShortJanNameInYourLang':0,
10797 'ShortFebNameInYourLang':1,
10820 * Get the short month name for the given month number.
10821 * Override this function for international dates.
10822 * @param {Number} month A zero-based javascript month number.
10823 * @return {String} The short month name.
10826 getShortMonthName : function(month) {
10827 return Date.monthNames[month].substring(0, 3);
10831 * Get the short day name for the given day number.
10832 * Override this function for international dates.
10833 * @param {Number} day A zero-based javascript day number.
10834 * @return {String} The short day name.
10837 getShortDayName : function(day) {
10838 return Date.dayNames[day].substring(0, 3);
10842 * Get the zero-based javascript month number for the given short/full month name.
10843 * Override this function for international dates.
10844 * @param {String} name The short/full month name.
10845 * @return {Number} The zero-based javascript month number.
10848 getMonthNumber : function(name) {
10849 // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
10850 return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
10854 * The base format-code to formatting-function hashmap used by the {@link #format} method.
10855 * Formatting functions are strings (or functions which return strings) which
10856 * will return the appropriate value when evaluated in the context of the Date object
10857 * from which the {@link #format} method is called.
10858 * Add to / override these mappings for custom date formatting.
10859 * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
10862 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
10863 (new Date()).format("X"); // returns the current day of the month
10869 d: "String.leftPad(this.getDate(), 2, '0')",
10870 D: "Date.getShortDayName(this.getDay())", // get localised short day name
10871 j: "this.getDate()",
10872 l: "Date.dayNames[this.getDay()]",
10873 N: "(this.getDay() ? this.getDay() : 7)",
10874 S: "this.getSuffix()",
10875 w: "this.getDay()",
10876 z: "this.getDayOfYear()",
10877 W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10878 F: "Date.monthNames[this.getMonth()]",
10879 m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10880 M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10881 n: "(this.getMonth() + 1)",
10882 t: "this.getDaysInMonth()",
10883 L: "(this.isLeapYear() ? 1 : 0)",
10884 o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10885 Y: "this.getFullYear()",
10886 y: "('' + this.getFullYear()).substring(2, 4)",
10887 a: "(this.getHours() < 12 ? 'am' : 'pm')",
10888 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10889 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10890 G: "this.getHours()",
10891 h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10892 H: "String.leftPad(this.getHours(), 2, '0')",
10893 i: "String.leftPad(this.getMinutes(), 2, '0')",
10894 s: "String.leftPad(this.getSeconds(), 2, '0')",
10895 u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10896 O: "this.getGMTOffset()",
10897 P: "this.getGMTOffset(true)",
10898 T: "this.getTimezone()",
10899 Z: "(this.getTimezoneOffset() * -60)",
10901 c: function() { // ISO-8601 -- GMT format
10902 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10903 var e = c.charAt(i);
10904 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10906 return code.join(" + ");
10909 c: function() { // ISO-8601 -- UTC format
10911 "this.getUTCFullYear()", "'-'",
10912 "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10913 "String.leftPad(this.getUTCDate(), 2, '0')",
10915 "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10916 "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10917 "String.leftPad(this.getUTCSeconds(), 2, '0')",
10923 U: "Math.round(this.getTime() / 1000)"
10927 * Checks if the passed Date parameters will cause a javascript Date "rollover".
10928 * @param {Number} year 4-digit year
10929 * @param {Number} month 1-based month-of-year
10930 * @param {Number} day Day of month
10931 * @param {Number} hour (optional) Hour
10932 * @param {Number} minute (optional) Minute
10933 * @param {Number} second (optional) Second
10934 * @param {Number} millisecond (optional) Millisecond
10935 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
10938 isValid : function(y, m, d, h, i, s, ms) {
10945 var dt = new Date(y, m - 1, d, h, i, s, ms);
10947 return y == dt.getFullYear() &&
10948 m == dt.getMonth() + 1 &&
10949 d == dt.getDate() &&
10950 h == dt.getHours() &&
10951 i == dt.getMinutes() &&
10952 s == dt.getSeconds() &&
10953 ms == dt.getMilliseconds();
10957 * Parses the passed string using the specified date format.
10958 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
10959 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
10960 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
10961 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
10962 * Keep in mind that the input date string must precisely match the specified format string
10963 * in order for the parse operation to be successful (failed parse operations return a null value).
10964 * <p>Example:</p><pre><code>
10965 //dt = Fri May 25 2007 (current date)
10966 var dt = new Date();
10968 //dt = Thu May 25 2006 (today's month/day in 2006)
10969 dt = Date.parseDate("2006", "Y");
10971 //dt = Sun Jan 15 2006 (all date parts specified)
10972 dt = Date.parseDate("2006-01-15", "Y-m-d");
10974 //dt = Sun Jan 15 2006 15:20:01
10975 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
10977 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
10978 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
10980 * @param {String} input The raw date string.
10981 * @param {String} format The expected date string format.
10982 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
10983 (defaults to false). Invalid date strings will return null when parsed.
10984 * @return {Date} The parsed Date.
10987 parseDate : function(input, format, strict) {
10988 var p = Date.parseFunctions;
10989 if (p[format] == null) {
10990 Date.createParser(format);
10992 return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
10996 getFormatCode : function(character) {
10997 var f = Date.formatCodes[character];
11000 f = typeof f == 'function'? f() : f;
11001 Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
11004 // note: unknown characters are treated as literals
11005 return f || ("'" + String.escape(character) + "'");
11009 createFormat : function(format) {
11014 for (var i = 0; i < format.length; ++i) {
11015 ch = format.charAt(i);
11016 if (!special && ch == "\\") {
11018 } else if (special) {
11020 code.push("'" + String.escape(ch) + "'");
11022 code.push(Date.getFormatCode(ch))
11025 Date.formatFunctions[format] = new Function("return " + code.join('+'));
11029 createParser : function() {
11031 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
11032 "def = Date.defaults,",
11033 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
11038 "if(u != null){", // i.e. unix time is defined
11039 "v = new Date(u * 1000);", // give top priority to UNIX time
11041 // create Date object representing midnight of the current day;
11042 // this will provide us with our date defaults
11043 // (note: clearTime() handles Daylight Saving Time automatically)
11044 "dt = (new Date()).clearTime();",
11046 // date calculations (note: these calculations create a dependency on Ext.num())
11047 "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
11048 "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
11049 "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
11051 // time calculations (note: these calculations create a dependency on Ext.num())
11052 "h = Ext.num(h, Ext.num(def.h, dt.getHours()));",
11053 "i = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
11054 "s = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
11055 "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
11057 "if(z >= 0 && y >= 0){",
11058 // both the year and zero-based day of year are defined and >= 0.
11059 // these 2 values alone provide sufficient info to create a full date object
11061 // create Date object representing January 1st for the given year
11062 "v = new Date(y, 0, 1, h, i, s, ms);",
11064 // then add day of year, checking for Date "rollover" if necessary
11065 "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
11066 "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
11067 "v = null;", // invalid date, so return null
11069 // plain old Date object
11070 "v = new Date(y, m, d, h, i, s, ms);",
11076 // favour UTC offset over GMT offset
11078 // reset to UTC, then add offset
11079 "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
11081 // reset to GMT, then add offset
11082 "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
11089 return function(format) {
11090 var regexNum = Date.parseRegexes.length,
11097 for (var i = 0; i < format.length; ++i) {
11098 ch = format.charAt(i);
11099 if (!special && ch == "\\") {
11101 } else if (special) {
11103 regex.push(String.escape(ch));
11105 var obj = $f(ch, currentGroup);
11106 currentGroup += obj.g;
11108 if (obj.g && obj.c) {
11114 Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$");
11115 Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
11123 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
11124 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
11125 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
11129 c:"d = parseInt(results[{0}], 10);\n",
11130 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
11134 c:"d = parseInt(results[{0}], 10);\n",
11135 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
11138 for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
11142 s:"(?:" + a.join("|") +")"
11149 s:"(?:" + Date.dayNames.join("|") + ")"
11155 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
11160 s:"(?:st|nd|rd|th)"
11165 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
11169 c:"z = parseInt(results[{0}], 10);\n",
11170 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
11175 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
11180 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
11181 s:"(" + Date.monthNames.join("|") + ")"
11185 for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
11186 return Ext.applyIf({
11187 s:"(" + a.join("|") + ")"
11192 c:"m = parseInt(results[{0}], 10) - 1;\n",
11193 s:"(\\d{2})" // month number with leading zeros (01 - 12)
11197 c:"m = parseInt(results[{0}], 10) - 1;\n",
11198 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
11203 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
11215 c:"y = parseInt(results[{0}], 10);\n",
11216 s:"(\\d{4})" // 4-digit year
11220 c:"var ty = parseInt(results[{0}], 10);\n"
11221 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
11226 c:"if (results[{0}] == 'am') {\n"
11227 + "if (!h || h == 12) { h = 0; }\n"
11228 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11233 c:"if (results[{0}] == 'AM') {\n"
11234 + "if (!h || h == 12) { h = 0; }\n"
11235 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11243 c:"h = parseInt(results[{0}], 10);\n",
11244 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
11251 c:"h = parseInt(results[{0}], 10);\n",
11252 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
11256 c:"i = parseInt(results[{0}], 10);\n",
11257 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
11261 c:"s = parseInt(results[{0}], 10);\n",
11262 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
11266 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
11267 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11272 "o = results[{0}];",
11273 "var sn = o.substring(0,1),", // get + / - sign
11274 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11275 "mn = o.substring(3,5) % 60;", // get minutes
11276 "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
11278 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
11283 "o = results[{0}];",
11284 "var sn = o.substring(0,1),", // get + / - sign
11285 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11286 "mn = o.substring(4,6) % 60;", // get minutes
11287 "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
11289 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
11294 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
11298 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
11299 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
11300 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
11305 $f("Y", 1), // year
11306 $f("m", 2), // month
11308 $f("h", 4), // hour
11309 $f("i", 5), // minute
11310 $f("s", 6), // second
11311 {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)
11312 {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
11313 "if(results[8]) {", // timezone specified
11314 "if(results[8] == 'Z'){",
11316 "}else if (results[8].indexOf(':') > -1){",
11317 $f("P", 8).c, // timezone offset with colon separator
11319 $f("O", 8).c, // timezone offset without colon separator
11325 for (var i = 0, l = arr.length; i < l; ++i) {
11326 calc.push(arr[i].c);
11333 arr[0].s, // year (required)
11334 "(?:", "-", arr[1].s, // month (optional)
11335 "(?:", "-", arr[2].s, // day (optional)
11337 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
11338 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
11339 "(?::", arr[5].s, ")?", // seconds (optional)
11340 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
11341 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
11350 c:"u = parseInt(results[{0}], 10);\n",
11351 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
11358 Ext.apply(Date.prototype, {
11360 dateFormat : function(format) {
11361 if (Date.formatFunctions[format] == null) {
11362 Date.createFormat(format);
11364 return Date.formatFunctions[format].call(this);
11368 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
11370 * Note: The date string returned by the javascript Date object's toString() method varies
11371 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
11372 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
11373 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
11374 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
11375 * from the GMT offset portion of the date string.
11376 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
11378 getTimezone : function() {
11379 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
11381 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
11382 // 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)
11383 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
11384 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
11385 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
11387 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
11388 // step 1: (?:\((.*)\) -- find timezone in parentheses
11389 // 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
11390 // step 3: remove all non uppercase characters found in step 1 and 2
11391 return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
11395 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
11396 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
11397 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
11399 getGMTOffset : function(colon) {
11400 return (this.getTimezoneOffset() > 0 ? "-" : "+")
11401 + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
11402 + (colon ? ":" : "")
11403 + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
11407 * Get the numeric day number of the year, adjusted for leap year.
11408 * @return {Number} 0 to 364 (365 in leap years).
11410 getDayOfYear: function() {
11413 m = this.getMonth(),
11416 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
11417 num += d.getDaysInMonth();
11419 return num + this.getDate() - 1;
11423 * Get the numeric ISO-8601 week number of the year.
11424 * (equivalent to the format specifier 'W', but without a leading zero).
11425 * @return {Number} 1 to 53
11427 getWeekOfYear : function() {
11428 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
11429 var ms1d = 864e5, // milliseconds in a day
11430 ms7d = 7 * ms1d; // milliseconds in a week
11432 return function() { // return a closure so constants get calculated only once
11433 var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
11434 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
11435 Wyr = new Date(AWN * ms7d).getUTCFullYear();
11437 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
11442 * Checks if the current date falls within a leap year.
11443 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
11445 isLeapYear : function() {
11446 var year = this.getFullYear();
11447 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
11451 * Get the first day of the current month, adjusted for leap year. The returned value
11452 * is the numeric day index within the week (0-6) which can be used in conjunction with
11453 * the {@link #monthNames} array to retrieve the textual day name.
11456 var dt = new Date('1/10/2007');
11457 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
11459 * @return {Number} The day number (0-6).
11461 getFirstDayOfMonth : function() {
11462 var day = (this.getDay() - (this.getDate() - 1)) % 7;
11463 return (day < 0) ? (day + 7) : day;
11467 * Get the last day of the current month, adjusted for leap year. The returned value
11468 * is the numeric day index within the week (0-6) which can be used in conjunction with
11469 * the {@link #monthNames} array to retrieve the textual day name.
11472 var dt = new Date('1/10/2007');
11473 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
11475 * @return {Number} The day number (0-6).
11477 getLastDayOfMonth : function() {
11478 return this.getLastDateOfMonth().getDay();
11483 * Get the date of the first day of the month in which this date resides.
11486 getFirstDateOfMonth : function() {
11487 return new Date(this.getFullYear(), this.getMonth(), 1);
11491 * Get the date of the last day of the month in which this date resides.
11494 getLastDateOfMonth : function() {
11495 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
11499 * Get the number of days in the current month, adjusted for leap year.
11500 * @return {Number} The number of days in the month.
11502 getDaysInMonth: function() {
11503 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
11505 return function() { // return a closure for efficiency
11506 var m = this.getMonth();
11508 return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
11513 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
11514 * @return {String} 'st, 'nd', 'rd' or 'th'.
11516 getSuffix : function() {
11517 switch (this.getDate()) {
11534 * Creates and returns a new Date instance with the exact same date value as the called instance.
11535 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
11536 * variable will also be changed. When the intention is to create a new variable that will not
11537 * modify the original instance, you should create a clone.
11539 * Example of correctly cloning a date:
11542 var orig = new Date('10/1/2006');
11545 document.write(orig); //returns 'Thu Oct 05 2006'!
11548 var orig = new Date('10/1/2006');
11549 var copy = orig.clone();
11551 document.write(orig); //returns 'Thu Oct 01 2006'
11553 * @return {Date} The new Date instance.
11555 clone : function() {
11556 return new Date(this.getTime());
11560 * Checks if the current date is affected by Daylight Saving Time (DST).
11561 * @return {Boolean} True if the current date is affected by DST.
11563 isDST : function() {
11564 // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
11565 // courtesy of @geoffrey.mcgill
11566 return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
11570 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
11571 * automatically adjusting for Daylight Saving Time (DST) where applicable.
11572 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
11573 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
11574 * @return {Date} this or the clone.
11576 clearTime : function(clone) {
11578 return this.clone().clearTime();
11581 // get current date before clearing time
11582 var d = this.getDate();
11586 this.setMinutes(0);
11587 this.setSeconds(0);
11588 this.setMilliseconds(0);
11590 if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
11591 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
11592 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
11594 // increment hour until cloned date == current date
11595 for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
11598 this.setHours(c.getHours());
11605 * Provides a convenient method for performing basic date arithmetic. This method
11606 * does not modify the Date instance being called - it creates and returns
11607 * a new Date instance containing the resulting date value.
11612 var dt = new Date('10/29/2006').add(Date.DAY, 5);
11613 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
11615 // Negative values will be subtracted:
11616 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
11617 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
11619 // You can even chain several calls together in one line:
11620 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
11621 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
11624 * @param {String} interval A valid date interval enum value.
11625 * @param {Number} value The amount to add to the current date.
11626 * @return {Date} The new Date instance.
11628 add : function(interval, value) {
11629 var d = this.clone();
11630 if (!interval || value === 0) return d;
11632 switch(interval.toLowerCase()) {
11634 d.setMilliseconds(this.getMilliseconds() + value);
11637 d.setSeconds(this.getSeconds() + value);
11640 d.setMinutes(this.getMinutes() + value);
11643 d.setHours(this.getHours() + value);
11646 d.setDate(this.getDate() + value);
11649 var day = this.getDate();
11651 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
11654 d.setMonth(this.getMonth() + value);
11657 d.setFullYear(this.getFullYear() + value);
11664 * Checks if this date falls on or between the given start and end dates.
11665 * @param {Date} start Start date
11666 * @param {Date} end End date
11667 * @return {Boolean} true if this date falls on or between the given start and end dates.
11669 between : function(start, end) {
11670 var t = this.getTime();
11671 return start.getTime() <= t && t <= end.getTime();
11677 * Formats a date given the supplied format string.
11678 * @param {String} format The format string.
11679 * @return {String} The formatted date.
11682 Date.prototype.format = Date.prototype.dateFormat;
11686 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
11687 Ext.apply(Date.prototype, {
11688 _xMonth : Date.prototype.setMonth,
11689 _xDate : Date.prototype.setDate,
11691 // Bug in Safari 1.3, 2.0 (WebKit build < 420)
11692 // Date.setMonth does not work consistently if iMonth is not 0-11
11693 setMonth : function(num) {
11695 var n = Math.ceil(-num),
11696 back_year = Math.ceil(n / 12),
11697 month = (n % 12) ? 12 - n % 12 : 0;
11699 this.setFullYear(this.getFullYear() - back_year);
11701 return this._xMonth(month);
11703 return this._xMonth(num);
11707 // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
11708 // The parameter for Date.setDate() is converted to a signed byte integer in Safari
11709 // http://brianary.blogspot.com/2006/03/safari-date-bug.html
11710 setDate : function(d) {
11711 // use setTime() to workaround setDate() bug
11712 // subtract current day of month in milliseconds, then add desired day of month in milliseconds
11713 return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
11720 /* Some basic Date tests... (requires Firebug)
11722 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
11723 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
11726 console.group('Standard Date.parseDate() Tests');
11727 console.log('Date.parseDate("2009-01-05T11:38:56", "c") = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
11728 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
11729 console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c") = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
11730 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
11731 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
11732 console.groupEnd();
11734 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
11735 // -- accepts ALL 6 levels of date-time granularity
11736 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
11737 console.log('Date.parseDate("1997", "c") = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
11738 console.log('Date.parseDate("1997-07", "c") = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
11739 console.log('Date.parseDate("1997-07-16", "c") = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
11740 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)
11741 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)
11742 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)
11743 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)
11744 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
11745 console.groupEnd();
11749 * @class Ext.util.MixedCollection
11750 * @extends Ext.util.Observable
11751 * A Collection class that maintains both numeric indexes and keys and exposes events.
11753 * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11754 * function should add function references to the collection. Defaults to
11756 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
11757 * and return the key value for that item. This is used when available to look up the key on items that
11758 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
11759 * equivalent to providing an implementation for the {@link #getKey} method.
11761 Ext.util.MixedCollection = function(allowFunctions, keyFn){
11769 * Fires when the collection is cleared.
11774 * Fires when an item is added to the collection.
11775 * @param {Number} index The index at which the item was added.
11776 * @param {Object} o The item added.
11777 * @param {String} key The key associated with the added item.
11782 * Fires when an item is replaced in the collection.
11783 * @param {String} key he key associated with the new added.
11784 * @param {Object} old The item being replaced.
11785 * @param {Object} new The new item.
11790 * Fires when an item is removed from the collection.
11791 * @param {Object} o The item being removed.
11792 * @param {String} key (optional) The key associated with the removed item.
11797 this.allowFunctions = allowFunctions === true;
11799 this.getKey = keyFn;
11801 Ext.util.MixedCollection.superclass.constructor.call(this);
11804 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
11807 * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11808 * function should add function references to the collection. Defaults to
11811 allowFunctions : false,
11814 * Adds an item to the collection. Fires the {@link #add} event when complete.
11815 * @param {String} key <p>The key to associate with the item, or the new item.</p>
11816 * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
11817 * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
11818 * the MixedCollection will be able to <i>derive</i> the key for the new item.
11819 * In this case just pass the new item in this parameter.</p>
11820 * @param {Object} o The item to add.
11821 * @return {Object} The item added.
11823 add : function(key, o){
11824 if(arguments.length == 1){
11826 key = this.getKey(o);
11828 if(typeof key != 'undefined' && key !== null){
11829 var old = this.map[key];
11830 if(typeof old != 'undefined'){
11831 return this.replace(key, o);
11836 this.items.push(o);
11837 this.keys.push(key);
11838 this.fireEvent('add', this.length-1, o, key);
11843 * MixedCollection has a generic way to fetch keys if you implement getKey. The default implementation
11844 * simply returns <b><code>item.id</code></b> but you can provide your own implementation
11845 * to return a different value as in the following examples:<pre><code>
11847 var mc = new Ext.util.MixedCollection();
11848 mc.add(someEl.dom.id, someEl);
11849 mc.add(otherEl.dom.id, otherEl);
11853 var mc = new Ext.util.MixedCollection();
11854 mc.getKey = function(el){
11860 // or via the constructor
11861 var mc = new Ext.util.MixedCollection(false, function(el){
11867 * @param {Object} item The item for which to find the key.
11868 * @return {Object} The key for the passed item.
11870 getKey : function(o){
11875 * Replaces an item in the collection. Fires the {@link #replace} event when complete.
11876 * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
11877 * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
11878 * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
11879 * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
11880 * with one having the same key value, then just pass the replacement item in this parameter.</p>
11881 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
11883 * @return {Object} The new item.
11885 replace : function(key, o){
11886 if(arguments.length == 1){
11888 key = this.getKey(o);
11890 var old = this.map[key];
11891 if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
11892 return this.add(key, o);
11894 var index = this.indexOfKey(key);
11895 this.items[index] = o;
11897 this.fireEvent('replace', key, old, o);
11902 * Adds all elements of an Array or an Object to the collection.
11903 * @param {Object/Array} objs An Object containing properties which will be added
11904 * to the collection, or an Array of values, each of which are added to the collection.
11905 * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
11906 * has been set to <tt>true</tt>.
11908 addAll : function(objs){
11909 if(arguments.length > 1 || Ext.isArray(objs)){
11910 var args = arguments.length > 1 ? arguments : objs;
11911 for(var i = 0, len = args.length; i < len; i++){
11915 for(var key in objs){
11916 if(this.allowFunctions || typeof objs[key] != 'function'){
11917 this.add(key, objs[key]);
11924 * Executes the specified function once for every item in the collection, passing the following arguments:
11925 * <div class="mdetail-params"><ul>
11926 * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
11927 * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
11928 * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
11930 * The function should return a boolean value. Returning false from the function will stop the iteration.
11931 * @param {Function} fn The function to execute for each item.
11932 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
11934 each : function(fn, scope){
11935 var items = [].concat(this.items); // each safe for removal
11936 for(var i = 0, len = items.length; i < len; i++){
11937 if(fn.call(scope || items[i], items[i], i, len) === false){
11944 * Executes the specified function once for every key in the collection, passing each
11945 * key, and its associated item as the first two parameters.
11946 * @param {Function} fn The function to execute for each item.
11947 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11949 eachKey : function(fn, scope){
11950 for(var i = 0, len = this.keys.length; i < len; i++){
11951 fn.call(scope || window, this.keys[i], this.items[i], i, len);
11956 * Returns the first item in the collection which elicits a true return value from the
11957 * passed selection function.
11958 * @param {Function} fn The selection function to execute for each item.
11959 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11960 * @return {Object} The first item in the collection which returned true from the selection function.
11962 find : function(fn, scope){
11963 for(var i = 0, len = this.items.length; i < len; i++){
11964 if(fn.call(scope || window, this.items[i], this.keys[i])){
11965 return this.items[i];
11972 * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
11973 * @param {Number} index The index to insert the item at.
11974 * @param {String} key The key to associate with the new item, or the item itself.
11975 * @param {Object} o (optional) If the second parameter was a key, the new item.
11976 * @return {Object} The item inserted.
11978 insert : function(index, key, o){
11979 if(arguments.length == 2){
11981 key = this.getKey(o);
11983 if(this.containsKey(key)){
11984 this.suspendEvents();
11985 this.removeKey(key);
11986 this.resumeEvents();
11988 if(index >= this.length){
11989 return this.add(key, o);
11992 this.items.splice(index, 0, o);
11993 if(typeof key != 'undefined' && key !== null){
11996 this.keys.splice(index, 0, key);
11997 this.fireEvent('add', index, o, key);
12002 * Remove an item from the collection.
12003 * @param {Object} o The item to remove.
12004 * @return {Object} The item removed or false if no item was removed.
12006 remove : function(o){
12007 return this.removeAt(this.indexOf(o));
12011 * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
12012 * @param {Number} index The index within the collection of the item to remove.
12013 * @return {Object} The item removed or false if no item was removed.
12015 removeAt : function(index){
12016 if(index < this.length && index >= 0){
12018 var o = this.items[index];
12019 this.items.splice(index, 1);
12020 var key = this.keys[index];
12021 if(typeof key != 'undefined'){
12022 delete this.map[key];
12024 this.keys.splice(index, 1);
12025 this.fireEvent('remove', o, key);
12032 * Removed an item associated with the passed key fom the collection.
12033 * @param {String} key The key of the item to remove.
12034 * @return {Object} The item removed or false if no item was removed.
12036 removeKey : function(key){
12037 return this.removeAt(this.indexOfKey(key));
12041 * Returns the number of items in the collection.
12042 * @return {Number} the number of items in the collection.
12044 getCount : function(){
12045 return this.length;
12049 * Returns index within the collection of the passed Object.
12050 * @param {Object} o The item to find the index of.
12051 * @return {Number} index of the item. Returns -1 if not found.
12053 indexOf : function(o){
12054 return this.items.indexOf(o);
12058 * Returns index within the collection of the passed key.
12059 * @param {String} key The key to find the index of.
12060 * @return {Number} index of the key.
12062 indexOfKey : function(key){
12063 return this.keys.indexOf(key);
12067 * Returns the item associated with the passed key OR index.
12068 * Key has priority over index. This is the equivalent
12069 * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
12070 * @param {String/Number} key The key or index of the item.
12071 * @return {Object} If the item is found, returns the item. If the item was not found, returns <tt>undefined</tt>.
12072 * If an item was found, but is a Class, returns <tt>null</tt>.
12074 item : function(key){
12075 var mk = this.map[key],
12076 item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
12077 return !Ext.isFunction(item) || this.allowFunctions ? item : null; // for prototype!
12081 * Returns the item at the specified index.
12082 * @param {Number} index The index of the item.
12083 * @return {Object} The item at the specified index.
12085 itemAt : function(index){
12086 return this.items[index];
12090 * Returns the item associated with the passed key.
12091 * @param {String/Number} key The key of the item.
12092 * @return {Object} The item associated with the passed key.
12094 key : function(key){
12095 return this.map[key];
12099 * Returns true if the collection contains the passed Object as an item.
12100 * @param {Object} o The Object to look for in the collection.
12101 * @return {Boolean} True if the collection contains the Object as an item.
12103 contains : function(o){
12104 return this.indexOf(o) != -1;
12108 * Returns true if the collection contains the passed Object as a key.
12109 * @param {String} key The key to look for in the collection.
12110 * @return {Boolean} True if the collection contains the Object as a key.
12112 containsKey : function(key){
12113 return typeof this.map[key] != 'undefined';
12117 * Removes all items from the collection. Fires the {@link #clear} event when complete.
12119 clear : function(){
12124 this.fireEvent('clear');
12128 * Returns the first item in the collection.
12129 * @return {Object} the first item in the collection..
12131 first : function(){
12132 return this.items[0];
12136 * Returns the last item in the collection.
12137 * @return {Object} the last item in the collection..
12140 return this.items[this.length-1];
12145 * Performs the actual sorting based on a direction and a sorting function. Internally,
12146 * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
12147 * the sorted array data back into this.items and this.keys
12148 * @param {String} property Property to sort by ('key', 'value', or 'index')
12149 * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
12150 * @param {Function} fn (optional) Comparison function that defines the sort order.
12151 * Defaults to sorting by numeric value.
12153 _sort : function(property, dir, fn){
12155 dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
12157 //this is a temporary array used to apply the sorting function
12160 items = this.items;
12162 //default to a simple sorter function if one is not provided
12163 fn = fn || function(a, b) {
12167 //copy all the items into a temporary array, which we will sort
12168 for(i = 0, len = items.length; i < len; i++){
12176 //sort the temporary array
12177 c.sort(function(a, b){
12178 var v = fn(a[property], b[property]) * dsc;
12180 v = (a.index < b.index ? -1 : 1);
12185 //copy the temporary array back into the main this.items and this.keys objects
12186 for(i = 0, len = c.length; i < len; i++){
12187 items[i] = c[i].value;
12188 keys[i] = c[i].key;
12191 this.fireEvent('sort', this);
12195 * Sorts this collection by <b>item</b> value with the passed comparison function.
12196 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12197 * @param {Function} fn (optional) Comparison function that defines the sort order.
12198 * Defaults to sorting by numeric value.
12200 sort : function(dir, fn){
12201 this._sort('value', dir, fn);
12205 * Reorders each of the items based on a mapping from old index to new index. Internally this
12206 * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
12207 * @param {Object} mapping Mapping from old item index to new item index
12209 reorder: function(mapping) {
12210 this.suspendEvents();
12212 var items = this.items,
12214 length = items.length,
12218 //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
12219 for (oldIndex in mapping) {
12220 order[mapping[oldIndex]] = items[oldIndex];
12223 for (index = 0; index < length; index++) {
12224 if (mapping[index] == undefined) {
12225 remaining.push(items[index]);
12229 for (index = 0; index < length; index++) {
12230 if (order[index] == undefined) {
12231 order[index] = remaining.shift();
12236 this.addAll(order);
12238 this.resumeEvents();
12239 this.fireEvent('sort', this);
12243 * Sorts this collection by <b>key</b>s.
12244 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12245 * @param {Function} fn (optional) Comparison function that defines the sort order.
12246 * Defaults to sorting by case insensitive string.
12248 keySort : function(dir, fn){
12249 this._sort('key', dir, fn || function(a, b){
12250 var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
12251 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12256 * Returns a range of items in this collection
12257 * @param {Number} startIndex (optional) The starting index. Defaults to 0.
12258 * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
12259 * @return {Array} An array of items
12261 getRange : function(start, end){
12262 var items = this.items;
12263 if(items.length < 1){
12266 start = start || 0;
12267 end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
12270 for(i = start; i <= end; i++) {
12271 r[r.length] = items[i];
12274 for(i = start; i >= end; i--) {
12275 r[r.length] = items[i];
12282 * Filter the <i>objects</i> in this collection by a specific property.
12283 * Returns a new collection that has been filtered.
12284 * @param {String} property A property on your objects
12285 * @param {String/RegExp} value Either string that the property values
12286 * should start with or a RegExp to test against the property
12287 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
12288 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
12289 * @return {MixedCollection} The new filtered collection
12291 filter : function(property, value, anyMatch, caseSensitive){
12292 if(Ext.isEmpty(value, false)){
12293 return this.clone();
12295 value = this.createValueMatcher(value, anyMatch, caseSensitive);
12296 return this.filterBy(function(o){
12297 return o && value.test(o[property]);
12302 * Filter by a function. Returns a <i>new</i> collection that has been filtered.
12303 * The passed function will be called with each object in the collection.
12304 * If the function returns true, the value is included otherwise it is filtered.
12305 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12306 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12307 * @return {MixedCollection} The new filtered collection
12309 filterBy : function(fn, scope){
12310 var r = new Ext.util.MixedCollection();
12311 r.getKey = this.getKey;
12312 var k = this.keys, it = this.items;
12313 for(var i = 0, len = it.length; i < len; i++){
12314 if(fn.call(scope||this, it[i], k[i])){
12315 r.add(k[i], it[i]);
12322 * Finds the index of the first matching object in this collection by a specific property/value.
12323 * @param {String} property The name of a property on your objects.
12324 * @param {String/RegExp} value A string that the property values
12325 * should start with or a RegExp to test against the property.
12326 * @param {Number} start (optional) The index to start searching at (defaults to 0).
12327 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
12328 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
12329 * @return {Number} The matched index or -1
12331 findIndex : function(property, value, start, anyMatch, caseSensitive){
12332 if(Ext.isEmpty(value, false)){
12335 value = this.createValueMatcher(value, anyMatch, caseSensitive);
12336 return this.findIndexBy(function(o){
12337 return o && value.test(o[property]);
12342 * Find the index of the first matching object in this collection by a function.
12343 * If the function returns <i>true</i> it is considered a match.
12344 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
12345 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12346 * @param {Number} start (optional) The index to start searching at (defaults to 0).
12347 * @return {Number} The matched index or -1
12349 findIndexBy : function(fn, scope, start){
12350 var k = this.keys, it = this.items;
12351 for(var i = (start||0), len = it.length; i < len; i++){
12352 if(fn.call(scope||this, it[i], k[i])){
12360 * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
12361 * and by Ext.data.Store#filter
12363 * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
12364 * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
12365 * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
12366 * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
12368 createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
12369 if (!value.exec) { // not a regex
12370 var er = Ext.escapeRe;
12371 value = String(value);
12373 if (anyMatch === true) {
12376 value = '^' + er(value);
12377 if (exactMatch === true) {
12381 value = new RegExp(value, caseSensitive ? '' : 'i');
12387 * Creates a shallow copy of this collection
12388 * @return {MixedCollection}
12390 clone : function(){
12391 var r = new Ext.util.MixedCollection();
12392 var k = this.keys, it = this.items;
12393 for(var i = 0, len = it.length; i < len; i++){
12394 r.add(k[i], it[i]);
12396 r.getKey = this.getKey;
12401 * This method calls {@link #item item()}.
12402 * Returns the item associated with the passed key OR index. Key has priority
12403 * over index. This is the equivalent of calling {@link #key} first, then if
12404 * nothing matched calling {@link #itemAt}.
12405 * @param {String/Number} key The key or index of the item.
12406 * @return {Object} If the item is found, returns the item. If the item was
12407 * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
12408 * returns <tt>null</tt>.
12410 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;/**
12411 * @class Ext.util.JSON
12412 * Modified version of Douglas Crockford"s json.js that doesn"t
12413 * mess with the Object prototype
12414 * http://www.json.org/js.html
12417 Ext.util.JSON = new (function(){
12418 var useHasOwn = !!{}.hasOwnProperty,
12419 isNative = function() {
12420 var useNative = null;
12422 return function() {
12423 if (useNative === null) {
12424 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
12430 pad = function(n) {
12431 return n < 10 ? "0" + n : n;
12433 doDecode = function(json){
12434 return eval("(" + json + ')');
12436 doEncode = function(o){
12437 if(!Ext.isDefined(o) || o === null){
12439 }else if(Ext.isArray(o)){
12440 return encodeArray(o);
12441 }else if(Ext.isDate(o)){
12442 return Ext.util.JSON.encodeDate(o);
12443 }else if(Ext.isString(o)){
12444 return encodeString(o);
12445 }else if(typeof o == "number"){
12446 //don't use isNumber here, since finite checks happen inside isNumber
12447 return isFinite(o) ? String(o) : "null";
12448 }else if(Ext.isBoolean(o)){
12451 var a = ["{"], b, i, v;
12453 // don't encode DOM objects
12454 if(!o.getElementsByTagName){
12455 if(!useHasOwn || o.hasOwnProperty(i)) {
12457 switch (typeof v) {
12466 a.push(doEncode(i), ":",
12467 v === null ? "null" : doEncode(v));
12486 encodeString = function(s){
12487 if (/["\\\x00-\x1f]/.test(s)) {
12488 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12493 c = b.charCodeAt();
12495 Math.floor(c / 16).toString(16) +
12496 (c % 16).toString(16);
12499 return '"' + s + '"';
12501 encodeArray = function(o){
12502 var a = ["["], b, i, l = o.length, v;
12503 for (i = 0; i < l; i += 1) {
12505 switch (typeof v) {
12514 a.push(v === null ? "null" : Ext.util.JSON.encode(v));
12523 * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
12524 * <b>The returned value includes enclosing double quotation marks.</b></p>
12525 * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
12526 * <p>To override this:</p><pre><code>
12527 Ext.util.JSON.encodeDate = function(d) {
12528 return d.format('"Y-m-d"');
12531 * @param {Date} d The Date to encode
12532 * @return {String} The string literal to use in a JSON string.
12534 this.encodeDate = function(o){
12535 return '"' + o.getFullYear() + "-" +
12536 pad(o.getMonth() + 1) + "-" +
12537 pad(o.getDate()) + "T" +
12538 pad(o.getHours()) + ":" +
12539 pad(o.getMinutes()) + ":" +
12540 pad(o.getSeconds()) + '"';
12544 * Encodes an Object, Array or other value
12545 * @param {Mixed} o The variable to encode
12546 * @return {String} The JSON string
12548 this.encode = function() {
12550 return function(o) {
12552 // setup encoding function on first access
12553 ec = isNative() ? JSON.stringify : doEncode;
12561 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
12562 * @param {String} json The JSON string
12563 * @return {Object} The resulting object
12565 this.decode = function() {
12567 return function(json) {
12569 // setup decoding function on first access
12570 dc = isNative() ? JSON.parse : doDecode;
12578 * Shorthand for {@link Ext.util.JSON#encode}
12579 * @param {Mixed} o The variable to encode
12580 * @return {String} The JSON string
12584 Ext.encode = Ext.util.JSON.encode;
12586 * Shorthand for {@link Ext.util.JSON#decode}
12587 * @param {String} json The JSON string
12588 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
12589 * @return {Object} The resulting object
12593 Ext.decode = Ext.util.JSON.decode;
12595 * @class Ext.util.Format
12596 * Reusable data formatting functions
12599 Ext.util.Format = function(){
12600 var trimRe = /^\s+|\s+$/g,
12601 stripTagsRE = /<\/?[^>]+>/gi,
12602 stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
12603 nl2brRe = /\r?\n/g;
12607 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12608 * @param {String} value The string to truncate
12609 * @param {Number} length The maximum length to allow before truncating
12610 * @param {Boolean} word True to try to find a common work break
12611 * @return {String} The converted text
12613 ellipsis : function(value, len, word){
12614 if(value && value.length > len){
12616 var vs = value.substr(0, len - 2),
12617 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
12618 if(index == -1 || index < (len - 15)){
12619 return value.substr(0, len - 3) + "...";
12621 return vs.substr(0, index) + "...";
12624 return value.substr(0, len - 3) + "...";
12631 * Checks a reference and converts it to empty string if it is undefined
12632 * @param {Mixed} value Reference to check
12633 * @return {Mixed} Empty string if converted, otherwise the original value
12635 undef : function(value){
12636 return value !== undefined ? value : "";
12640 * Checks a reference and converts it to the default value if it's empty
12641 * @param {Mixed} value Reference to check
12642 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
12645 defaultValue : function(value, defaultValue){
12646 return value !== undefined && value !== '' ? value : defaultValue;
12650 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12651 * @param {String} value The string to encode
12652 * @return {String} The encoded text
12654 htmlEncode : function(value){
12655 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
12659 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12660 * @param {String} value The string to decode
12661 * @return {String} The decoded text
12663 htmlDecode : function(value){
12664 return !value ? value : String(value).replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&");
12668 * Trims any whitespace from either side of a string
12669 * @param {String} value The text to trim
12670 * @return {String} The trimmed text
12672 trim : function(value){
12673 return String(value).replace(trimRe, "");
12677 * Returns a substring from within an original string
12678 * @param {String} value The original text
12679 * @param {Number} start The start index of the substring
12680 * @param {Number} length The length of the substring
12681 * @return {String} The substring
12683 substr : function(value, start, length){
12684 return String(value).substr(start, length);
12688 * Converts a string to all lower case letters
12689 * @param {String} value The text to convert
12690 * @return {String} The converted text
12692 lowercase : function(value){
12693 return String(value).toLowerCase();
12697 * Converts a string to all upper case letters
12698 * @param {String} value The text to convert
12699 * @return {String} The converted text
12701 uppercase : function(value){
12702 return String(value).toUpperCase();
12706 * Converts the first character only of a string to upper case
12707 * @param {String} value The text to convert
12708 * @return {String} The converted text
12710 capitalize : function(value){
12711 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12715 call : function(value, fn){
12716 if(arguments.length > 2){
12717 var args = Array.prototype.slice.call(arguments, 2);
12718 args.unshift(value);
12719 return eval(fn).apply(window, args);
12721 return eval(fn).call(window, value);
12726 * Format a number as US currency
12727 * @param {Number/String} value The numeric value to format
12728 * @return {String} The formatted currency string
12730 usMoney : function(v){
12731 v = (Math.round((v-0)*100))/100;
12732 v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12734 var ps = v.split('.'),
12736 sub = ps[1] ? '.'+ ps[1] : '.00',
12737 r = /(\d+)(\d{3})/;
12738 while (r.test(whole)) {
12739 whole = whole.replace(r, '$1' + ',' + '$2');
12742 if(v.charAt(0) == '-'){
12743 return '-$' + v.substr(1);
12749 * Parse a value into a formatted date using the specified format pattern.
12750 * @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)
12751 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12752 * @return {String} The formatted date string
12754 date : function(v, format){
12758 if(!Ext.isDate(v)){
12759 v = new Date(Date.parse(v));
12761 return v.dateFormat(format || "m/d/Y");
12765 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12766 * @param {String} format Any valid date format string
12767 * @return {Function} The date formatting function
12769 dateRenderer : function(format){
12770 return function(v){
12771 return Ext.util.Format.date(v, format);
12776 * Strips all HTML tags
12777 * @param {Mixed} value The text from which to strip tags
12778 * @return {String} The stripped text
12780 stripTags : function(v){
12781 return !v ? v : String(v).replace(stripTagsRE, "");
12785 * Strips all script tags
12786 * @param {Mixed} value The text from which to strip script tags
12787 * @return {String} The stripped text
12789 stripScripts : function(v){
12790 return !v ? v : String(v).replace(stripScriptsRe, "");
12794 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
12795 * @param {Number/String} size The numeric value to format
12796 * @return {String} The formatted file size
12798 fileSize : function(size){
12800 return size + " bytes";
12801 } else if(size < 1048576) {
12802 return (Math.round(((size*10) / 1024))/10) + " KB";
12804 return (Math.round(((size*10) / 1048576))/10) + " MB";
12809 * It does simple math for use in a template, for example:<pre><code>
12810 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
12812 * @return {Function} A function that operates on the passed value.
12816 return function(v, a){
12818 fns[a] = new Function('v', 'return v ' + a + ';');
12825 * Rounds the passed number to the required decimal precision.
12826 * @param {Number/String} value The numeric value to round.
12827 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
12828 * @return {Number} The rounded value.
12830 round : function(value, precision) {
12831 var result = Number(value);
12832 if (typeof precision == 'number') {
12833 precision = Math.pow(10, precision);
12834 result = Math.round(value * precision) / precision;
12840 * Formats the number according to the format string.
12841 * <div style="margin-left:40px">examples (123456.789):
12842 * <div style="margin-left:10px">
12843 * 0 - (123456) show only digits, no precision<br>
12844 * 0.00 - (123456.78) show only digits, 2 precision<br>
12845 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
12846 * 0,000 - (123,456) show comma and digits, no precision<br>
12847 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
12848 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
12849 * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
12850 * For example: 0.000,00/i
12852 * @param {Number} v The number to format.
12853 * @param {String} format The way you would like to format this text.
12854 * @return {String} The formatted number.
12856 number: function(v, format) {
12860 v = Ext.num(v, NaN);
12870 if(format.substr(format.length - 2) == '/i'){
12871 format = format.substr(0, format.length - 2);
12877 var hasComma = format.indexOf(comma) != -1,
12878 psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
12880 if(1 < psplit.length){
12881 v = v.toFixed(psplit[1].length);
12882 }else if(2 < psplit.length){
12883 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
12888 var fnum = v.toString();
12890 psplit = fnum.split('.');
12893 var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;
12895 for (var i = 0; i < j; i += n) {
12899 parr[parr.length] = cnum.substr(i, n);
12902 fnum = parr.join(comma);
12904 fnum += dec + psplit[1];
12908 fnum = psplit[0] + dec + psplit[1];
12912 return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
12916 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
12917 * @param {String} format Any valid number format string for {@link #number}
12918 * @return {Function} The number formatting function
12920 numberRenderer : function(format){
12921 return function(v){
12922 return Ext.util.Format.number(v, format);
12927 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
12928 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
12929 * if the value is 0 or greater than 1.
12930 * @param {Number} value The value to compare against
12931 * @param {String} singular The singular form of the word
12932 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
12934 plural : function(v, s, p){
12935 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
12939 * Converts newline characters to the HTML tag <br/>
12940 * @param {String} The string value to format.
12941 * @return {String} The string with embedded <br/> tags in place of newlines.
12943 nl2br : function(v){
12944 return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
12949 * @class Ext.XTemplate
12950 * @extends Ext.Template
12951 * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
12952 * <li>Autofilling arrays using templates and sub-templates</li>
12953 * <li>Conditional processing with basic comparison operators</li>
12954 * <li>Basic math function support</li>
12955 * <li>Execute arbitrary inline code with special built-in template variables</li>
12956 * <li>Custom member functions</li>
12957 * <li>Many special tags and built-in operators that aren't defined as part of
12958 * the API, but are supported in the templates that can be created</li>
12960 * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
12961 * <li>{@link Ext.DataView}</li>
12962 * <li>{@link Ext.ListView}</li>
12963 * <li>{@link Ext.form.ComboBox}</li>
12964 * <li>{@link Ext.grid.TemplateColumn}</li>
12965 * <li>{@link Ext.grid.GroupingView}</li>
12966 * <li>{@link Ext.menu.Item}</li>
12967 * <li>{@link Ext.layout.MenuLayout}</li>
12968 * <li>{@link Ext.ColorPalette}</li>
12971 * <p>For example usage {@link #XTemplate see the constructor}.</p>
12974 * The {@link Ext.Template#Template Ext.Template constructor} describes
12975 * the acceptable parameters to pass to the constructor. The following
12976 * examples demonstrate all of the supported features.</p>
12978 * <div class="mdetail-params"><ul>
12980 * <li><b><u>Sample Data</u></b>
12981 * <div class="sub-desc">
12982 * <p>This is the data object used for reference in each code example:</p>
12985 name: 'Jack Slocum',
12986 title: 'Lead Developer',
12987 company: 'Ext JS, LLC',
12988 email: 'jack@extjs.com',
12989 address: '4 Red Bulls Drive',
12993 drinks: ['Red Bull', 'Coffee', 'Water'],
12995 name: 'Sara Grace',
13001 name: 'John James',
13010 * <li><b><u>Auto filling of arrays</u></b>
13011 * <div class="sub-desc">
13012 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
13013 * to process the provided data object:
13015 * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
13016 * repeating the template block inside the <tt>tpl</tt> tag for each item in the
13018 * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
13019 * <li>While processing an array, the special variable <tt>{#}</tt>
13020 * will provide the current array index + 1 (starts at 1, not 0).</li>
13024 <tpl <b>for</b>=".">...</tpl> // loop through array at root node
13025 <tpl <b>for</b>="foo">...</tpl> // loop through array at foo node
13026 <tpl <b>for</b>="foo.bar">...</tpl> // loop through array at foo.bar node
13028 * Using the sample data above:
13030 var tpl = new Ext.XTemplate(
13032 '<tpl <b>for</b>=".">', // process the data.kids node
13033 '<p>{#}. {name}</p>', // use current array index to autonumber
13036 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
13038 * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
13039 * to access specified members of the provided data object to populate the template:</p>
13041 var tpl = new Ext.XTemplate(
13042 '<p>Name: {name}</p>',
13043 '<p>Title: {title}</p>',
13044 '<p>Company: {company}</p>',
13046 '<tpl <b>for="kids"</b>>', // interrogate the kids property within the data
13047 '<p>{name}</p>',
13050 tpl.overwrite(panel.body, data); // pass the root node of the data object
13052 * <p>Flat arrays that contain values (and not objects) can be auto-rendered
13053 * using the special <b><tt>{.}</tt></b> variable inside a loop. This variable
13054 * will represent the value of the array at the current index:</p>
13056 var tpl = new Ext.XTemplate(
13057 '<p>{name}\'s favorite beverages:</p>',
13058 '<tpl for="drinks">',
13059 '<div> - {.}</div>',
13062 tpl.overwrite(panel.body, data);
13064 * <p>When processing a sub-template, for example while looping through a child array,
13065 * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
13067 var tpl = new Ext.XTemplate(
13068 '<p>Name: {name}</p>',
13070 '<tpl for="kids">',
13071 '<tpl if="age > 1">',
13072 '<p>{name}</p>',
13073 '<p>Dad: {<b>parent</b>.name}</p>',
13077 tpl.overwrite(panel.body, data);
13083 * <li><b><u>Conditional processing with basic comparison operators</u></b>
13084 * <div class="sub-desc">
13085 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
13086 * to provide conditional checks for deciding whether or not to render specific
13087 * parts of the template. Notes:<div class="sub-desc"><ul>
13088 * <li>Double quotes must be encoded if used within the conditional</li>
13089 * <li>There is no <tt>else</tt> operator — if needed, two opposite
13090 * <tt>if</tt> statements should be used.</li>
13093 <tpl if="age > 1 && age < 10">Child</tpl>
13094 <tpl if="age >= 10 && age < 18">Teenager</tpl>
13095 <tpl <b>if</b>="this.isGirl(name)">...</tpl>
13096 <tpl <b>if</b>="id==\'download\'">...</tpl>
13097 <tpl <b>if</b>="needsIcon"><img src="{icon}" class="{iconCls}"/></tpl>
13099 <tpl if="name == "Jack"">Hello</tpl>
13100 // encode " if it is part of the condition, e.g.
13101 <tpl if="name == &quot;Jack&quot;">Hello</tpl>
13103 * Using the sample data above:
13105 var tpl = new Ext.XTemplate(
13106 '<p>Name: {name}</p>',
13108 '<tpl for="kids">',
13109 '<tpl if="age > 1">',
13110 '<p>{name}</p>',
13114 tpl.overwrite(panel.body, data);
13120 * <li><b><u>Basic math support</u></b>
13121 * <div class="sub-desc">
13122 * <p>The following basic math operators may be applied directly on numeric
13123 * data values:</p><pre>
13128 var tpl = new Ext.XTemplate(
13129 '<p>Name: {name}</p>',
13131 '<tpl for="kids">',
13132 '<tpl if="age &gt; 1">', // <-- Note that the > is encoded
13133 '<p>{#}: {name}</p>', // <-- Auto-number each item
13134 '<p>In 5 Years: {age+5}</p>', // <-- Basic math
13135 '<p>Dad: {parent.name}</p>',
13139 tpl.overwrite(panel.body, data);
13145 * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
13146 * <div class="sub-desc">
13147 * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
13148 * in the scope of the template. There are some special variables available in that code:
13150 * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
13151 * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
13152 * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
13153 * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
13154 * loop you are in (1-based).</li>
13155 * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
13156 * of the array you are looping.</li>
13157 * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
13159 * This example demonstrates basic row striping using an inline code block and the
13160 * <tt>xindex</tt> variable:</p>
13162 var tpl = new Ext.XTemplate(
13163 '<p>Name: {name}</p>',
13164 '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
13166 '<tpl for="kids">',
13167 '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
13172 tpl.overwrite(panel.body, data);
13177 * <li><b><u>Template member functions</u></b>
13178 * <div class="sub-desc">
13179 * <p>One or more member functions can be specified in a configuration
13180 * object passed into the XTemplate constructor for more complex processing:</p>
13182 var tpl = new Ext.XTemplate(
13183 '<p>Name: {name}</p>',
13185 '<tpl for="kids">',
13186 '<tpl if="this.isGirl(name)">',
13187 '<p>Girl: {name} - {age}</p>',
13189 // use opposite if statement to simulate 'else' processing:
13190 '<tpl if="this.isGirl(name) == false">',
13191 '<p>Boy: {name} - {age}</p>',
13193 '<tpl if="this.isBaby(age)">',
13194 '<p>{name} is a baby!</p>',
13196 '</tpl></p>',
13198 // XTemplate configuration:
13200 disableFormats: true,
13201 // member functions:
13202 isGirl: function(name){
13203 return name == 'Sara Grace';
13205 isBaby: function(age){
13210 tpl.overwrite(panel.body, data);
13217 * @param {Mixed} config
13219 Ext.XTemplate = function(){
13220 Ext.XTemplate.superclass.constructor.apply(this, arguments);
13224 re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
13225 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
13226 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
13227 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
13235 RETURN = 'return ',
13236 WITHVALUES = 'with(values){ ';
13238 s = ['<tpl>', s, '</tpl>'].join('');
13240 while((m = s.match(re))){
13241 var m2 = m[0].match(nameRe),
13242 m3 = m[0].match(ifRe),
13243 m4 = m[0].match(execRe),
13247 name = m2 && m2[1] ? m2[1] : '';
13250 exp = m3 && m3[1] ? m3[1] : null;
13252 fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
13256 exp = m4 && m4[1] ? m4[1] : null;
13258 exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
13263 case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
13264 case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
13265 default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
13275 s = s.replace(m[0], '{xtpl'+ id + '}');
13278 Ext.each(tpls, function(t) {
13281 me.master = tpls[tpls.length-1];
13284 Ext.extend(Ext.XTemplate, Ext.Template, {
13286 re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
13288 codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
13291 applySubTemplate : function(id, values, parent, xindex, xcount){
13297 if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
13298 (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
13301 vs = t.target ? t.target.call(me, values, parent) : values;
13303 parent = t.target ? values : parent;
13304 if(t.target && Ext.isArray(vs)){
13305 Ext.each(vs, function(v, i) {
13306 buf[buf.length] = t.compiled.call(me, v, parent, i+1, len);
13308 return buf.join('');
13310 return t.compiled.call(me, vs, parent, xindex, xcount);
13314 compileTpl : function(tpl){
13315 var fm = Ext.util.Format,
13316 useF = this.disableFormats !== true,
13317 sep = Ext.isGecko ? "+" : ",",
13320 function fn(m, name, format, args, math){
13321 if(name.substr(0, 4) == 'xtpl'){
13322 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
13327 }else if(name === '#'){
13329 }else if(name.indexOf('.') != -1){
13332 v = "values['" + name + "']";
13335 v = '(' + v + math + ')';
13337 if (format && useF) {
13338 args = args ? ',' + args : "";
13339 if(format.substr(0, 5) != "this."){
13340 format = "fm." + format + '(';
13342 format = 'this.call("'+ format.substr(5) + '", ';
13346 args= ''; format = "("+v+" === undefined ? '' : ";
13348 return "'"+ sep + format + v + args + ")"+sep+"'";
13351 function codeFn(m, code){
13352 // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
13353 return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
13356 // branched to use + in gecko and [].join() in others
13358 body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
13359 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
13362 body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
13363 body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
13364 body.push("'].join('');};");
13365 body = body.join('');
13372 * Returns an HTML fragment of this template with the specified values applied.
13373 * @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'})
13374 * @return {String} The HTML fragment
13376 applyTemplate : function(values){
13377 return this.master.compiled.call(this, values, {}, 1, 1);
13381 * Compile the template to a function for optimized performance. Recommended if the template will be used frequently.
13382 * @return {Function} The compiled function
13384 compile : function(){return this;}
13391 * @property disableFormats
13401 * Alias for {@link #applyTemplate}
13402 * Returns an HTML fragment of this template with the specified values applied.
13403 * @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'})
13404 * @return {String} The HTML fragment
13405 * @member Ext.XTemplate
13408 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
13411 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
13412 * @param {String/HTMLElement} el A DOM element or its id
13413 * @return {Ext.Template} The created template
13416 Ext.XTemplate.from = function(el){
13417 el = Ext.getDom(el);
13418 return new Ext.XTemplate(el.value || el.innerHTML);
13420 * @class Ext.util.CSS
13421 * Utility class for manipulating CSS rules
13424 Ext.util.CSS = function(){
13426 var doc = document;
13428 var camelRe = /(-[a-z])/gi;
13429 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13433 * Creates a stylesheet from a text blob of rules.
13434 * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
13435 * @param {String} cssText The text containing the css rules
13436 * @param {String} id An id to add to the stylesheet for later removal
13437 * @return {StyleSheet}
13439 createStyleSheet : function(cssText, id){
13441 var head = doc.getElementsByTagName("head")[0];
13442 var rules = doc.createElement("style");
13443 rules.setAttribute("type", "text/css");
13445 rules.setAttribute("id", id);
13448 head.appendChild(rules);
13449 ss = rules.styleSheet;
13450 ss.cssText = cssText;
13453 rules.appendChild(doc.createTextNode(cssText));
13455 rules.cssText = cssText;
13457 head.appendChild(rules);
13458 ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13460 this.cacheStyleSheet(ss);
13465 * Removes a style or link tag by id
13466 * @param {String} id The id of the tag
13468 removeStyleSheet : function(id){
13469 var existing = doc.getElementById(id);
13471 existing.parentNode.removeChild(existing);
13476 * Dynamically swaps an existing stylesheet reference for a new one
13477 * @param {String} id The id of an existing link tag to remove
13478 * @param {String} url The href of the new stylesheet to include
13480 swapStyleSheet : function(id, url){
13481 this.removeStyleSheet(id);
13482 var ss = doc.createElement("link");
13483 ss.setAttribute("rel", "stylesheet");
13484 ss.setAttribute("type", "text/css");
13485 ss.setAttribute("id", id);
13486 ss.setAttribute("href", url);
13487 doc.getElementsByTagName("head")[0].appendChild(ss);
13491 * Refresh the rule cache if you have dynamically added stylesheets
13492 * @return {Object} An object (hash) of rules indexed by selector
13494 refreshCache : function(){
13495 return this.getRules(true);
13499 cacheStyleSheet : function(ss){
13503 try{// try catch for cross domain access issue
13504 var ssRules = ss.cssRules || ss.rules;
13505 for(var j = ssRules.length-1; j >= 0; --j){
13506 rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
13512 * Gets all css rules for the document
13513 * @param {Boolean} refreshCache true to refresh the internal cache
13514 * @return {Object} An object (hash) of rules indexed by selector
13516 getRules : function(refreshCache){
13517 if(rules === null || refreshCache){
13519 var ds = doc.styleSheets;
13520 for(var i =0, len = ds.length; i < len; i++){
13522 this.cacheStyleSheet(ds[i]);
13530 * Gets an an individual CSS rule by selector(s)
13531 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13532 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13533 * @return {CSSRule} The CSS rule or null if one is not found
13535 getRule : function(selector, refreshCache){
13536 var rs = this.getRules(refreshCache);
13537 if(!Ext.isArray(selector)){
13538 return rs[selector.toLowerCase()];
13540 for(var i = 0; i < selector.length; i++){
13541 if(rs[selector[i]]){
13542 return rs[selector[i].toLowerCase()];
13550 * Updates a rule property
13551 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13552 * @param {String} property The css property
13553 * @param {String} value The new value for the property
13554 * @return {Boolean} true If a rule was found and updated
13556 updateRule : function(selector, property, value){
13557 if(!Ext.isArray(selector)){
13558 var rule = this.getRule(selector);
13560 rule.style[property.replace(camelRe, camelFn)] = value;
13564 for(var i = 0; i < selector.length; i++){
13565 if(this.updateRule(selector[i], property, value)){
13574 @class Ext.util.ClickRepeater
13575 @extends Ext.util.Observable
13577 A wrapper class which can be applied to any element. Fires a "click" event while the
13578 mouse is pressed. The interval between firings may be specified in the config but
13579 defaults to 20 milliseconds.
13581 Optionally, a CSS class may be applied to the element during the time it is pressed.
13583 @cfg {Mixed} el The element to act as a button.
13584 @cfg {Number} delay The initial delay before the repeating event begins firing.
13585 Similar to an autorepeat key delay.
13586 @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
13587 @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13588 @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13589 "interval" and "delay" are ignored.
13590 @cfg {Boolean} preventDefault True to prevent the default click event
13591 @cfg {Boolean} stopDefault True to stop the default click event
13594 2007-02-02 jvs Original code contributed by Nige "Animal" White
13595 2007-02-02 jvs Renamed to ClickRepeater
13596 2007-02-03 jvs Modifications for FF Mac and Safari
13599 @param {Mixed} el The element to listen on
13600 @param {Object} config
13602 Ext.util.ClickRepeater = function(el, config)
13604 this.el = Ext.get(el);
13605 this.el.unselectable();
13607 Ext.apply(this, config);
13612 * Fires when the mouse button is depressed.
13613 * @param {Ext.util.ClickRepeater} this
13618 * Fires on a specified interval during the time the element is pressed.
13619 * @param {Ext.util.ClickRepeater} this
13624 * Fires when the mouse key is released.
13625 * @param {Ext.util.ClickRepeater} this
13630 if(!this.disabled){
13631 this.disabled = true;
13635 // allow inline handler
13637 this.on("click", this.handler, this.scope || this);
13640 Ext.util.ClickRepeater.superclass.constructor.call(this);
13643 Ext.extend(Ext.util.ClickRepeater, Ext.util.Observable, {
13646 preventDefault : true,
13647 stopDefault : false,
13651 * Enables the repeater and allows events to fire.
13653 enable: function(){
13655 this.el.on('mousedown', this.handleMouseDown, this);
13657 this.el.on('dblclick', this.handleDblClick, this);
13659 if(this.preventDefault || this.stopDefault){
13660 this.el.on('click', this.eventOptions, this);
13663 this.disabled = false;
13667 * Disables the repeater and stops events from firing.
13669 disable: function(/* private */ force){
13670 if(force || !this.disabled){
13671 clearTimeout(this.timer);
13672 if(this.pressClass){
13673 this.el.removeClass(this.pressClass);
13675 Ext.getDoc().un('mouseup', this.handleMouseUp, this);
13676 this.el.removeAllListeners();
13678 this.disabled = true;
13682 * Convenience function for setting disabled/enabled by boolean.
13683 * @param {Boolean} disabled
13685 setDisabled: function(disabled){
13686 this[disabled ? 'disable' : 'enable']();
13689 eventOptions: function(e){
13690 if(this.preventDefault){
13691 e.preventDefault();
13693 if(this.stopDefault){
13699 destroy : function() {
13700 this.disable(true);
13701 Ext.destroy(this.el);
13702 this.purgeListeners();
13705 handleDblClick : function(){
13706 clearTimeout(this.timer);
13709 this.fireEvent("mousedown", this);
13710 this.fireEvent("click", this);
13714 handleMouseDown : function(){
13715 clearTimeout(this.timer);
13717 if(this.pressClass){
13718 this.el.addClass(this.pressClass);
13720 this.mousedownTime = new Date();
13722 Ext.getDoc().on("mouseup", this.handleMouseUp, this);
13723 this.el.on("mouseout", this.handleMouseOut, this);
13725 this.fireEvent("mousedown", this);
13726 this.fireEvent("click", this);
13728 // Do not honor delay or interval if acceleration wanted.
13729 if (this.accelerate) {
13732 this.timer = this.click.defer(this.delay || this.interval, this);
13736 click : function(){
13737 this.fireEvent("click", this);
13738 this.timer = this.click.defer(this.accelerate ?
13739 this.easeOutExpo(this.mousedownTime.getElapsed(),
13743 this.interval, this);
13746 easeOutExpo : function (t, b, c, d) {
13747 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
13751 handleMouseOut : function(){
13752 clearTimeout(this.timer);
13753 if(this.pressClass){
13754 this.el.removeClass(this.pressClass);
13756 this.el.on("mouseover", this.handleMouseReturn, this);
13760 handleMouseReturn : function(){
13761 this.el.un("mouseover", this.handleMouseReturn, this);
13762 if(this.pressClass){
13763 this.el.addClass(this.pressClass);
13769 handleMouseUp : function(){
13770 clearTimeout(this.timer);
13771 this.el.un("mouseover", this.handleMouseReturn, this);
13772 this.el.un("mouseout", this.handleMouseOut, this);
13773 Ext.getDoc().un("mouseup", this.handleMouseUp, this);
13774 this.el.removeClass(this.pressClass);
13775 this.fireEvent("mouseup", this);
13778 * @class Ext.KeyNav
13779 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
13780 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13781 * way to implement custom navigation schemes for any UI component.</p>
13782 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13783 * pageUp, pageDown, del, home, end. Usage:</p>
13785 var nav = new Ext.KeyNav("my-element", {
13786 "left" : function(e){
13787 this.moveLeft(e.ctrlKey);
13789 "right" : function(e){
13790 this.moveRight(e.ctrlKey);
13792 "enter" : function(e){
13799 * @param {Mixed} el The element to bind to
13800 * @param {Object} config The config
13802 Ext.KeyNav = function(el, config){
13803 this.el = Ext.get(el);
13804 Ext.apply(this, config);
13805 if(!this.disabled){
13806 this.disabled = true;
13811 Ext.KeyNav.prototype = {
13813 * @cfg {Boolean} disabled
13814 * True to disable this KeyNav instance (defaults to false)
13818 * @cfg {String} defaultEventAction
13819 * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key. Valid values are
13820 * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
13821 * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
13823 defaultEventAction: "stopEvent",
13825 * @cfg {Boolean} forceKeyDown
13826 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
13827 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13828 * handle keydown instead of keypress.
13830 forceKeyDown : false,
13833 relay : function(e){
13834 var k = e.getKey();
13835 var h = this.keyToHandler[k];
13837 if(this.doRelay(e, this[h], h) !== true){
13838 e[this.defaultEventAction]();
13844 doRelay : function(e, h, hname){
13845 return h.call(this.scope || this, e);
13848 // possible handlers
13862 // quick lookup hash
13878 stopKeyUp: function(e) {
13879 var k = e.getKey();
13881 if (k >= 37 && k <= 40) {
13882 // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
13883 // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
13889 * Destroy this KeyNav (this is the same as calling disable).
13891 destroy: function(){
13896 * Enable this KeyNav
13898 enable: function() {
13899 if (this.disabled) {
13900 if (Ext.isSafari2) {
13901 // call stopKeyUp() on "keyup" event
13902 this.el.on('keyup', this.stopKeyUp, this);
13905 this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13906 this.disabled = false;
13911 * Disable this KeyNav
13913 disable: function() {
13914 if (!this.disabled) {
13915 if (Ext.isSafari2) {
13916 // remove "keyup" event handler
13917 this.el.un('keyup', this.stopKeyUp, this);
13920 this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13921 this.disabled = true;
13926 * Convenience function for setting disabled/enabled by boolean.
13927 * @param {Boolean} disabled
13929 setDisabled : function(disabled){
13930 this[disabled ? "disable" : "enable"]();
13934 isKeydown: function(){
13935 return this.forceKeyDown || Ext.EventManager.useKeydown;
13939 * @class Ext.KeyMap
13940 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13941 * The constructor accepts the same config object as defined by {@link #addBinding}.
13942 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13943 * combination it will call the function with this signature (if the match is a multi-key
13944 * combination the callback will still be called only once): (String key, Ext.EventObject e)
13945 * A KeyMap can also handle a string representation of keys.<br />
13948 // map one key by key code
13949 var map = new Ext.KeyMap("my-element", {
13950 key: 13, // or Ext.EventObject.ENTER
13955 // map multiple keys to one action by string
13956 var map = new Ext.KeyMap("my-element", {
13962 // map multiple keys to multiple actions by strings and array of codes
13963 var map = new Ext.KeyMap("my-element", [
13966 fn: function(){ alert("Return was pressed"); }
13969 fn: function(){ alert('a, b or c was pressed'); }
13974 fn: function(){ alert('Control + shift + tab was pressed.'); }
13978 * <b>Note: A KeyMap starts enabled</b>
13980 * @param {Mixed} el The element to bind to
13981 * @param {Object} config The config (see {@link #addBinding})
13982 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13984 Ext.KeyMap = function(el, config, eventName){
13985 this.el = Ext.get(el);
13986 this.eventName = eventName || "keydown";
13987 this.bindings = [];
13989 this.addBinding(config);
13994 Ext.KeyMap.prototype = {
13996 * True to stop the event from bubbling and prevent the default browser action if the
13997 * key was handled by the KeyMap (defaults to false)
14003 * Add a new binding to this KeyMap. The following config object properties are supported:
14005 Property Type Description
14006 ---------- --------------- ----------------------------------------------------------------------
14007 key String/Array A single keycode or an array of keycodes to handle
14008 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)
14009 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)
14010 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)
14011 handler Function The function to call when KeyMap finds the expected key combination
14012 fn Function Alias of handler (for backwards-compatibility)
14013 scope Object The scope of the callback function
14014 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)
14020 var map = new Ext.KeyMap(document, {
14021 key: Ext.EventObject.ENTER,
14026 //Add a new binding to the existing KeyMap later
14034 * @param {Object/Array} config A single KeyMap config or an array of configs
14036 addBinding : function(config){
14037 if(Ext.isArray(config)){
14038 Ext.each(config, function(c){
14039 this.addBinding(c);
14043 var keyCode = config.key,
14044 fn = config.fn || config.handler,
14045 scope = config.scope;
14047 if (config.stopEvent) {
14048 this.stopEvent = config.stopEvent;
14051 if(typeof keyCode == "string"){
14053 var keyString = keyCode.toUpperCase();
14054 for(var j = 0, len = keyString.length; j < len; j++){
14055 ks.push(keyString.charCodeAt(j));
14059 var keyArray = Ext.isArray(keyCode);
14061 var handler = function(e){
14062 if(this.checkModifiers(config, e)){
14063 var k = e.getKey();
14065 for(var i = 0, len = keyCode.length; i < len; i++){
14066 if(keyCode[i] == k){
14067 if(this.stopEvent){
14070 fn.call(scope || window, k, e);
14076 if(this.stopEvent){
14079 fn.call(scope || window, k, e);
14084 this.bindings.push(handler);
14088 checkModifiers: function(config, e){
14089 var val, key, keys = ['shift', 'ctrl', 'alt'];
14090 for (var i = 0, len = keys.length; i < len; ++i){
14093 if(!(val === undefined || (val === e[key + 'Key']))){
14101 * Shorthand for adding a single key listener
14102 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14103 * following options:
14104 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14105 * @param {Function} fn The function to call
14106 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
14108 on : function(key, fn, scope){
14109 var keyCode, shift, ctrl, alt;
14110 if(typeof key == "object" && !Ext.isArray(key)){
14129 handleKeyDown : function(e){
14130 if(this.enabled){ //just in case
14131 var b = this.bindings;
14132 for(var i = 0, len = b.length; i < len; i++){
14133 b[i].call(this, e);
14139 * Returns true if this KeyMap is enabled
14140 * @return {Boolean}
14142 isEnabled : function(){
14143 return this.enabled;
14147 * Enables this KeyMap
14149 enable: function(){
14151 this.el.on(this.eventName, this.handleKeyDown, this);
14152 this.enabled = true;
14157 * Disable this KeyMap
14159 disable: function(){
14161 this.el.removeListener(this.eventName, this.handleKeyDown, this);
14162 this.enabled = false;
14167 * Convenience function for setting disabled/enabled by boolean.
14168 * @param {Boolean} disabled
14170 setDisabled : function(disabled){
14171 this[disabled ? "disable" : "enable"]();
14174 * @class Ext.util.TextMetrics
14175 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14176 * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
14177 * should not contain any HTML, otherwise it may not be measured correctly.
14180 Ext.util.TextMetrics = function(){
14184 * Measures the size of the specified text
14185 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14186 * that can affect the size of the rendered text
14187 * @param {String} text The text to measure
14188 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14189 * in order to accurately measure the text height
14190 * @return {Object} An object containing the text's size {width: (width), height: (height)}
14192 measure : function(el, text, fixedWidth){
14194 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
14197 shared.setFixedWidth(fixedWidth || 'auto');
14198 return shared.getSize(text);
14202 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
14203 * the overhead of multiple calls to initialize the style properties on each measurement.
14204 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14205 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14206 * in order to accurately measure the text height
14207 * @return {Ext.util.TextMetrics.Instance} instance The new instance
14209 createInstance : function(el, fixedWidth){
14210 return Ext.util.TextMetrics.Instance(el, fixedWidth);
14215 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14216 var ml = new Ext.Element(document.createElement('div'));
14217 document.body.appendChild(ml.dom);
14218 ml.position('absolute');
14219 ml.setLeftTop(-1000, -1000);
14223 ml.setWidth(fixedWidth);
14228 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14229 * Returns the size of the specified text based on the internal element's style and width properties
14230 * @param {String} text The text to measure
14231 * @return {Object} An object containing the text's size {width: (width), height: (height)}
14233 getSize : function(text){
14235 var s = ml.getSize();
14241 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14242 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14243 * that can affect the size of the rendered text
14244 * @param {String/HTMLElement} el The element, dom node or id
14246 bind : function(el){
14248 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
14253 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14254 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
14255 * to set a fixed width in order to accurately measure the text height.
14256 * @param {Number} width The width to set on the element
14258 setFixedWidth : function(width){
14259 ml.setWidth(width);
14263 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14264 * Returns the measured width of the specified text
14265 * @param {String} text The text to measure
14266 * @return {Number} width The width in pixels
14268 getWidth : function(text){
14269 ml.dom.style.width = 'auto';
14270 return this.getSize(text).width;
14274 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14275 * Returns the measured height of the specified text. For multiline text, be sure to call
14276 * {@link #setFixedWidth} if necessary.
14277 * @param {String} text The text to measure
14278 * @return {Number} height The height in pixels
14280 getHeight : function(text){
14281 return this.getSize(text).height;
14285 instance.bind(bindTo);
14290 Ext.Element.addMethods({
14292 * Returns the width in pixels of the passed text, or the width of the text in this Element.
14293 * @param {String} text The text to measure. Defaults to the innerHTML of the element.
14294 * @param {Number} min (Optional) The minumum value to return.
14295 * @param {Number} max (Optional) The maximum value to return.
14296 * @return {Number} The text width in pixels.
14297 * @member Ext.Element getTextWidth
14299 getTextWidth : function(text, min, max){
14300 return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
14304 * @class Ext.util.Cookies
14305 * Utility class for managing and interacting with cookies.
14308 Ext.util.Cookies = {
14310 * Create a cookie with the specified name and value. Additional settings
14311 * for the cookie may be optionally specified (for example: expiration,
14312 * access restriction, SSL).
14313 * @param {String} name The name of the cookie to set.
14314 * @param {Mixed} value The value to set for the cookie.
14315 * @param {Object} expires (Optional) Specify an expiration date the
14316 * cookie is to persist until. Note that the specified Date object will
14317 * be converted to Greenwich Mean Time (GMT).
14318 * @param {String} path (Optional) Setting a path on the cookie restricts
14319 * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>).
14320 * @param {String} domain (Optional) Setting a domain restricts access to
14321 * pages on a given domain (typically used to allow cookie access across
14322 * subdomains). For example, "extjs.com" will create a cookie that can be
14323 * accessed from any subdomain of extjs.com, including www.extjs.com,
14324 * support.extjs.com, etc.
14325 * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
14326 * should only be accessible via SSL on a page using the HTTPS protocol.
14327 * Defaults to <tt>false</tt>. Note that this will only work if the page
14328 * calling this code uses the HTTPS protocol, otherwise the cookie will be
14329 * created with default options.
14331 set : function(name, value){
14332 var argv = arguments;
14333 var argc = arguments.length;
14334 var expires = (argc > 2) ? argv[2] : null;
14335 var path = (argc > 3) ? argv[3] : '/';
14336 var domain = (argc > 4) ? argv[4] : null;
14337 var secure = (argc > 5) ? argv[5] : false;
14338 document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
14342 * Retrieves cookies that are accessible by the current page. If a cookie
14343 * does not exist, <code>get()</code> returns <tt>null</tt>. The following
14344 * example retrieves the cookie called "valid" and stores the String value
14345 * in the variable <tt>validStatus</tt>.
14347 * var validStatus = Ext.util.Cookies.get("valid");
14349 * @param {String} name The name of the cookie to get
14350 * @return {Mixed} Returns the cookie value for the specified name;
14351 * null if the cookie name does not exist.
14353 get : function(name){
14354 var arg = name + "=";
14355 var alen = arg.length;
14356 var clen = document.cookie.length;
14361 if(document.cookie.substring(i, j) == arg){
14362 return Ext.util.Cookies.getCookieVal(j);
14364 i = document.cookie.indexOf(" ", i) + 1;
14373 * Removes a cookie with the provided name from the browser
14374 * if found by setting its expiration date to sometime in the past.
14375 * @param {String} name The name of the cookie to remove
14377 clear : function(name){
14378 if(Ext.util.Cookies.get(name)){
14379 document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
14385 getCookieVal : function(offset){
14386 var endstr = document.cookie.indexOf(";", offset);
14388 endstr = document.cookie.length;
14390 return unescape(document.cookie.substring(offset, endstr));
14393 * Framework-wide error-handler. Developers can override this method to provide
14394 * custom exception-handling. Framework errors will often extend from the base
14396 * @param {Object/Error} e The thrown exception object.
14398 Ext.handleError = function(e) {
14405 * <p>A base error class. Future implementations are intended to provide more
14406 * robust error handling throughout the framework (<b>in the debug build only</b>)
14407 * to check for common errors and problems. The messages issued by this class
14408 * will aid error checking. Error checks will be automatically removed in the
14409 * production build so that performance is not negatively impacted.</p>
14410 * <p>Some sample messages currently implemented:</p><pre>
14411 "DataProxy attempted to execute an API-action but found an undefined
14412 url / function. Please review your Proxy url/api-configuration."
14414 "Could not locate your "root" property in your server response.
14415 Please review your JsonReader config to ensure the config-property
14416 "root" matches the property your server-response. See the JsonReader
14417 docs for additional assistance."
14419 * <p>An example of the code used for generating error messages:</p><pre><code>
14428 function generateError(data) {
14429 throw new Ext.Error('foo-error', data);
14432 * @param {String} message
14434 Ext.Error = function(message) {
14435 // Try to read the message from Ext.Error.lang
14436 this.message = (this.lang[message]) ? this.lang[message] : message;
14439 Ext.Error.prototype = new Error();
14440 Ext.apply(Ext.Error.prototype, {
14441 // protected. Extensions place their error-strings here.
14449 getName : function() {
14456 getMessage : function() {
14457 return this.message;
14463 toJson : function() {
14464 return Ext.encode(this);