3 * Copyright(c) 2006-2010 Ext JS, LLC
5 * http://www.extjs.com/license
8 * @class Ext.DomHelper
\r
9 * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
\r
10 * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
\r
11 * from your DOM building code.</p>
\r
13 * <p><b><u>DomHelper element specification object</u></b></p>
\r
14 * <p>A specification object is used when creating elements. Attributes of this object
\r
15 * are assumed to be element attributes, except for 4 special attributes:
\r
16 * <div class="mdetail-params"><ul>
\r
17 * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
\r
18 * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
\r
19 * same kind of element definition objects to be created and appended. These can be nested
\r
20 * as deep as you want.</div></li>
\r
21 * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
\r
22 * This will end up being either the "class" attribute on a HTML fragment or className
\r
23 * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
\r
24 * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
\r
27 * <p><b><u>Insertion methods</u></b></p>
\r
28 * <p>Commonly used insertion methods:
\r
29 * <div class="mdetail-params"><ul>
\r
30 * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
\r
31 * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
\r
32 * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
\r
33 * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
\r
34 * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
\r
35 * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
\r
38 * <p><b><u>Example</u></b></p>
\r
39 * <p>This is an example, where an unordered list with 3 children items is appended to an existing
\r
40 * element with id <tt>'my-div'</tt>:<br>
\r
42 var dh = Ext.DomHelper; // create shorthand alias
\r
43 // specification object
\r
48 // append children after creating
\r
49 children: [ // may also specify 'cn' instead of 'children'
\r
50 {tag: 'li', id: 'item0', html: 'List Item 0'},
\r
51 {tag: 'li', id: 'item1', html: 'List Item 1'},
\r
52 {tag: 'li', id: 'item2', html: 'List Item 2'}
\r
55 var list = dh.append(
\r
56 'my-div', // the context element 'my-div' can either be the id or the actual node
\r
57 spec // the specification object
\r
60 * <p>Element creation specification parameters in this class may also be passed as an Array of
\r
61 * specification objects. This can be used to insert multiple sibling nodes into an existing
\r
62 * container very efficiently. For example, to add more list items to the example above:<pre><code>
\r
63 dh.append('my-ul', [
\r
64 {tag: 'li', id: 'item3', html: 'List Item 3'},
\r
65 {tag: 'li', id: 'item4', html: 'List Item 4'}
\r
69 * <p><b><u>Templating</u></b></p>
\r
70 * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
\r
71 * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
\r
72 * insert new elements. Revisiting the example above, we could utilize templating this time:
\r
75 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
\r
77 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
\r
79 for(var i = 0; i < 5, i++){
\r
80 tpl.append(list, [i]); // use template to append to the actual node
\r
83 * <p>An example using a template:<pre><code>
\r
84 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
\r
86 var tpl = new Ext.DomHelper.createTemplate(html);
\r
87 tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack's Site"]);
\r
88 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
\r
91 * <p>The same example using named parameters:<pre><code>
\r
92 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
\r
94 var tpl = new Ext.DomHelper.createTemplate(html);
\r
95 tpl.append('blog-roll', {
\r
97 url: 'http://www.jackslocum.com/',
\r
98 text: "Jack's Site"
\r
100 tpl.append('blog-roll', {
\r
102 url: 'http://www.dustindiaz.com/',
\r
103 text: "Dustin's Site"
\r
105 * </code></pre></p>
\r
107 * <p><b><u>Compiling Templates</u></b></p>
\r
108 * <p>Templates are applied using regular expressions. The performance is great, but if
\r
109 * you are adding a bunch of DOM elements using the same template, you can increase
\r
110 * performance even further by {@link Ext.Template#compile "compiling"} the template.
\r
111 * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
\r
112 * broken up at the different variable points and a dynamic function is created and eval'ed.
\r
113 * The generated function performs string concatenation of these parts and the passed
\r
114 * variables instead of using regular expressions.
\r
116 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
\r
118 var tpl = new Ext.DomHelper.createTemplate(html);
\r
121 //... use template like normal
\r
122 * </code></pre></p>
\r
124 * <p><b><u>Performance Boost</u></b></p>
\r
125 * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
\r
126 * of DOM can significantly boost performance.</p>
\r
127 * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
\r
128 * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
\r
129 * results in the creation of a text node. Usage:</p>
\r
131 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
\r
135 Ext.DomHelper = function(){
\r
136 var tempTableEl = null,
\r
137 emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
\r
138 tableRe = /^table|tbody|tr|td$/i,
\r
140 // kill repeat to save bytes
\r
141 afterbegin = 'afterbegin',
\r
142 afterend = 'afterend',
\r
143 beforebegin = 'beforebegin',
\r
144 beforeend = 'beforeend',
\r
147 tbs = ts+'<tbody>',
\r
148 tbe = '</tbody>'+te,
\r
149 trs = tbs + '<tr>',
\r
153 function doInsert(el, o, returnElement, pos, sibling, append){
\r
154 var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));
\r
155 return returnElement ? Ext.get(newNode, true) : newNode;
\r
158 // build as innerHTML where available
\r
159 function createHtml(o){
\r
167 if(Ext.isString(o)){
\r
169 } else if (Ext.isArray(o)) {
\r
170 for (var i=0; i < o.length; i++) {
\r
172 b += createHtml(o[i]);
\r
176 b += '<' + (o.tag = o.tag || 'div');
\r
177 Ext.iterate(o, function(attr, val){
\r
178 if(!/tag|children|cn|html$/i.test(attr)){
\r
179 if (Ext.isObject(val)) {
\r
180 b += ' ' + attr + '="';
\r
181 Ext.iterate(val, function(key, keyVal){
\r
182 b += key + ':' + keyVal + ';';
\r
186 b += ' ' + ({cls : 'class', htmlFor : 'for'}[attr] || attr) + '="' + val + '"';
\r
190 // Now either just close the tag or try to add children and close the tag.
\r
191 if (emptyTags.test(o.tag)) {
\r
195 if ((cn = o.children || o.cn)) {
\r
196 b += createHtml(cn);
\r
200 b += '</' + o.tag + '>';
\r
206 function ieTable(depth, s, h, e){
\r
207 tempTableEl.innerHTML = [s, h, e].join('');
\r
211 while(++i < depth){
\r
212 el = el.firstChild;
\r
214 // If the result is multiple siblings, then encapsulate them into one fragment.
\r
215 if(ns = el.nextSibling){
\r
216 var df = document.createDocumentFragment();
\r
218 ns = el.nextSibling;
\r
219 df.appendChild(el);
\r
229 * Nasty code for IE's broken table implementation
\r
231 function insertIntoTable(tag, where, el, html) {
\r
235 tempTableEl = tempTableEl || document.createElement('div');
\r
237 if(tag == 'td' && (where == afterbegin || where == beforeend) ||
\r
238 !/td|tr|tbody/i.test(tag) && (where == beforebegin || where == afterend)) {
\r
241 before = where == beforebegin ? el :
\r
242 where == afterend ? el.nextSibling :
\r
243 where == afterbegin ? el.firstChild : null;
\r
245 if (where == beforebegin || where == afterend) {
\r
246 el = el.parentNode;
\r
249 if (tag == 'td' || (tag == 'tr' && (where == beforeend || where == afterbegin))) {
\r
250 node = ieTable(4, trs, html, tre);
\r
251 } else if ((tag == 'tbody' && (where == beforeend || where == afterbegin)) ||
\r
252 (tag == 'tr' && (where == beforebegin || where == afterend))) {
\r
253 node = ieTable(3, tbs, html, tbe);
\r
255 node = ieTable(2, ts, html, te);
\r
257 el.insertBefore(node, before);
\r
264 * Returns the markup for the passed Element(s) config.
\r
265 * @param {Object} o The DOM object spec (and children)
\r
268 markup : function(o){
\r
269 return createHtml(o);
\r
273 * Applies a style specification to an element.
\r
274 * @param {String/HTMLElement} el The element to apply styles to
\r
275 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
\r
276 * a function which returns such a specification.
\r
278 applyStyles : function(el, styles){
\r
285 if(Ext.isFunction(styles)){
\r
286 styles = styles.call();
\r
288 if(Ext.isString(styles)){
\r
289 styles = styles.trim().split(/\s*(?::|;)\s*/);
\r
290 for(len = styles.length; i < len;){
\r
291 el.setStyle(styles[i++], styles[i++]);
\r
293 }else if (Ext.isObject(styles)){
\r
294 el.setStyle(styles);
\r
300 * Inserts an HTML fragment into the DOM.
\r
301 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
\r
302 * @param {HTMLElement} el The context element
\r
303 * @param {String} html The HTML fragment
\r
304 * @return {HTMLElement} The new node
\r
306 insertHtml : function(where, el, html){
\r
315 where = where.toLowerCase();
\r
316 // add these here because they are used in both branches of the condition.
\r
317 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
\r
318 hash[afterend] = ['AfterEnd', 'nextSibling'];
\r
320 if (el.insertAdjacentHTML) {
\r
321 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
\r
324 // add these two to the hash.
\r
325 hash[afterbegin] = ['AfterBegin', 'firstChild'];
\r
326 hash[beforeend] = ['BeforeEnd', 'lastChild'];
\r
327 if ((hashVal = hash[where])) {
\r
328 el.insertAdjacentHTML(hashVal[0], html);
\r
329 return el[hashVal[1]];
\r
332 range = el.ownerDocument.createRange();
\r
333 setStart = 'setStart' + (/end/i.test(where) ? 'After' : 'Before');
\r
335 range[setStart](el);
\r
336 frag = range.createContextualFragment(html);
\r
337 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
\r
338 return el[(where == beforebegin ? 'previous' : 'next') + 'Sibling'];
\r
340 rangeEl = (where == afterbegin ? 'first' : 'last') + 'Child';
\r
341 if (el.firstChild) {
\r
342 range[setStart](el[rangeEl]);
\r
343 frag = range.createContextualFragment(html);
\r
344 if(where == afterbegin){
\r
345 el.insertBefore(frag, el.firstChild);
\r
347 el.appendChild(frag);
\r
350 el.innerHTML = html;
\r
352 return el[rangeEl];
\r
355 throw 'Illegal insertion point -> "' + where + '"';
\r
359 * Creates new DOM element(s) and inserts them before el.
\r
360 * @param {Mixed} el The context element
\r
361 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
362 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
363 * @return {HTMLElement/Ext.Element} The new node
\r
365 insertBefore : function(el, o, returnElement){
\r
366 return doInsert(el, o, returnElement, beforebegin);
\r
370 * Creates new DOM element(s) and inserts them after el.
\r
371 * @param {Mixed} el The context element
\r
372 * @param {Object} o The DOM object spec (and children)
\r
373 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
374 * @return {HTMLElement/Ext.Element} The new node
\r
376 insertAfter : function(el, o, returnElement){
\r
377 return doInsert(el, o, returnElement, afterend, 'nextSibling');
\r
381 * Creates new DOM element(s) and inserts them as the first child of el.
\r
382 * @param {Mixed} el The context element
\r
383 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
384 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
385 * @return {HTMLElement/Ext.Element} The new node
\r
387 insertFirst : function(el, o, returnElement){
\r
388 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
\r
392 * Creates new DOM element(s) and appends them to el.
\r
393 * @param {Mixed} el The context element
\r
394 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
395 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
396 * @return {HTMLElement/Ext.Element} The new node
\r
398 append : function(el, o, returnElement){
\r
399 return doInsert(el, o, returnElement, beforeend, '', true);
\r
403 * Creates new DOM element(s) and overwrites the contents of el with them.
\r
404 * @param {Mixed} el The context element
\r
405 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
406 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
407 * @return {HTMLElement/Ext.Element} The new node
\r
409 overwrite : function(el, o, returnElement){
\r
410 el = Ext.getDom(el);
\r
411 el.innerHTML = createHtml(o);
\r
412 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
\r
415 createHtml : createHtml
\r
419 * @class Ext.DomHelper
\r
421 Ext.apply(Ext.DomHelper,
\r
424 afterbegin = 'afterbegin',
\r
425 afterend = 'afterend',
\r
426 beforebegin = 'beforebegin',
\r
427 beforeend = 'beforeend';
\r
430 function doInsert(el, o, returnElement, pos, sibling, append){
\r
431 el = Ext.getDom(el);
\r
434 newNode = createDom(o, null);
\r
436 el.appendChild(newNode);
\r
438 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
\r
441 newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
\r
443 return returnElement ? Ext.get(newNode, true) : newNode;
\r
448 function createDom(o, parentNode){
\r
456 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
\r
457 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
\r
458 Ext.each(o, function(v) {
\r
461 } else if (Ext.isString(o)) { // Allow a string as a child spec.
\r
462 el = doc.createTextNode(o);
\r
464 el = doc.createElement( o.tag || 'div' );
\r
465 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
\r
466 Ext.iterate(o, function(attr, val){
\r
467 if(!/tag|children|cn|html|style/.test(attr)){
\r
469 el.className = val;
\r
472 el.setAttribute(attr, val);
\r
479 Ext.DomHelper.applyStyles(el, o.style);
\r
481 if ((cn = o.children || o.cn)) {
\r
483 } else if (o.html) {
\r
484 el.innerHTML = o.html;
\r
488 parentNode.appendChild(el);
\r
495 * Creates a new Ext.Template from the DOM object spec.
\r
496 * @param {Object} o The DOM object spec (and children)
\r
497 * @return {Ext.Template} The new template
\r
499 createTemplate : function(o){
\r
500 var html = Ext.DomHelper.createHtml(o);
\r
501 return new Ext.Template(html);
\r
504 /** True to force the use of DOM instead of html fragments @type Boolean */
\r
508 * Creates new DOM element(s) and inserts them before el.
\r
509 * @param {Mixed} el The context element
\r
510 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
511 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
512 * @return {HTMLElement/Ext.Element} The new node
\r
515 insertBefore : function(el, o, returnElement){
\r
516 return doInsert(el, o, returnElement, beforebegin);
\r
520 * Creates new DOM element(s) and inserts them after el.
\r
521 * @param {Mixed} el The context element
\r
522 * @param {Object} o The DOM object spec (and children)
\r
523 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
524 * @return {HTMLElement/Ext.Element} The new node
\r
527 insertAfter : function(el, o, returnElement){
\r
528 return doInsert(el, o, returnElement, afterend, 'nextSibling');
\r
532 * Creates new DOM element(s) and inserts them as the first child of el.
\r
533 * @param {Mixed} el The context element
\r
534 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
535 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
536 * @return {HTMLElement/Ext.Element} The new node
\r
539 insertFirst : function(el, o, returnElement){
\r
540 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
\r
544 * Creates new DOM element(s) and appends them to el.
\r
545 * @param {Mixed} el The context element
\r
546 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
547 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
548 * @return {HTMLElement/Ext.Element} The new node
\r
551 append: function(el, o, returnElement){
\r
552 return doInsert(el, o, returnElement, beforeend, '', true);
\r
556 * Creates new DOM element(s) without inserting them to the document.
\r
557 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
558 * @return {HTMLElement} The new uninserted node
\r
560 createDom: createDom
\r
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
\r
803 Ext.apply(Ext.Template.prototype, {
\r
805 * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format
\r
806 * functions in the template. If the template does not contain
\r
807 * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>
\r
808 * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.
\r
810 var t = new Ext.Template(
\r
811 '<div name="{id}">',
\r
812 '<span class="{cls}">{name} {value}</span>',
\r
815 compiled: true, // {@link #compile} immediately
\r
816 disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting
\r
820 * For a list of available format functions, see {@link Ext.util.Format}.
\r
822 disableFormats : false,
\r
824 * See <code>{@link #disableFormats}</code>.
\r
826 * @property disableFormats
\r
830 * The regular expression used to match template variables
\r
835 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
\r
838 * Returns an HTML fragment of this template with the specified values applied.
\r
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'})
\r
840 * @return {String} The HTML fragment
\r
843 applyTemplate : function(values){
\r
845 useF = me.disableFormats !== true,
\r
846 fm = Ext.util.Format,
\r
850 return me.compiled(values);
\r
852 function fn(m, name, format, args){
\r
853 if (format && useF) {
\r
854 if (format.substr(0, 5) == "this.") {
\r
855 return tpl.call(format.substr(5), values[name], values);
\r
858 // quoted values are required for strings in compiled templates,
\r
859 // but for non compiled we need to strip them
\r
860 // quoted reversed for jsmin
\r
861 var re = /^\s*['"](.*)["']\s*$/;
\r
862 args = args.split(',');
\r
863 for(var i = 0, len = args.length; i < len; i++){
\r
864 args[i] = args[i].replace(re, "$1");
\r
866 args = [values[name]].concat(args);
\r
868 args = [values[name]];
\r
870 return fm[format].apply(fm, args);
\r
873 return values[name] !== undefined ? values[name] : "";
\r
876 return me.html.replace(me.re, fn);
\r
880 * Compiles the template into an internal function, eliminating the RegEx overhead.
\r
881 * @return {Ext.Template} this
\r
884 compile : function(){
\r
886 fm = Ext.util.Format,
\r
887 useF = me.disableFormats !== true,
\r
888 sep = Ext.isGecko ? "+" : ",",
\r
891 function fn(m, name, format, args){
\r
892 if(format && useF){
\r
893 args = args ? ',' + args : "";
\r
894 if(format.substr(0, 5) != "this."){
\r
895 format = "fm." + format + '(';
\r
897 format = 'this.call("'+ format.substr(5) + '", ';
\r
901 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
\r
903 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
\r
906 // branched to use + in gecko and [].join() in others
\r
908 body = "this.compiled = function(values){ return '" +
\r
909 me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
\r
912 body = ["this.compiled = function(values){ return ['"];
\r
913 body.push(me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
\r
914 body.push("'].join('');};");
\r
915 body = body.join('');
\r
921 // private function used to call members
\r
922 call : function(fnName, value, allValues){
\r
923 return this[fnName](value, allValues);
\r
926 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate; /*
\r
927 * This is code is also distributed under MIT license for use
\r
928 * with jQuery and prototype JavaScript libraries.
\r
931 * @class Ext.DomQuery
\r
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).
\r
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>
\r
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.
\r
939 <h4>Element Selectors:</h4>
\r
941 <li> <b>*</b> any element</li>
\r
942 <li> <b>E</b> an element with the tag E</li>
\r
943 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
\r
944 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
\r
945 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
\r
946 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
\r
948 <h4>Attribute Selectors:</h4>
\r
949 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
\r
951 <li> <b>E[foo]</b> has an attribute "foo"</li>
\r
952 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
\r
953 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
\r
954 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
\r
955 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
\r
956 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
\r
957 <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
\r
959 <h4>Pseudo Classes:</h4>
\r
961 <li> <b>E:first-child</b> E is the first child of its parent</li>
\r
962 <li> <b>E:last-child</b> E is the last child of its parent</li>
\r
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>
\r
964 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
\r
965 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
\r
966 <li> <b>E:only-child</b> E is the only child of its parent</li>
\r
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>
\r
968 <li> <b>E:first</b> the first E in the resultset</li>
\r
969 <li> <b>E:last</b> the last E in the resultset</li>
\r
970 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
\r
971 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
\r
972 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
\r
973 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
\r
974 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
\r
975 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
\r
976 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
\r
977 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
\r
978 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
\r
979 <li> <b>E:any(S1|S2|S2)</b> an E element which matches any of the simple selectors S1, S2 or S3//\\</li>
\r
981 <h4>CSS Value Selectors:</h4>
\r
983 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
\r
984 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
\r
985 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
\r
986 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
\r
987 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
\r
988 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
\r
992 Ext.DomQuery = function(){
\r
997 trimRe = /^\s+|\s+$/g,
\r
998 tplRe = /\{(\d+)\}/g,
\r
999 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
\r
1000 tagTokenRe = /^(#)?([\w-\*]+)/,
\r
1001 nthRe = /(\d*)n\+?(\d*)/,
\r
1003 // This is for IE MSXML which does not support expandos.
\r
1004 // IE runs the same speed using setAttribute, however FF slows way down
\r
1005 // and Safari completely fails so they need to continue to use expandos.
\r
1006 isIE = window.ActiveXObject ? true : false,
\r
1009 // this eval is stop the compressor from
\r
1010 // renaming the variable to something shorter
\r
1011 eval("var batch = 30803;");
\r
1013 // Retrieve the child node from a particular
\r
1014 // parent at the specified index.
\r
1015 function child(parent, index){
\r
1017 n = parent.firstChild;
\r
1019 if(n.nodeType == 1){
\r
1024 n = n.nextSibling;
\r
1029 // retrieve the next element node
\r
1030 function next(n){
\r
1031 while((n = n.nextSibling) && n.nodeType != 1);
\r
1035 // retrieve the previous element node
\r
1037 while((n = n.previousSibling) && n.nodeType != 1);
\r
1041 // Mark each child node with a nodeIndex skipping and
\r
1042 // removing empty text nodes.
\r
1043 function children(parent){
\r
1044 var n = parent.firstChild,
\r
1048 nextNode = n.nextSibling;
\r
1049 // clean worthless empty nodes.
\r
1050 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
\r
1051 parent.removeChild(n);
\r
1053 // add an expando nodeIndex
\r
1054 n.nodeIndex = ++nodeIndex;
\r
1062 // nodeSet - array of nodes
\r
1063 // cls - CSS Class
\r
1064 function byClassName(nodeSet, cls){
\r
1068 var result = [], ri = -1;
\r
1069 for(var i = 0, ci; ci = nodeSet[i]; i++){
\r
1070 if((' '+ci.className+' ').indexOf(cls) != -1){
\r
1071 result[++ri] = ci;
\r
1077 function attrValue(n, attr){
\r
1078 // if its an array, use the first node.
\r
1079 if(!n.tagName && typeof n.length != "undefined"){
\r
1086 if(attr == "for"){
\r
1089 if(attr == "class" || attr == "className"){
\r
1090 return n.className;
\r
1092 return n.getAttribute(attr) || n[attr];
\r
1098 // mode - false, /, >, +, ~
\r
1099 // tagName - defaults to "*"
\r
1100 function getNodes(ns, mode, tagName){
\r
1101 var result = [], ri = -1, cs;
\r
1105 tagName = tagName || "*";
\r
1106 // convert to array
\r
1107 if(typeof ns.getElementsByTagName != "undefined"){
\r
1111 // no mode specified, grab all elements by tagName
\r
1114 for(var i = 0, ni; ni = ns[i]; i++){
\r
1115 cs = ni.getElementsByTagName(tagName);
\r
1116 for(var j = 0, ci; ci = cs[j]; j++){
\r
1117 result[++ri] = ci;
\r
1120 // Direct Child mode (/ or >)
\r
1121 // E > F or E/F all direct children elements of E that have the tag
\r
1122 } else if(mode == "/" || mode == ">"){
\r
1123 var utag = tagName.toUpperCase();
\r
1124 for(var i = 0, ni, cn; ni = ns[i]; i++){
\r
1125 cn = ni.childNodes;
\r
1126 for(var j = 0, cj; cj = cn[j]; j++){
\r
1127 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
\r
1128 result[++ri] = cj;
\r
1132 // Immediately Preceding mode (+)
\r
1133 // E + F all elements with the tag F that are immediately preceded by an element with the tag E
\r
1134 }else if(mode == "+"){
\r
1135 var utag = tagName.toUpperCase();
\r
1136 for(var i = 0, n; n = ns[i]; i++){
\r
1137 while((n = n.nextSibling) && n.nodeType != 1);
\r
1138 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
\r
1142 // Sibling mode (~)
\r
1143 // E ~ F all elements with the tag F that are preceded by a sibling element with the tag E
\r
1144 }else if(mode == "~"){
\r
1145 var utag = tagName.toUpperCase();
\r
1146 for(var i = 0, n; n = ns[i]; i++){
\r
1147 while((n = n.nextSibling)){
\r
1148 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
\r
1157 function concat(a, b){
\r
1159 return a.concat(b);
\r
1161 for(var i = 0, l = b.length; i < l; i++){
\r
1162 a[a.length] = b[i];
\r
1167 function byTag(cs, tagName){
\r
1168 if(cs.tagName || cs == document){
\r
1174 var result = [], ri = -1;
\r
1175 tagName = tagName.toLowerCase();
\r
1176 for(var i = 0, ci; ci = cs[i]; i++){
\r
1177 if(ci.nodeType == 1 && ci.tagName.toLowerCase() == tagName){
\r
1178 result[++ri] = ci;
\r
1184 function byId(cs, id){
\r
1185 if(cs.tagName || cs == document){
\r
1191 var result = [], ri = -1;
\r
1192 for(var i = 0, ci; ci = cs[i]; i++){
\r
1193 if(ci && ci.id == id){
\r
1194 result[++ri] = ci;
\r
1201 // operators are =, !=, ^=, $=, *=, %=, |= and ~=
\r
1202 // custom can be "{"
\r
1203 function byAttribute(cs, attr, value, op, custom){
\r
1206 useGetStyle = custom == "{",
\r
1207 fn = Ext.DomQuery.operators[op],
\r
1210 for(var i = 0, ci; ci = cs[i]; i++){
\r
1211 // skip non-element nodes.
\r
1212 if(ci.nodeType != 1){
\r
1216 innerHTML = ci.innerHTML;
\r
1217 // we only need to change the property names if we're dealing with html nodes, not XML
\r
1218 if(innerHTML !== null && innerHTML !== undefined){
\r
1220 a = Ext.DomQuery.getStyle(ci, attr);
\r
1221 } else if (attr == "class" || attr == "className"){
\r
1223 } else if (attr == "for"){
\r
1225 } else if (attr == "href"){
\r
1226 // getAttribute href bug
\r
1227 // http://www.glennjones.net/Post/809/getAttributehrefbug.htm
\r
1228 a = ci.getAttribute("href", 2);
\r
1230 a = ci.getAttribute(attr);
\r
1233 a = ci.getAttribute(attr);
\r
1235 if((fn && fn(a, value)) || (!fn && a)){
\r
1236 result[++ri] = ci;
\r
1242 function byPseudo(cs, name, value){
\r
1243 return Ext.DomQuery.pseudos[name](cs, value);
\r
1246 function nodupIEXml(cs){
\r
1249 cs[0].setAttribute("_nodup", d);
\r
1251 for(var i = 1, len = cs.length; i < len; i++){
\r
1253 if(!c.getAttribute("_nodup") != d){
\r
1254 c.setAttribute("_nodup", d);
\r
1258 for(var i = 0, len = cs.length; i < len; i++){
\r
1259 cs[i].removeAttribute("_nodup");
\r
1264 function nodup(cs){
\r
1268 var len = cs.length, c, i, r = cs, cj, ri = -1;
\r
1269 if(!len || typeof cs.nodeType != "undefined" || len == 1){
\r
1272 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
\r
1273 return nodupIEXml(cs);
\r
1277 for(i = 1; c = cs[i]; i++){
\r
1278 if(c._nodup != d){
\r
1282 for(var j = 0; j < i; j++){
\r
1285 for(j = i+1; cj = cs[j]; j++){
\r
1286 if(cj._nodup != d){
\r
1297 function quickDiffIEXml(c1, c2){
\r
1300 for(var i = 0, len = c1.length; i < len; i++){
\r
1301 c1[i].setAttribute("_qdiff", d);
\r
1303 for(var i = 0, len = c2.length; i < len; i++){
\r
1304 if(c2[i].getAttribute("_qdiff") != d){
\r
1305 r[r.length] = c2[i];
\r
1308 for(var i = 0, len = c1.length; i < len; i++){
\r
1309 c1[i].removeAttribute("_qdiff");
\r
1314 function quickDiff(c1, c2){
\r
1315 var len1 = c1.length,
\r
1321 if(isIE && typeof c1[0].selectSingleNode != "undefined"){
\r
1322 return quickDiffIEXml(c1, c2);
\r
1324 for(var i = 0; i < len1; i++){
\r
1327 for(var i = 0, len = c2.length; i < len; i++){
\r
1328 if(c2[i]._qdiff != d){
\r
1329 r[r.length] = c2[i];
\r
1335 function quickId(ns, mode, root, id){
\r
1337 var d = root.ownerDocument || root;
\r
1338 return d.getElementById(id);
\r
1340 ns = getNodes(ns, mode, "*");
\r
1341 return byId(ns, id);
\r
1345 getStyle : function(el, name){
\r
1346 return Ext.fly(el).getStyle(name);
\r
1349 * Compiles a selector/xpath query into a reusable function. The returned function
\r
1350 * takes one parameter "root" (optional), which is the context node from where the query should start.
\r
1351 * @param {String} selector The selector/xpath query
\r
1352 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
\r
1353 * @return {Function}
\r
1355 compile : function(path, type){
\r
1356 type = type || "select";
\r
1358 // setup fn preamble
\r
1359 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
\r
1362 matchers = Ext.DomQuery.matchers,
\r
1363 matchersLn = matchers.length,
\r
1365 // accept leading mode switch
\r
1366 lmode = path.match(modeRe);
\r
1368 if(lmode && lmode[1]){
\r
1369 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
\r
1370 path = path.replace(lmode[1], "");
\r
1373 // strip leading slashes
\r
1374 while(path.substr(0, 1)=="/"){
\r
1375 path = path.substr(1);
\r
1378 while(path && lastPath != path){
\r
1380 var tokenMatch = path.match(tagTokenRe);
\r
1381 if(type == "select"){
\r
1384 if(tokenMatch[1] == "#"){
\r
1385 fn[fn.length] = 'n = quickId(n, mode, root, "'+tokenMatch[2]+'");';
\r
1387 fn[fn.length] = 'n = getNodes(n, mode, "'+tokenMatch[2]+'");';
\r
1389 path = path.replace(tokenMatch[0], "");
\r
1390 }else if(path.substr(0, 1) != '@'){
\r
1391 fn[fn.length] = 'n = getNodes(n, mode, "*");';
\r
1393 // type of "simple"
\r
1396 if(tokenMatch[1] == "#"){
\r
1397 fn[fn.length] = 'n = byId(n, "'+tokenMatch[2]+'");';
\r
1399 fn[fn.length] = 'n = byTag(n, "'+tokenMatch[2]+'");';
\r
1401 path = path.replace(tokenMatch[0], "");
\r
1404 while(!(modeMatch = path.match(modeRe))){
\r
1405 var matched = false;
\r
1406 for(var j = 0; j < matchersLn; j++){
\r
1407 var t = matchers[j];
\r
1408 var m = path.match(t.re);
\r
1410 fn[fn.length] = t.select.replace(tplRe, function(x, i){
\r
1413 path = path.replace(m[0], "");
\r
1418 // prevent infinite loop on bad selector
\r
1420 throw 'Error parsing selector, parsing failed at "' + path + '"';
\r
1424 fn[fn.length] = 'mode="'+modeMatch[1].replace(trimRe, "")+'";';
\r
1425 path = path.replace(modeMatch[1], "");
\r
1429 fn[fn.length] = "return nodup(n);\n}";
\r
1431 // eval fn and return it
\r
1432 eval(fn.join(""));
\r
1437 * Selects a group of elements.
\r
1438 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
\r
1439 * @param {Node/String} root (optional) The start of the query (defaults to document).
\r
1440 * @return {Array} An Array of DOM elements which match the selector. If there are
\r
1441 * no matches, and empty Array is returned.
\r
1443 jsSelect: function(path, root, type){
\r
1444 // set root to doc if not specified.
\r
1445 root = root || document;
\r
1447 if(typeof root == "string"){
\r
1448 root = document.getElementById(root);
\r
1450 var paths = path.split(","),
\r
1453 // loop over each selector
\r
1454 for(var i = 0, len = paths.length; i < len; i++){
\r
1455 var subPath = paths[i].replace(trimRe, "");
\r
1456 // compile and place in cache
\r
1457 if(!cache[subPath]){
\r
1458 cache[subPath] = Ext.DomQuery.compile(subPath);
\r
1459 if(!cache[subPath]){
\r
1460 throw subPath + " is not a valid selector";
\r
1463 var result = cache[subPath](root);
\r
1464 if(result && result != document){
\r
1465 results = results.concat(result);
\r
1469 // if there were multiple selectors, make sure dups
\r
1471 if(paths.length > 1){
\r
1472 return nodup(results);
\r
1476 isXml: function(el) {
\r
1477 var docEl = (el ? el.ownerDocument || el : 0).documentElement;
\r
1478 return docEl ? docEl.nodeName !== "HTML" : false;
\r
1480 select : document.querySelectorAll ? function(path, root, type) {
\r
1481 root = root || document;
\r
1482 if (!Ext.DomQuery.isXml(root)) {
\r
1484 var cs = root.querySelectorAll(path);
\r
1485 return Ext.toArray(cs);
\r
1489 return Ext.DomQuery.jsSelect.call(this, path, root, type);
\r
1490 } : function(path, root, type) {
\r
1491 return Ext.DomQuery.jsSelect.call(this, path, root, type);
\r
1495 * Selects a single element.
\r
1496 * @param {String} selector The selector/xpath query
\r
1497 * @param {Node} root (optional) The start of the query (defaults to document).
\r
1498 * @return {Element} The DOM element which matched the selector.
\r
1500 selectNode : function(path, root){
\r
1501 return Ext.DomQuery.select(path, root)[0];
\r
1505 * Selects the value of a node, optionally replacing null with the defaultValue.
\r
1506 * @param {String} selector The selector/xpath query
\r
1507 * @param {Node} root (optional) The start of the query (defaults to document).
\r
1508 * @param {String} defaultValue
\r
1509 * @return {String}
\r
1511 selectValue : function(path, root, defaultValue){
\r
1512 path = path.replace(trimRe, "");
\r
1513 if(!valueCache[path]){
\r
1514 valueCache[path] = Ext.DomQuery.compile(path, "select");
\r
1516 var n = valueCache[path](root), v;
\r
1517 n = n[0] ? n[0] : n;
\r
1519 // overcome a limitation of maximum textnode size
\r
1520 // Rumored to potentially crash IE6 but has not been confirmed.
\r
1521 // http://reference.sitepoint.com/javascript/Node/normalize
\r
1522 // https://developer.mozilla.org/En/DOM/Node.normalize
\r
1523 if (typeof n.normalize == 'function') n.normalize();
\r
1525 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
\r
1526 return ((v === null||v === undefined||v==='') ? defaultValue : v);
\r
1530 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
\r
1531 * @param {String} selector The selector/xpath query
\r
1532 * @param {Node} root (optional) The start of the query (defaults to document).
\r
1533 * @param {Number} defaultValue
\r
1534 * @return {Number}
\r
1536 selectNumber : function(path, root, defaultValue){
\r
1537 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
\r
1538 return parseFloat(v);
\r
1542 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
\r
1543 * @param {String/HTMLElement/Array} el An element id, element or array of elements
\r
1544 * @param {String} selector The simple selector to test
\r
1545 * @return {Boolean}
\r
1547 is : function(el, ss){
\r
1548 if(typeof el == "string"){
\r
1549 el = document.getElementById(el);
\r
1551 var isArray = Ext.isArray(el),
\r
1552 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
\r
1553 return isArray ? (result.length == el.length) : (result.length > 0);
\r
1557 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
\r
1558 * @param {Array} el An array of elements to filter
\r
1559 * @param {String} selector The simple selector to test
\r
1560 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
\r
1561 * the selector instead of the ones that match
\r
1562 * @return {Array} An Array of DOM elements which match the selector. If there are
\r
1563 * no matches, and empty Array is returned.
\r
1565 filter : function(els, ss, nonMatches){
\r
1566 ss = ss.replace(trimRe, "");
\r
1567 if(!simpleCache[ss]){
\r
1568 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
\r
1570 var result = simpleCache[ss](els);
\r
1571 return nonMatches ? quickDiff(result, els) : result;
\r
1575 * Collection of matching regular expressions and code snippets.
\r
1576 * Each capture group within () will be replace the {} in the select
\r
1577 * statement as specified by their index.
\r
1580 re: /^\.([\w-]+)/,
\r
1581 select: 'n = byClassName(n, " {1} ");'
\r
1583 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
\r
1584 select: 'n = byPseudo(n, "{1}", "{2}");'
\r
1586 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
\r
1587 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
\r
1590 select: 'n = byId(n, "{1}");'
\r
1593 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
\r
1598 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
\r
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, > <.
\r
1602 "=" : function(a, v){
\r
1605 "!=" : function(a, v){
\r
1608 "^=" : function(a, v){
\r
1609 return a && a.substr(0, v.length) == v;
\r
1611 "$=" : function(a, v){
\r
1612 return a && a.substr(a.length-v.length) == v;
\r
1614 "*=" : function(a, v){
\r
1615 return a && a.indexOf(v) !== -1;
\r
1617 "%=" : function(a, v){
\r
1618 return (a % v) == 0;
\r
1620 "|=" : function(a, v){
\r
1621 return a && (a == v || a.substr(0, v.length+1) == v+'-');
\r
1623 "~=" : function(a, v){
\r
1624 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
\r
1629 * <p>Object hash of "pseudo class" filter functions which are used when filtering selections. Each function is passed
\r
1630 * two parameters:</p><div class="mdetail-params"><ul>
\r
1631 * <li><b>c</b> : Array<div class="sub-desc">An Array of DOM elements to filter.</div></li>
\r
1632 * <li><b>v</b> : String<div class="sub-desc">The argument (if any) supplied in the selector.</div></li>
\r
1634 * <p>A filter function returns an Array of DOM elements which conform to the pseudo class.</p>
\r
1635 * <p>In addition to the provided pseudo classes listed above such as <code>first-child</code> and <code>nth-child</code>,
\r
1636 * developers may add additional, custom psuedo class filters to select elements according to application-specific requirements.</p>
\r
1637 * <p>For example, to filter <code><a></code> elements to only return links to <i>external</i> resources:</p>
\r
1639 Ext.DomQuery.pseudos.external = function(c, v){
\r
1640 var r = [], ri = -1;
\r
1641 for(var i = 0, ci; ci = c[i]; i++){
\r
1642 // Include in result set only if it's a link to an external resource
\r
1643 if(ci.hostname != location.hostname){
\r
1649 * Then external links could be gathered with the following statement:<code><pre>
\r
1650 var externalLinks = Ext.select("a:external");
\r
1654 "first-child" : function(c){
\r
1655 var r = [], ri = -1, n;
\r
1656 for(var i = 0, ci; ci = n = c[i]; i++){
\r
1657 while((n = n.previousSibling) && n.nodeType != 1);
\r
1665 "last-child" : function(c){
\r
1666 var r = [], ri = -1, n;
\r
1667 for(var i = 0, ci; ci = n = c[i]; i++){
\r
1668 while((n = n.nextSibling) && n.nodeType != 1);
\r
1676 "nth-child" : function(c, a) {
\r
1677 var r = [], ri = -1,
\r
1678 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
\r
1679 f = (m[1] || 1) - 0, l = m[2] - 0;
\r
1680 for(var i = 0, n; n = c[i]; i++){
\r
1681 var pn = n.parentNode;
\r
1682 if (batch != pn._batch) {
\r
1684 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
\r
1685 if(cn.nodeType == 1){
\r
1686 cn.nodeIndex = ++j;
\r
1689 pn._batch = batch;
\r
1692 if (l == 0 || n.nodeIndex == l){
\r
1695 } else if ((n.nodeIndex + l) % f == 0){
\r
1703 "only-child" : function(c){
\r
1704 var r = [], ri = -1;;
\r
1705 for(var i = 0, ci; ci = c[i]; i++){
\r
1706 if(!prev(ci) && !next(ci)){
\r
1713 "empty" : function(c){
\r
1714 var r = [], ri = -1;
\r
1715 for(var i = 0, ci; ci = c[i]; i++){
\r
1716 var cns = ci.childNodes, j = 0, cn, empty = true;
\r
1717 while(cn = cns[j]){
\r
1719 if(cn.nodeType == 1 || cn.nodeType == 3){
\r
1731 "contains" : function(c, v){
\r
1732 var r = [], ri = -1;
\r
1733 for(var i = 0, ci; ci = c[i]; i++){
\r
1734 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
\r
1741 "nodeValue" : function(c, v){
\r
1742 var r = [], ri = -1;
\r
1743 for(var i = 0, ci; ci = c[i]; i++){
\r
1744 if(ci.firstChild && ci.firstChild.nodeValue == v){
\r
1751 "checked" : function(c){
\r
1752 var r = [], ri = -1;
\r
1753 for(var i = 0, ci; ci = c[i]; i++){
\r
1754 if(ci.checked == true){
\r
1761 "not" : function(c, ss){
\r
1762 return Ext.DomQuery.filter(c, ss, true);
\r
1765 "any" : function(c, selectors){
\r
1766 var ss = selectors.split('|'),
\r
1767 r = [], ri = -1, s;
\r
1768 for(var i = 0, ci; ci = c[i]; i++){
\r
1769 for(var j = 0; s = ss[j]; j++){
\r
1770 if(Ext.DomQuery.is(ci, s)){
\r
1779 "odd" : function(c){
\r
1780 return this["nth-child"](c, "odd");
\r
1783 "even" : function(c){
\r
1784 return this["nth-child"](c, "even");
\r
1787 "nth" : function(c, a){
\r
1788 return c[a-1] || [];
\r
1791 "first" : function(c){
\r
1792 return c[0] || [];
\r
1795 "last" : function(c){
\r
1796 return c[c.length-1] || [];
\r
1799 "has" : function(c, ss){
\r
1800 var s = Ext.DomQuery.select,
\r
1802 for(var i = 0, ci; ci = c[i]; i++){
\r
1803 if(s(ss, ci).length > 0){
\r
1810 "next" : function(c, ss){
\r
1811 var is = Ext.DomQuery.is,
\r
1813 for(var i = 0, ci; ci = c[i]; i++){
\r
1815 if(n && is(n, ss)){
\r
1822 "prev" : function(c, ss){
\r
1823 var is = Ext.DomQuery.is,
\r
1825 for(var i = 0, ci; ci = c[i]; i++){
\r
1827 if(n && is(n, ss)){
\r
1838 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
\r
1839 * @param {String} path The selector/xpath query
\r
1840 * @param {Node} root (optional) The start of the query (defaults to document).
\r
1845 Ext.query = Ext.DomQuery.select;
\r
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(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];
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,
2353 if(l.fn == fn && (s == scope || s == this.obj)){
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
\r
2427 Ext.apply(Ext.util.Observable.prototype, function(){
\r
2428 // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
\r
2429 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
\r
2431 function getMethodEvent(method){
\r
2432 var e = (this.methodEvents = this.methodEvents ||
\r
2433 {})[method], returnValue, v, cancel, obj = this;
\r
2436 this.methodEvents[method] = e = {};
\r
2437 e.originalFn = this[method];
\r
2438 e.methodName = method;
\r
2442 var makeCall = function(fn, scope, args){
\r
2443 if (!Ext.isEmpty(v = fn.apply(scope || obj, args))) {
\r
2444 if (Ext.isObject(v)) {
\r
2445 returnValue = !Ext.isEmpty(v.returnValue) ? v.returnValue : v;
\r
2446 cancel = !!v.cancel;
\r
2449 if (v === false) {
\r
2458 this[method] = function(){
\r
2459 var args = Ext.toArray(arguments);
\r
2460 returnValue = v = undefined;
\r
2463 Ext.each(e.before, function(b){
\r
2464 makeCall(b.fn, b.scope, args);
\r
2466 return returnValue;
\r
2470 if (!Ext.isEmpty(v = e.originalFn.apply(obj, args))) {
\r
2473 Ext.each(e.after, function(a){
\r
2474 makeCall(a.fn, a.scope, args);
\r
2476 return returnValue;
\r
2479 return returnValue;
\r
2486 // these are considered experimental
\r
2487 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
\r
2488 // adds an 'interceptor' called before the original method
\r
2489 beforeMethod : function(method, fn, scope){
\r
2490 getMethodEvent.call(this, method).before.push({
\r
2496 // adds a 'sequence' called after the original method
\r
2497 afterMethod : function(method, fn, scope){
\r
2498 getMethodEvent.call(this, method).after.push({
\r
2504 removeMethodListener: function(method, fn, scope){
\r
2505 var e = getMethodEvent.call(this, method), found = false;
\r
2506 Ext.each(e.before, function(b, i, arr){
\r
2507 if (b.fn == fn && b.scope == scope) {
\r
2514 Ext.each(e.after, function(a, i, arr){
\r
2515 if (a.fn == fn && a.scope == scope) {
\r
2524 * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
\r
2525 * @param {Object} o The Observable whose events this object is to relay.
\r
2526 * @param {Array} events Array of event names to relay.
\r
2528 relayEvents : function(o, events){
\r
2530 function createHandler(ename){
\r
2531 return function(){
\r
2532 return me.fireEvent.apply(me, [ename].concat(Ext.toArray(arguments)));
\r
2535 Ext.each(events, function(ename){
\r
2536 me.events[ename] = me.events[ename] || true;
\r
2537 o.on(ename, createHandler(ename), me);
\r
2542 * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
\r
2543 * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
\r
2544 * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default
\r
2545 * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
\r
2546 * access the required target more quickly.</p>
\r
2547 * <p>Example:</p><pre><code>
\r
2548 Ext.override(Ext.form.Field, {
\r
2549 // Add functionality to Field's initComponent to enable the change event to bubble
\r
2550 initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
\r
2551 this.enableBubble('change');
\r
2554 // We know that we want Field's events to bubble directly to the FormPanel.
\r
2555 getBubbleTarget : function() {
\r
2556 if (!this.formPanel) {
\r
2557 this.formPanel = this.findParentByType('form');
\r
2559 return this.formPanel;
\r
2563 var myForm = new Ext.formPanel({
\r
2564 title: 'User Details',
\r
2569 change: function() {
\r
2570 // Title goes red if form has been modified.
\r
2571 myForm.header.setStyle('color', 'red');
\r
2576 * @param {String/Array} events The event name to bubble, or an Array of event names.
\r
2578 enableBubble : function(events){
\r
2580 if(!Ext.isEmpty(events)){
\r
2581 events = Ext.isArray(events) ? events : Ext.toArray(arguments);
\r
2582 Ext.each(events, function(ename){
\r
2583 ename = ename.toLowerCase();
\r
2584 var ce = me.events[ename] || true;
\r
2585 if (Ext.isBoolean(ce)) {
\r
2586 ce = new Ext.util.Event(me, ename);
\r
2587 me.events[ename] = ce;
\r
2598 * Starts capture on the specified Observable. All events will be passed
\r
2599 * to the supplied function with the event name + standard signature of the event
\r
2600 * <b>before</b> the event is fired. If the supplied function returns false,
\r
2601 * the event will not fire.
\r
2602 * @param {Observable} o The Observable to capture events from.
\r
2603 * @param {Function} fn The function to call when an event is fired.
\r
2604 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
\r
2607 Ext.util.Observable.capture = function(o, fn, scope){
\r
2608 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
\r
2613 * Sets observability on the passed class constructor.<p>
\r
2614 * <p>This makes any event fired on any instance of the passed class also fire a single event through
\r
2615 * the <i>class</i> allowing for central handling of events on many instances at once.</p>
\r
2616 * <p>Usage:</p><pre><code>
\r
2617 Ext.util.Observable.observeClass(Ext.data.Connection);
\r
2618 Ext.data.Connection.on('beforerequest', function(con, options) {
\r
2619 console.log('Ajax request made to ' + options.url);
\r
2621 * @param {Function} c The class constructor to make observable.
\r
2622 * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
\r
2625 Ext.util.Observable.observeClass = function(c, listeners){
\r
2628 Ext.apply(c, new Ext.util.Observable());
\r
2629 Ext.util.Observable.capture(c.prototype, c.fireEvent, c);
\r
2631 if(Ext.isObject(listeners)){
\r
2637 * @class Ext.EventManager
\r
2638 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
\r
2639 * several useful events directly.
\r
2640 * See {@link Ext.EventObject} for more details on normalized event objects.
\r
2643 Ext.EventManager = function(){
\r
2644 var docReadyEvent,
\r
2646 docReadyState = false,
\r
2647 E = Ext.lib.Event,
\r
2651 IEDEFERED = "ie-deferred-loader",
\r
2652 DOMCONTENTLOADED = "DOMContentLoaded",
\r
2653 propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
\r
2655 * This cache is used to hold special js objects, the document and window, that don't have an id. We need to keep
\r
2656 * a reference to them so we can look them up at a later point.
\r
2658 specialElCache = [];
\r
2660 function getId(el){
\r
2663 len = specialElCache.length,
\r
2668 if(el.getElementById || el.navigator){
\r
2670 for(; i < len; ++i){
\r
2671 o = specialElCache[i];
\r
2678 // for browsers that support it, ensure that give the el the same id
\r
2680 specialElCache.push({
\r
2689 if(!Ext.elCache[id]){
\r
2690 Ext.Element.addToCache(new Ext.Element(el), id);
\r
2692 Ext.elCache[id].skipGC = true;
\r
2699 /// There is some jquery work around stuff here that isn't needed in Ext Core.
\r
2700 function addListener(el, ename, fn, task, wrap, scope){
\r
2701 el = Ext.getDom(el);
\r
2702 var id = getId(el),
\r
2703 es = Ext.elCache[id].events,
\r
2706 wfn = E.on(el, ename, wrap);
\r
2707 es[ename] = es[ename] || [];
\r
2709 /* 0 = Original Function,
\r
2710 1 = Event Manager Wrapped Function,
\r
2712 3 = Adapter Wrapped Function,
\r
2715 es[ename].push([fn, wrap, scope, wfn, task]);
\r
2717 // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
\r
2718 // without breaking ExtJS.
\r
2720 // workaround for jQuery
\r
2721 if(el.addEventListener && ename == "mousewheel"){
\r
2722 var args = ["DOMMouseScroll", wrap, false];
\r
2723 el.addEventListener.apply(el, args);
\r
2724 Ext.EventManager.addListener(WINDOW, 'unload', function(){
\r
2725 el.removeEventListener.apply(el, args);
\r
2729 // fix stopped mousedowns on the document
\r
2730 if(el == DOC && ename == "mousedown"){
\r
2731 Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
\r
2735 function fireDocReady(){
\r
2736 if(!docReadyState){
\r
2737 Ext.isReady = docReadyState = true;
\r
2738 if(docReadyProcId){
\r
2739 clearInterval(docReadyProcId);
\r
2741 if(Ext.isGecko || Ext.isOpera) {
\r
2742 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
\r
2745 var defer = DOC.getElementById(IEDEFERED);
\r
2747 defer.onreadystatechange = null;
\r
2748 defer.parentNode.removeChild(defer);
\r
2751 if(docReadyEvent){
\r
2752 docReadyEvent.fire();
\r
2753 docReadyEvent.listeners = []; // clearListeners no longer compatible. Force single: true?
\r
2758 function initDocReady(){
\r
2759 var COMPLETE = "complete";
\r
2761 docReadyEvent = new Ext.util.Event();
\r
2762 if (Ext.isGecko || Ext.isOpera) {
\r
2763 DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
\r
2764 } else if (Ext.isIE){
\r
2765 DOC.write("<s"+'cript id=' + IEDEFERED + ' defer="defer" src="/'+'/:"></s'+"cript>");
\r
2766 DOC.getElementById(IEDEFERED).onreadystatechange = function(){
\r
2767 if(this.readyState == COMPLETE){
\r
2771 } else if (Ext.isWebKit){
\r
2772 docReadyProcId = setInterval(function(){
\r
2773 if(DOC.readyState == COMPLETE) {
\r
2778 // no matter what, make sure it fires on load
\r
2779 E.on(WINDOW, "load", fireDocReady);
\r
2782 function createTargeted(h, o){
\r
2783 return function(){
\r
2784 var args = Ext.toArray(arguments);
\r
2785 if(o.target == Ext.EventObject.setEvent(args[0]).target){
\r
2786 h.apply(this, args);
\r
2791 function createBuffered(h, o, task){
\r
2792 return function(e){
\r
2793 // create new event object impl so new events don't wipe out properties
\r
2794 task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
\r
2798 function createSingle(h, el, ename, fn, scope){
\r
2799 return function(e){
\r
2800 Ext.EventManager.removeListener(el, ename, fn, scope);
\r
2805 function createDelayed(h, o, fn){
\r
2806 return function(e){
\r
2807 var task = new Ext.util.DelayedTask(h);
\r
2811 fn.tasks.push(task);
\r
2812 task.delay(o.delay || 10, h, null, [new Ext.EventObjectImpl(e)]);
\r
2816 function listen(element, ename, opt, fn, scope){
\r
2817 var o = !Ext.isObject(opt) ? {} : opt,
\r
2818 el = Ext.getDom(element), task;
\r
2821 scope = scope || o.scope;
\r
2824 throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
\r
2827 // prevent errors while unload occurring
\r
2828 if(!Ext){// !window[xname]){ ==> can't we do this?
\r
2831 e = Ext.EventObject.setEvent(e);
\r
2834 if(!(t = e.getTarget(o.delegate, el))){
\r
2840 if (o.stopEvent) {
\r
2843 if (o.preventDefault) {
\r
2844 e.preventDefault();
\r
2846 if (o.stopPropagation) {
\r
2847 e.stopPropagation();
\r
2849 if (o.normalized) {
\r
2850 e = e.browserEvent;
\r
2853 fn.call(scope || el, e, t, o);
\r
2856 h = createTargeted(h, o);
\r
2859 h = createDelayed(h, o, fn);
\r
2862 h = createSingle(h, el, ename, fn, scope);
\r
2865 task = new Ext.util.DelayedTask(h);
\r
2866 h = createBuffered(h, o, task);
\r
2869 addListener(el, ename, fn, task, h, scope);
\r
2875 * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
\r
2876 * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
\r
2877 * @param {String/HTMLElement} el The html element or id to assign the event handler to.
\r
2878 * @param {String} eventName The name of the event to listen for.
\r
2879 * @param {Function} handler The handler function the event invokes. This function is passed
\r
2880 * the following parameters:<ul>
\r
2881 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
\r
2882 * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
\r
2883 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
\r
2884 * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
\r
2886 * @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>.
\r
2887 * @param {Object} options (optional) An object containing handler configuration properties.
\r
2888 * This may contain any of the following properties:<ul>
\r
2889 * <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>
\r
2890 * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
\r
2891 * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
\r
2892 * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
\r
2893 * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
\r
2894 * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
\r
2895 * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
\r
2896 * <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>
\r
2897 * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
\r
2898 * by the specified number of milliseconds. If the event fires again within that time, the original
\r
2899 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
\r
2900 * <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>
\r
2902 * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
\r
2904 addListener : function(element, eventName, fn, scope, options){
\r
2905 if(Ext.isObject(eventName)){
\r
2906 var o = eventName, e, val;
\r
2909 if(!propRe.test(e)){
\r
2910 if(Ext.isFunction(val)){
\r
2912 listen(element, e, o, val, o.scope);
\r
2914 // individual options
\r
2915 listen(element, e, val);
\r
2920 listen(element, eventName, options, fn, scope);
\r
2925 * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
\r
2926 * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
\r
2927 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
\r
2928 * @param {String} eventName The name of the event.
\r
2929 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
\r
2930 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
\r
2931 * then this must refer to the same object.
\r
2933 removeListener : function(el, eventName, fn, scope){
\r
2934 el = Ext.getDom(el);
\r
2935 var id = getId(el),
\r
2936 f = el && (Ext.elCache[id].events)[eventName] || [],
\r
2937 wrap, i, l, k, len, fnc;
\r
2939 for (i = 0, len = f.length; i < len; i++) {
\r
2941 /* 0 = Original Function,
\r
2942 1 = Event Manager Wrapped Function,
\r
2944 3 = Adapter Wrapped Function,
\r
2947 if (Ext.isArray(fnc = f[i]) && fnc[0] == fn && (!scope || fnc[2] == scope)) {
\r
2951 k = fn.tasks && fn.tasks.length;
\r
2954 fn.tasks[k].cancel();
\r
2959 E.un(el, eventName, E.extAdapter ? fnc[3] : wrap);
\r
2961 // jQuery workaround that should be removed from Ext Core
\r
2962 if(wrap && el.addEventListener && eventName == "mousewheel"){
\r
2963 el.removeEventListener("DOMMouseScroll", wrap, false);
\r
2966 // fix stopped mousedowns on the document
\r
2967 if(wrap && el == DOC && eventName == "mousedown"){
\r
2968 Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
\r
2972 if (f.length === 0) {
\r
2973 delete Ext.elCache[id].events[eventName];
\r
2975 for (k in Ext.elCache[id].events) {
\r
2978 Ext.elCache[id].events = {};
\r
2985 * Removes all event handers from an element. Typically you will use {@link Ext.Element#removeAllListeners}
\r
2986 * directly on an Element in favor of calling this version.
\r
2987 * @param {String/HTMLElement} el The id or html element from which to remove all event handlers.
\r
2989 removeAll : function(el){
\r
2990 el = Ext.getDom(el);
\r
2991 var id = getId(el),
\r
2992 ec = Ext.elCache[id] || {},
\r
2993 es = ec.events || {},
\r
2994 f, i, len, ename, fn, k, wrap;
\r
2997 if(es.hasOwnProperty(ename)){
\r
2999 /* 0 = Original Function,
\r
3000 1 = Event Manager Wrapped Function,
\r
3002 3 = Adapter Wrapped Function,
\r
3005 for (i = 0, len = f.length; i < len; i++) {
\r
3010 if(fn[0].tasks && (k = fn[0].tasks.length)) {
\r
3012 fn[0].tasks[k].cancel();
\r
3017 E.un(el, ename, E.extAdapter ? fn[3] : wrap);
\r
3019 // jQuery workaround that should be removed from Ext Core
\r
3020 if(el.addEventListener && wrap && ename == "mousewheel"){
\r
3021 el.removeEventListener("DOMMouseScroll", wrap, false);
\r
3024 // fix stopped mousedowns on the document
\r
3025 if(wrap && el == DOC && ename == "mousedown"){
\r
3026 Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
\r
3031 if (Ext.elCache[id]) {
\r
3032 Ext.elCache[id].events = {};
\r
3036 getListeners : function(el, eventName) {
\r
3037 el = Ext.getDom(el);
\r
3038 var id = getId(el),
\r
3039 ec = Ext.elCache[id] || {},
\r
3040 es = ec.events || {},
\r
3042 if (es && es[eventName]) {
\r
3043 return es[eventName];
\r
3049 purgeElement : function(el, recurse, eventName) {
\r
3050 el = Ext.getDom(el);
\r
3051 var id = getId(el),
\r
3052 ec = Ext.elCache[id] || {},
\r
3053 es = ec.events || {},
\r
3056 if (es && es.hasOwnProperty(eventName)) {
\r
3057 f = es[eventName];
\r
3058 for (i = 0, len = f.length; i < len; i++) {
\r
3059 Ext.EventManager.removeListener(el, eventName, f[i][0]);
\r
3063 Ext.EventManager.removeAll(el);
\r
3065 if (recurse && el && el.childNodes) {
\r
3066 for (i = 0, len = el.childNodes.length; i < len; i++) {
\r
3067 Ext.EventManager.purgeElement(el.childNodes[i], recurse, eventName);
\r
3072 _unload : function() {
\r
3074 for (el in Ext.elCache) {
\r
3075 Ext.EventManager.removeAll(el);
\r
3077 delete Ext.elCache;
\r
3078 delete Ext.Element._flyweights;
\r
3081 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Can be
\r
3082 * accessed shorthanded as Ext.onReady().
\r
3083 * @param {Function} fn The method the event invokes.
\r
3084 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
\r
3085 * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
\r
3086 * <code>{single: true}</code> be used so that the handler is removed on first invocation.
\r
3088 onDocumentReady : function(fn, scope, options){
\r
3089 if(docReadyState){ // if it already fired
\r
3090 docReadyEvent.addListener(fn, scope, options);
\r
3091 docReadyEvent.fire();
\r
3092 docReadyEvent.listeners = []; // clearListeners no longer compatible. Force single: true?
\r
3094 if(!docReadyEvent) initDocReady();
\r
3095 options = options || {};
\r
3096 options.delay = options.delay || 1;
\r
3097 docReadyEvent.addListener(fn, scope, options);
\r
3102 * Appends an event handler to an element. Shorthand for {@link #addListener}.
\r
3103 * @param {String/HTMLElement} el The html element or id to assign the event handler to
\r
3104 * @param {String} eventName The name of the event to listen for.
\r
3105 * @param {Function} handler The handler function the event invokes.
\r
3106 * @param {Object} scope (optional) (<code>this</code> reference) in which the handler function executes. <b>Defaults to the Element</b>.
\r
3107 * @param {Object} options (optional) An object containing standard {@link #addListener} options
\r
3108 * @member Ext.EventManager
\r
3111 pub.on = pub.addListener;
\r
3113 * Removes an event handler from an element. Shorthand for {@link #removeListener}.
\r
3114 * @param {String/HTMLElement} el The id or html element from which to remove the listener.
\r
3115 * @param {String} eventName The name of the event.
\r
3116 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #on} call.</b>
\r
3117 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
\r
3118 * then this must refer to the same object.
\r
3119 * @member Ext.EventManager
\r
3122 pub.un = pub.removeListener;
\r
3124 pub.stoppedMouseDownEvent = new Ext.util.Event();
\r
3128 * Adds a listener to be notified when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
\r
3129 * @param {Function} fn The method the event invokes.
\r
3130 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
\r
3131 * @param {boolean} options (optional) Options object as passed to {@link Ext.Element#addListener}. It is recommended that the options
\r
3132 * <code>{single: true}</code> be used so that the handler is removed on first invocation.
\r
3136 Ext.onReady = Ext.EventManager.onDocumentReady;
\r
3139 //Initialize doc classes
\r
3142 var initExtCss = function(){
\r
3143 // find the body element
\r
3144 var bd = document.body || document.getElementsByTagName('body')[0];
\r
3145 if(!bd){ return false; }
\r
3147 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
\r
3148 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
\r
3149 : Ext.isOpera ? "ext-opera"
\r
3150 : Ext.isWebKit ? "ext-webkit" : ""];
\r
3153 cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
\r
3154 }else if(Ext.isChrome){
\r
3155 cls.push("ext-chrome");
\r
3159 cls.push("ext-mac");
\r
3162 cls.push("ext-linux");
\r
3165 if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
\r
3166 var p = bd.parentNode;
\r
3168 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';
\r
3171 bd.className += cls.join(' ');
\r
3175 if(!initExtCss()){
\r
3176 Ext.onReady(initExtCss);
\r
3182 * @class Ext.EventObject
\r
3183 * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
\r
3184 * wraps the browser's native event-object normalizing cross-browser differences,
\r
3185 * such as which mouse button is clicked, keys pressed, mechanisms to stop
\r
3186 * event-propagation along with a method to prevent default actions from taking place.
\r
3187 * <p>For example:</p>
\r
3189 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
\r
3190 e.preventDefault();
\r
3191 var target = e.getTarget(); // same as t (the target HTMLElement)
\r
3194 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.Element}
\r
3195 myDiv.on( // 'on' is shorthand for addListener
\r
3196 "click", // perform an action on click of myDiv
\r
3197 handleClick // reference to the action handler
\r
3199 // other methods to do the same:
\r
3200 Ext.EventManager.on("myDiv", 'click', handleClick);
\r
3201 Ext.EventManager.addListener("myDiv", 'click', handleClick);
\r
3205 Ext.EventObject = function(){
\r
3206 var E = Ext.lib.Event,
\r
3207 // safari keypress events for special keys return bad keycodes
\r
3210 63234 : 37, // left
\r
3211 63235 : 39, // right
\r
3213 63233 : 40, // down
\r
3214 63276 : 33, // page up
\r
3215 63277 : 34, // page down
\r
3216 63272 : 46, // delete
\r
3217 63273 : 36, // home
\r
3220 // normalize button clicks
\r
3221 btnMap = Ext.isIE ? {1:0,4:1,2:2} :
\r
3222 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
\r
3224 Ext.EventObjectImpl = function(e){
\r
3226 this.setEvent(e.browserEvent || e);
\r
3230 Ext.EventObjectImpl.prototype = {
\r
3232 setEvent : function(e){
\r
3234 if(e == me || (e && e.browserEvent)){ // already wrapped
\r
3237 me.browserEvent = e;
\r
3239 // normalize buttons
\r
3240 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
\r
3241 if(e.type == 'click' && me.button == -1){
\r
3245 me.shiftKey = e.shiftKey;
\r
3246 // mac metaKey behaves like ctrlKey
\r
3247 me.ctrlKey = e.ctrlKey || e.metaKey || false;
\r
3248 me.altKey = e.altKey;
\r
3249 // in getKey these will be normalized for the mac
\r
3250 me.keyCode = e.keyCode;
\r
3251 me.charCode = e.charCode;
\r
3252 // cache the target for the delayed and or buffered events
\r
3253 me.target = E.getTarget(e);
\r
3255 me.xy = E.getXY(e);
\r
3258 me.shiftKey = false;
\r
3259 me.ctrlKey = false;
\r
3260 me.altKey = false;
\r
3270 * Stop the event (preventDefault and stopPropagation)
\r
3272 stopEvent : function(){
\r
3274 if(me.browserEvent){
\r
3275 if(me.browserEvent.type == 'mousedown'){
\r
3276 Ext.EventManager.stoppedMouseDownEvent.fire(me);
\r
3278 E.stopEvent(me.browserEvent);
\r
3283 * Prevents the browsers default handling of the event.
\r
3285 preventDefault : function(){
\r
3286 if(this.browserEvent){
\r
3287 E.preventDefault(this.browserEvent);
\r
3292 * Cancels bubbling of the event.
\r
3294 stopPropagation : function(){
\r
3296 if(me.browserEvent){
\r
3297 if(me.browserEvent.type == 'mousedown'){
\r
3298 Ext.EventManager.stoppedMouseDownEvent.fire(me);
\r
3300 E.stopPropagation(me.browserEvent);
\r
3305 * Gets the character code for the event.
\r
3306 * @return {Number}
\r
3308 getCharCode : function(){
\r
3309 return this.charCode || this.keyCode;
\r
3313 * Returns a normalized keyCode for the event.
\r
3314 * @return {Number} The key code
\r
3316 getKey : function(){
\r
3317 return this.normalizeKey(this.keyCode || this.charCode)
\r
3321 normalizeKey: function(k){
\r
3322 return Ext.isSafari ? (safariKeys[k] || k) : k;
\r
3326 * Gets the x coordinate of the event.
\r
3327 * @return {Number}
\r
3329 getPageX : function(){
\r
3330 return this.xy[0];
\r
3334 * Gets the y coordinate of the event.
\r
3335 * @return {Number}
\r
3337 getPageY : function(){
\r
3338 return this.xy[1];
\r
3342 * Gets the page coordinates of the event.
\r
3343 * @return {Array} The xy values like [x, y]
\r
3345 getXY : function(){
\r
3350 * Gets the target for the event.
\r
3351 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
\r
3352 * @param {Number/Mixed} maxDepth (optional) The max depth to
\r
3353 search as a number or element (defaults to 10 || document.body)
\r
3354 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
\r
3355 * @return {HTMLelement}
\r
3357 getTarget : function(selector, maxDepth, returnEl){
\r
3358 return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
\r
3362 * Gets the related target.
\r
3363 * @return {HTMLElement}
\r
3365 getRelatedTarget : function(){
\r
3366 return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
\r
3370 * Normalizes mouse wheel delta across browsers
\r
3371 * @return {Number} The delta
\r
3373 getWheelDelta : function(){
\r
3374 var e = this.browserEvent;
\r
3376 if(e.wheelDelta){ /* IE/Opera. */
\r
3377 delta = e.wheelDelta/120;
\r
3378 }else if(e.detail){ /* Mozilla case. */
\r
3379 delta = -e.detail/3;
\r
3385 * 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.
\r
3386 * Example usage:<pre><code>
\r
3387 // Handle click on any child of an element
\r
3388 Ext.getBody().on('click', function(e){
\r
3389 if(e.within('some-el')){
\r
3390 alert('Clicked on a child of some-el!');
\r
3394 // Handle click directly on an element, ignoring clicks on child nodes
\r
3395 Ext.getBody().on('click', function(e,t){
\r
3396 if((t.id == 'some-el') && !e.within(t, true)){
\r
3397 alert('Clicked directly on some-el!');
\r
3401 * @param {Mixed} el The id, DOM element or Ext.Element to check
\r
3402 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
\r
3403 * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
\r
3404 * @return {Boolean}
\r
3406 within : function(el, related, allowEl){
\r
3408 var t = this[related ? "getRelatedTarget" : "getTarget"]();
\r
3409 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
\r
3415 return new Ext.EventObjectImpl();
\r
3418 * @class Ext.EventManager
3420 Ext.apply(Ext.EventManager, function(){
3426 propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
3429 // note 1: IE fires ONLY the keydown event on specialkey autorepeat
3430 // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
3431 // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
3432 useKeydown = Ext.isWebKit ?
3433 Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
3434 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
3438 doResizeEvent: function(){
3439 var h = D.getViewHeight(),
3440 w = D.getViewWidth();
3442 //whacky problem in IE where the resize event will fire even though the w/h are the same.
3443 if(curHeight != h || curWidth != w){
3444 resizeEvent.fire(curWidth = w, curHeight = h);
3449 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
3450 * passes new viewport width and height to handlers.
3451 * @param {Function} fn The handler function the window resize event invokes.
3452 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3453 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
3455 onWindowResize : function(fn, scope, options){
3457 resizeEvent = new Ext.util.Event();
3458 resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
3459 Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
3461 resizeEvent.addListener(fn, scope, options);
3464 // exposed only to allow manual firing
3465 fireWindowResize : function(){
3467 resizeTask.delay(100);
3472 * 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.
3473 * @param {Function} fn The function the event invokes.
3474 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
3475 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
3477 onTextResize : function(fn, scope, options){
3479 textEvent = new Ext.util.Event();
3480 var textEl = new Ext.Element(document.createElement('div'));
3481 textEl.dom.className = 'x-text-resize';
3482 textEl.dom.innerHTML = 'X';
3483 textEl.appendTo(document.body);
3484 textSize = textEl.dom.offsetHeight;
3485 setInterval(function(){
3486 if(textEl.dom.offsetHeight != textSize){
3487 textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
3489 }, this.textResizeInterval);
3491 textEvent.addListener(fn, scope, options);
3495 * Removes the passed window resize listener.
3496 * @param {Function} fn The method the event invokes
3497 * @param {Object} scope The scope of handler
3499 removeResizeListener : function(fn, scope){
3501 resizeEvent.removeListener(fn, scope);
3506 fireResize : function(){
3508 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
3513 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
3515 textResizeInterval : 50,
3518 * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
3522 // protected for use inside the framework
3523 // detects whether we should use keydown or keypress based on the browser.
3524 useKeydown: useKeydown
3528 Ext.EventManager.on = Ext.EventManager.addListener;
3531 Ext.apply(Ext.EventObjectImpl.prototype, {
3532 /** Key constant @type Number */
3534 /** Key constant @type Number */
3536 /** Key constant @type Number */
3538 /** Key constant @type Number */
3540 /** Key constant @type Number */
3542 /** Key constant @type Number */
3544 /** Key constant @type Number */
3546 CONTROL : 17, // legacy
3547 /** Key constant @type Number */
3549 /** Key constant @type Number */
3551 /** Key constant @type Number */
3553 /** Key constant @type Number */
3555 /** Key constant @type Number */
3557 /** Key constant @type Number */
3559 PAGEUP : 33, // legacy
3560 /** Key constant @type Number */
3562 PAGEDOWN : 34, // legacy
3563 /** Key constant @type Number */
3565 /** Key constant @type Number */
3567 /** Key constant @type Number */
3569 /** Key constant @type Number */
3571 /** Key constant @type Number */
3573 /** Key constant @type Number */
3575 /** Key constant @type Number */
3577 /** Key constant @type Number */
3579 /** Key constant @type Number */
3581 /** Key constant @type Number */
3583 /** Key constant @type Number */
3585 /** Key constant @type Number */
3587 /** Key constant @type Number */
3589 /** Key constant @type Number */
3591 /** Key constant @type Number */
3593 /** Key constant @type Number */
3595 /** Key constant @type Number */
3597 /** Key constant @type Number */
3599 /** Key constant @type Number */
3601 /** Key constant @type Number */
3603 /** Key constant @type Number */
3605 /** Key constant @type Number */
3607 /** Key constant @type Number */
3609 /** Key constant @type Number */
3611 /** Key constant @type Number */
3613 /** Key constant @type Number */
3615 /** Key constant @type Number */
3617 /** Key constant @type Number */
3619 /** Key constant @type Number */
3621 /** Key constant @type Number */
3623 /** Key constant @type Number */
3625 /** Key constant @type Number */
3627 /** Key constant @type Number */
3629 /** Key constant @type Number */
3631 /** Key constant @type Number */
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 /** Key constant @type Number */
3647 /** Key constant @type Number */
3649 /** Key constant @type Number */
3651 /** Key constant @type Number */
3653 /** Key constant @type Number */
3655 /** Key constant @type Number */
3657 /** Key constant @type Number */
3659 /** 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 */
3711 isNavKeyPress : function(){
3713 k = this.normalizeKey(me.keyCode);
3714 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
3720 isSpecialKey : function(){
3721 var k = this.normalizeKey(this.keyCode);
3722 return (this.type == 'keypress' && this.ctrlKey) ||
3723 this.isNavKeyPress() ||
3724 (k == this.BACKSPACE) || // Backspace
3725 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
3726 (k >= 44 && k <= 45); // Print Screen, Insert
3729 getPoint : function(){
3730 return new Ext.lib.Point(this.xy[0], this.xy[1]);
3734 * Returns true if the control, meta, shift or alt key was pressed during this event.
3737 hasModifier : function(){
3738 return ((this.ctrlKey || this.altKey) || this.shiftKey);
3741 * @class Ext.Element
3742 * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
3743 * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
3744 * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
3745 * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
3746 * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
3750 var el = Ext.get("my-div");
3752 // by DOM element reference
3753 var el = Ext.get(myDivElement);
3755 * <b>Animations</b><br />
3756 * <p>When an element is manipulated, by default there is no animation.</p>
3758 var el = Ext.get("my-div");
3763 * <p>Many of the functions for manipulating an element have an optional "animate" parameter. This
3764 * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
3766 // default animation
3767 el.setWidth(100, true);
3770 * <p>To configure the effects, an object literal with animation options to use as the Element animation
3771 * configuration object can also be specified. Note that the supported Element animation configuration
3772 * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects. The supported
3773 * Element animation configuration options are:</p>
3775 Option Default Description
3776 --------- -------- ---------------------------------------------
3777 {@link Ext.Fx#duration duration} .35 The duration of the animation in seconds
3778 {@link Ext.Fx#easing easing} easeOut The easing method
3779 {@link Ext.Fx#callback callback} none A function to execute when the anim completes
3780 {@link Ext.Fx#scope scope} this The scope (this) of the callback function
3784 // Element animation options object
3786 {@link Ext.Fx#duration duration}: 1,
3787 {@link Ext.Fx#easing easing}: 'elasticIn',
3788 {@link Ext.Fx#callback callback}: this.foo,
3789 {@link Ext.Fx#scope scope}: this
3791 // animation with some options set
3792 el.setWidth(100, opt);
3794 * <p>The Element animation object being used for the animation will be set on the options
3795 * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
3797 // using the "anim" property to get the Anim object
3798 if(opt.anim.isAnimated()){
3802 * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
3803 * <p><b> Composite (Collections of) Elements</b></p>
3804 * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
3805 * @constructor Create a new Element directly.
3806 * @param {String/HTMLElement} element
3807 * @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).
3812 Ext.Element = function(element, forceNew){
3813 var dom = typeof element == "string" ?
3814 DOC.getElementById(element) : element,
3817 if(!dom) return null;
3821 if(!forceNew && id && Ext.elCache[id]){ // element object already exists
3822 return Ext.elCache[id].el;
3832 * The DOM element ID
3835 this.id = id || Ext.id(dom);
3838 var D = Ext.lib.Dom,
3847 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
3848 * @param {Object} o The object with the attributes
3849 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
3850 * @return {Ext.Element} this
3852 set : function(o, useSet){
3856 useSet = (useSet !== false) && !!el.setAttribute;
3859 if (o.hasOwnProperty(attr)) {
3861 if (attr == 'style') {
3862 DH.applyStyles(el, val);
3863 } else if (attr == 'cls') {
3865 } else if (useSet) {
3866 el.setAttribute(attr, val);
3878 * Fires when a mouse click is detected within the element.
3879 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3880 * @param {HtmlElement} t The target of the event.
3881 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3884 * @event contextmenu
3885 * Fires when a right click is detected within the element.
3886 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3887 * @param {HtmlElement} t The target of the event.
3888 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3892 * Fires when a mouse double click is detected within the element.
3893 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3894 * @param {HtmlElement} t The target of the event.
3895 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3899 * Fires when a mousedown is detected within the element.
3900 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3901 * @param {HtmlElement} t The target of the event.
3902 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3906 * Fires when a mouseup is detected within the element.
3907 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3908 * @param {HtmlElement} t The target of the event.
3909 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3913 * Fires when a mouseover is detected within the element.
3914 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3915 * @param {HtmlElement} t The target of the event.
3916 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3920 * Fires when a mousemove is detected with the element.
3921 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3922 * @param {HtmlElement} t The target of the event.
3923 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3927 * Fires when a mouseout is detected with the element.
3928 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3929 * @param {HtmlElement} t The target of the event.
3930 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3934 * Fires when the mouse enters the element.
3935 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3936 * @param {HtmlElement} t The target of the event.
3937 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3941 * Fires when the mouse leaves the element.
3942 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3943 * @param {HtmlElement} t The target of the event.
3944 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3950 * Fires when a keypress is detected within the element.
3951 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3952 * @param {HtmlElement} t The target of the event.
3953 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3957 * Fires when a keydown is detected within the element.
3958 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3959 * @param {HtmlElement} t The target of the event.
3960 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3964 * Fires when a keyup 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.
3971 // HTML frame/object events
3974 * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
3975 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3976 * @param {HtmlElement} t The target of the event.
3977 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3981 * 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.
3982 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3983 * @param {HtmlElement} t The target of the event.
3984 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3988 * Fires when an object/image is stopped from loading before completely loaded.
3989 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3990 * @param {HtmlElement} t The target of the event.
3991 * @param {Object} o The options configuration passed to the {@link #addListener} call.
3995 * Fires when an object/image/frame cannot be loaded properly.
3996 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
3997 * @param {HtmlElement} t The target of the event.
3998 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4002 * Fires when a document view is resized.
4003 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4004 * @param {HtmlElement} t The target of the event.
4005 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4009 * Fires when a document view is scrolled.
4010 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4011 * @param {HtmlElement} t The target of the event.
4012 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4018 * Fires when a user selects some text in a text field, including input and textarea.
4019 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4020 * @param {HtmlElement} t The target of the event.
4021 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4025 * Fires when a control loses the input focus and its value has been modified since gaining focus.
4026 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4027 * @param {HtmlElement} t The target of the event.
4028 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4032 * Fires when a form is submitted.
4033 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4034 * @param {HtmlElement} t The target of the event.
4035 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4039 * Fires when a form is reset.
4040 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4041 * @param {HtmlElement} t The target of the event.
4042 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4046 * Fires when an element receives focus either via the pointing device or by tab navigation.
4047 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4048 * @param {HtmlElement} t The target of the event.
4049 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4053 * Fires when an element loses focus either via the pointing device or by tabbing navigation.
4054 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4055 * @param {HtmlElement} t The target of the event.
4056 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4059 // User Interface events
4062 * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
4063 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4064 * @param {HtmlElement} t The target of the event.
4065 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4068 * @event DOMFocusOut
4069 * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
4070 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4071 * @param {HtmlElement} t The target of the event.
4072 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4075 * @event DOMActivate
4076 * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
4077 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4078 * @param {HtmlElement} t The target of the event.
4079 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4082 // DOM Mutation events
4084 * @event DOMSubtreeModified
4085 * Where supported. Fires when the subtree is modified.
4086 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4087 * @param {HtmlElement} t The target of the event.
4088 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4091 * @event DOMNodeInserted
4092 * Where supported. Fires when a node has been added as a child of another node.
4093 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4094 * @param {HtmlElement} t The target of the event.
4095 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4098 * @event DOMNodeRemoved
4099 * Where supported. Fires when a descendant node of the element is removed.
4100 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4101 * @param {HtmlElement} t The target of the event.
4102 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4105 * @event DOMNodeRemovedFromDocument
4106 * Where supported. Fires when a node is being removed from a document.
4107 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4108 * @param {HtmlElement} t The target of the event.
4109 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4112 * @event DOMNodeInsertedIntoDocument
4113 * Where supported. Fires when a node is being inserted into a document.
4114 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4115 * @param {HtmlElement} t The target of the event.
4116 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4119 * @event DOMAttrModified
4120 * Where supported. Fires when an attribute has been modified.
4121 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4122 * @param {HtmlElement} t The target of the event.
4123 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4126 * @event DOMCharacterDataModified
4127 * Where supported. Fires when the character data has been modified.
4128 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
4129 * @param {HtmlElement} t The target of the event.
4130 * @param {Object} o The options configuration passed to the {@link #addListener} call.
4134 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
4140 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
4141 * @param {String} selector The simple selector to test
4142 * @return {Boolean} True if this element matches the selector, else false
4144 is : function(simpleSelector){
4145 return Ext.DomQuery.is(this.dom, simpleSelector);
4149 * Tries to focus the element. Any exceptions are caught and ignored.
4150 * @param {Number} defer (optional) Milliseconds to defer the focus
4151 * @return {Ext.Element} this
4153 focus : function(defer, /* private */ dom) {
4155 dom = dom || me.dom;
4158 me.focus.defer(defer, null, [null, dom]);
4167 * Tries to blur the element. Any exceptions are caught and ignored.
4168 * @return {Ext.Element} this
4178 * Returns the value of the "value" attribute
4179 * @param {Boolean} asNumber true to parse the value as a number
4180 * @return {String/Number}
4182 getValue : function(asNumber){
4183 var val = this.dom.value;
4184 return asNumber ? parseInt(val, 10) : val;
4188 * Appends an event handler to this element. The shorthand version {@link #on} is equivalent.
4189 * @param {String} eventName The name of event to handle.
4190 * @param {Function} fn The handler function the event invokes. This function is passed
4191 * the following parameters:<ul>
4192 * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
4193 * <li><b>el</b> : HtmlElement<div class="sub-desc">The DOM element which was the target of the event.
4194 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
4195 * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
4197 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4198 * <b>If omitted, defaults to this Element.</b>.
4199 * @param {Object} options (optional) An object containing handler configuration properties.
4200 * This may contain any of the following properties:<ul>
4201 * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
4202 * <b>If omitted, defaults to this Element.</b></div></li>
4203 * <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>
4204 * <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>
4205 * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
4206 * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
4207 * <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>
4208 * <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>
4209 * <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>
4210 * <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>
4211 * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
4212 * by the specified number of milliseconds. If the event fires again within that time, the original
4213 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
4216 * <b>Combining Options</b><br>
4217 * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
4218 * addListener. The two are equivalent. Using the options argument, it is possible to combine different
4219 * types of listeners:<br>
4221 * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
4222 * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
4224 el.on('click', this.onClick, this, {
4229 });</code></pre></p>
4231 * <b>Attaching multiple handlers in 1 call</b><br>
4232 * The method also allows for a single argument to be passed which is a config object containing properties
4233 * which specify multiple handlers.</p>
4243 fn: this.onMouseOver,
4247 fn: this.onMouseOut,
4252 * Or a shorthand syntax:<br>
4253 * Code:<pre><code></p>
4255 'click' : this.onClick,
4256 'mouseover' : this.onMouseOver,
4257 'mouseout' : this.onMouseOut,
4261 * <p><b>delegate</b></p>
4262 * <p>This is a configuration option that you can pass along when registering a handler for
4263 * an event to assist with event delegation. Event delegation is a technique that is used to
4264 * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
4265 * for a container element as opposed to each element within a container. By setting this
4266 * configuration option to a simple selector, the target element will be filtered to look for
4267 * a descendant of the target.
4268 * For example:<pre><code>
4269 // using this markup:
4271 <p id='p1'>paragraph one</p>
4272 <p id='p2' class='clickable'>paragraph two</p>
4273 <p id='p3'>paragraph three</p>
4275 // utilize event delegation to registering just one handler on the container element:
4276 el = Ext.get('elId');
4281 console.info(t.id); // 'p2'
4285 // filter the target element to be a descendant with the class 'clickable'
4286 delegate: '.clickable'
4290 * @return {Ext.Element} this
4292 addListener : function(eventName, fn, scope, options){
4293 Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
4298 * Removes an event handler from this element. The shorthand version {@link #un} is equivalent.
4299 * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
4300 * listener, the same scope must be specified here.
4303 el.removeListener('click', this.handlerFn);
4305 el.un('click', this.handlerFn);
4307 * @param {String} eventName The name of the event from which to remove the handler.
4308 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4309 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4310 * then this must refer to the same object.
4311 * @return {Ext.Element} this
4313 removeListener : function(eventName, fn, scope){
4314 Ext.EventManager.removeListener(this.dom, eventName, fn, scope || this);
4319 * Removes all previous added listeners from this element
4320 * @return {Ext.Element} this
4322 removeAllListeners : function(){
4323 Ext.EventManager.removeAll(this.dom);
4328 * Recursively removes all previous added listeners from this element and its children
4329 * @return {Ext.Element} this
4331 purgeAllListeners : function() {
4332 Ext.EventManager.purgeElement(this, true);
4336 * @private Test if size has a unit, otherwise appends the default
4338 addUnits : function(size){
4339 if(size === "" || size == "auto" || size === undefined){
4341 } else if(!isNaN(size) || !unitPattern.test(size)){
4342 size = size + (this.defaultUnit || 'px');
4348 * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
4349 * 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>
4350 * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt><script></tt> elements. This is a browser restriction.</p>
4351 * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
4352 * exactly how to request the HTML.
4353 * @return {Ext.Element} this
4355 load : function(url, params, cb){
4356 Ext.Ajax.request(Ext.apply({
4358 url: url.url || url,
4361 indicatorText: url.indicatorText || ''
4362 }, Ext.isObject(url) ? url : {}));
4367 * Tests various css rules/browsers to determine if this element uses a border box
4370 isBorderBox : function(){
4371 return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
4375 * <p>Removes this element's dom reference. Note that event and cache removal is handled at {@link Ext#removeNode}</p>
4377 remove : function(){
4383 Ext.removeNode(dom);
4388 * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
4389 * @param {Function} overFn The function to call when the mouse enters the Element.
4390 * @param {Function} outFn The function to call when the mouse leaves the Element.
4391 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the functions are executed. Defaults to the Element's DOM element.
4392 * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
4393 * @return {Ext.Element} this
4395 hover : function(overFn, outFn, scope, options){
4397 me.on('mouseenter', overFn, scope || me.dom, options);
4398 me.on('mouseleave', outFn, scope || me.dom, options);
4403 * Returns true if this element is an ancestor of the passed element
4404 * @param {HTMLElement/String} el The element to check
4405 * @return {Boolean} True if this element is an ancestor of el, else false
4407 contains : function(el){
4408 return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
4412 * Returns the value of a namespaced attribute from the element's underlying DOM node.
4413 * @param {String} namespace The namespace in which to look for the attribute
4414 * @param {String} name The attribute name
4415 * @return {String} The attribute value
4418 getAttributeNS : function(ns, name){
4419 return this.getAttribute(name, ns);
4423 * Returns the value of an attribute from the element's underlying DOM node.
4424 * @param {String} name The attribute name
4425 * @param {String} namespace (optional) The namespace in which to look for the attribute
4426 * @return {String} The attribute value
4428 getAttribute : Ext.isIE ? function(name, ns){
4430 type = typeof d[ns + ":" + name];
4432 if(['undefined', 'unknown'].indexOf(type) == -1){
4433 return d[ns + ":" + name];
4436 } : function(name, ns){
4438 return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
4442 * Update the innerHTML of this element
4443 * @param {String} html The new HTML
4444 * @return {Ext.Element} this
4446 update : function(html) {
4448 this.dom.innerHTML = html;
4454 var ep = El.prototype;
4456 El.addMethods = function(o){
4461 * Appends an event handler (shorthand for {@link #addListener}).
4462 * @param {String} eventName The name of event to handle.
4463 * @param {Function} fn The handler function the event invokes.
4464 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the handler function is executed.
4465 * @param {Object} options (optional) An object containing standard {@link #addListener} options
4466 * @member Ext.Element
4469 ep.on = ep.addListener;
4472 * Removes an event handler from this element (see {@link #removeListener} for additional notes).
4473 * @param {String} eventName The name of the event from which to remove the handler.
4474 * @param {Function} fn The handler function to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
4475 * @param {Object} scope If a scope (<b><code>this</code></b> reference) was specified when the listener was added,
4476 * then this must refer to the same object.
4477 * @return {Ext.Element} this
4478 * @member Ext.Element
4481 ep.un = ep.removeListener;
4484 * true to automatically adjust width and height settings for box-model issues (default to true)
4486 ep.autoBoxAdjust = true;
4489 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
4497 * Retrieves Ext.Element objects.
4498 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4499 * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4500 * its ID, use {@link Ext.ComponentMgr#get}.</p>
4501 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4502 * object was recreated with the same id via AJAX or DOM.</p>
4503 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4504 * @return {Element} The Element object (or null if no matching element was found)
4506 * @member Ext.Element
4509 El.get = function(el){
4513 if(!el){ return null; }
4514 if (typeof el == "string") { // element id
4515 if (!(elm = DOC.getElementById(el))) {
4518 if (EC[el] && EC[el].el) {
4522 ex = El.addToCache(new El(elm));
4525 } else if (el.tagName) { // dom element
4529 if (EC[id] && EC[id].el) {
4533 ex = El.addToCache(new El(el));
4536 } else if (el instanceof El) {
4538 el.dom = DOC.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
4539 // catch case where it hasn't been appended
4542 } else if(el.isComposite) {
4544 } else if(Ext.isArray(el)) {
4545 return El.select(el);
4546 } else if(el == DOC) {
4547 // create a bogus element object representing the document object
4549 var f = function(){};
4550 f.prototype = El.prototype;
4559 El.addToCache = function(el, id){
4569 // private method for getting and setting element data
4570 El.data = function(el, key, value){
4575 var c = EC[el.id].data;
4576 if(arguments.length == 2){
4579 return (c[key] = value);
4584 // Garbage collection - uncache elements/purge listeners on orphaned elements
4585 // so we don't hold a reference and cause the browser to retain them
4586 function garbageCollect(){
4587 if(!Ext.enableGarbageCollector){
4588 clearInterval(El.collectorThreadId);
4602 // -------------------------------------------------------
4603 // Determining what is garbage:
4604 // -------------------------------------------------------
4606 // dom node is null, definitely garbage
4607 // -------------------------------------------------------
4609 // no parentNode == direct orphan, definitely garbage
4610 // -------------------------------------------------------
4611 // !d.offsetParent && !document.getElementById(eid)
4612 // display none elements have no offsetParent so we will
4613 // also try to look it up by it's id. However, check
4614 // offsetParent first so we don't do unneeded lookups.
4615 // This enables collection of elements that are not orphans
4616 // directly, but somewhere up the line they have an orphan
4618 // -------------------------------------------------------
4619 if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
4620 if(Ext.enableListenerCollection){
4621 Ext.EventManager.removeAll(d);
4626 // Cleanup IE Object leaks
4632 EC = Ext.elCache = t;
4636 El.collectorThreadId = setInterval(garbageCollect, 30000);
4638 var flyFn = function(){};
4639 flyFn.prototype = El.prototype;
4642 El.Flyweight = function(dom){
4646 El.Flyweight.prototype = new flyFn();
4647 El.Flyweight.prototype.isFlyweight = true;
4648 El._flyweights = {};
4651 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4652 * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4653 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4654 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4655 * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4656 * @param {String/HTMLElement} el The dom node or id
4657 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4658 * (e.g. internally Ext uses "_global")
4659 * @return {Element} The shared Element object (or null if no matching element was found)
4660 * @member Ext.Element
4663 El.fly = function(el, named){
4665 named = named || '_global';
4667 if (el = Ext.getDom(el)) {
4668 (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
4669 ret = El._flyweights[named];
4675 * Retrieves Ext.Element objects.
4676 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
4677 * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
4678 * its ID, use {@link Ext.ComponentMgr#get}.</p>
4679 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
4680 * object was recreated with the same id via AJAX or DOM.</p>
4681 * Shorthand of {@link Ext.Element#get}
4682 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
4683 * @return {Element} The Element object (or null if no matching element was found)
4690 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
4691 * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
4692 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
4693 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
4694 * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
4695 * @param {String/HTMLElement} el The dom node or id
4696 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
4697 * (e.g. internally Ext uses "_global")
4698 * @return {Element} The shared Element object (or null if no matching element was found)
4704 // speedy lookup for elements never to box adjust
4705 var noBoxAdjust = Ext.isStrict ? {
4708 input:1, select:1, textarea:1
4710 if(Ext.isIE || Ext.isGecko){
4711 noBoxAdjust['button'] = 1;
4716 * @class Ext.Element
4718 Ext.Element.addMethods({
4720 * Stops the specified event(s) from bubbling and optionally prevents the default action
4721 * @param {String/Array} eventName an event / array of events to stop from bubbling
4722 * @param {Boolean} preventDefault (optional) true to prevent the default action too
4723 * @return {Ext.Element} this
4725 swallowEvent : function(eventName, preventDefault){
4728 e.stopPropagation();
4733 if(Ext.isArray(eventName)){
4734 Ext.each(eventName, function(e) {
4739 me.on(eventName, fn);
4744 * Create an event handler on this element such that when the event fires and is handled by this element,
4745 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
4746 * @param {String} eventName The type of event to relay
4747 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
4748 * for firing the relayed event
4750 relayEvent : function(eventName, observable){
4751 this.on(eventName, function(e){
4752 observable.fireEvent(eventName, e);
4757 * Removes worthless text nodes
4758 * @param {Boolean} forceReclean (optional) By default the element
4759 * keeps track if it has been cleaned already so
4760 * you can call this over and over. However, if you update the element and
4761 * need to force a reclean, you can pass true.
4763 clean : function(forceReclean){
4769 if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){
4774 var nx = n.nextSibling;
4775 if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){
4782 Ext.Element.data(dom, 'isCleaned', true);
4787 * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
4788 * parameter as {@link Ext.Updater#update}
4789 * @return {Ext.Element} this
4792 var um = this.getUpdater();
4793 um.update.apply(um, arguments);
4798 * Gets this element's {@link Ext.Updater Updater}
4799 * @return {Ext.Updater} The Updater
4801 getUpdater : function(){
4802 return this.updateManager || (this.updateManager = new Ext.Updater(this));
4806 * Update the innerHTML of this element, optionally searching for and processing scripts
4807 * @param {String} html The new HTML
4808 * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
4809 * @param {Function} callback (optional) For async script loading you can be notified when the update completes
4810 * @return {Ext.Element} this
4812 update : function(html, loadScripts, callback){
4818 if(loadScripts !== true){
4819 this.dom.innerHTML = html;
4820 if(Ext.isFunction(callback)){
4829 html += '<span id="' + id + '"></span>';
4831 Ext.lib.Event.onAvailable(id, function(){
4833 hd = DOC.getElementsByTagName("head")[0],
4834 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
4835 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
4836 typeRe = /\stype=([\'\"])(.*?)\1/i,
4844 while((match = re.exec(html))){
4846 srcMatch = attrs ? attrs.match(srcRe) : false;
4847 if(srcMatch && srcMatch[2]){
4848 s = DOC.createElement("script");
4849 s.src = srcMatch[2];
4850 typeMatch = attrs.match(typeRe);
4851 if(typeMatch && typeMatch[2]){
4852 s.type = typeMatch[2];
4855 }else if(match[2] && match[2].length > 0){
4856 if(window.execScript) {
4857 window.execScript(match[2]);
4859 window.eval(match[2]);
4863 el = DOC.getElementById(id);
4864 if(el){Ext.removeNode(el);}
4865 if(Ext.isFunction(callback)){
4869 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
4873 // inherit docs, overridden so we can add removeAnchor
4874 removeAllListeners : function(){
4875 this.removeAnchor();
4876 Ext.EventManager.removeAll(this.dom);
4881 * Creates a proxy element of this element
4882 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
4883 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
4884 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
4885 * @return {Ext.Element} The new proxy element
4887 createProxy : function(config, renderTo, matchBox){
4888 config = Ext.isObject(config) ? config : {tag : "div", cls: config};
4891 proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
4892 Ext.DomHelper.insertBefore(me.dom, config, true);
4894 if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded
4895 proxy.setBox(me.getBox());
4901 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
4903 * @class Ext.Element
4905 Ext.Element.addMethods({
4907 * Gets the x,y coordinates specified by the anchor position on the element.
4908 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo}
4909 * for details on supported anchor positions.
4910 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
4911 * of page coordinates
4912 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
4913 * {width: (target width), height: (target height)} (defaults to the element's current size)
4914 * @return {Array} [x, y] An array containing the element's x and y coordinates
4916 getAnchorXY : function(anchor, local, s){
4917 //Passing a different size is useful for pre-calculating anchors,
4918 //especially for anchored animations that change the el size.
4919 anchor = (anchor || "tl").toLowerCase();
4923 vp = me.dom == document.body || me.dom == document,
4924 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
4925 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),
4929 scroll = me.getScroll(),
4930 extraX = vp ? scroll.left : !local ? o[0] : 0,
4931 extraY = vp ? scroll.top : !local ? o[1] : 0,
4933 c : [r(w * 0.5), r(h * 0.5)],
4934 t : [r(w * 0.5), 0],
4935 l : [0, r(h * 0.5)],
4936 r : [w, r(h * 0.5)],
4937 b : [r(w * 0.5), h],
4945 return [xy[0] + extraX, xy[1] + extraY];
4949 * Anchors an element to another element and realigns it when the window is resized.
4950 * @param {Mixed} element The element to align to.
4951 * @param {String} position The position to align to.
4952 * @param {Array} offsets (optional) Offset the positioning by [x, y]
4953 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
4954 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
4955 * is a number, it is used as the buffer delay (defaults to 50ms).
4956 * @param {Function} callback The function to call after the animation finishes
4957 * @return {Ext.Element} this
4959 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
4962 scroll = !Ext.isEmpty(monitorScroll),
4963 action = function(){
4964 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
4965 Ext.callback(callback, Ext.fly(dom));
4967 anchor = this.getAnchor();
4969 // previous listener anchor, remove it
4970 this.removeAnchor();
4976 Ext.EventManager.onWindowResize(action, null);
4979 Ext.EventManager.on(window, 'scroll', action, null,
4980 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
4982 action.call(me); // align immediately
4987 * Remove any anchor to this element. See {@link #anchorTo}.
4988 * @return {Ext.Element} this
4990 removeAnchor : function(){
4992 anchor = this.getAnchor();
4994 if(anchor && anchor.fn){
4995 Ext.EventManager.removeResizeListener(anchor.fn);
4997 Ext.EventManager.un(window, 'scroll', anchor.fn);
5005 getAnchor : function(){
5006 var data = Ext.Element.data,
5011 var anchor = data(dom, '_anchor');
5014 anchor = data(dom, '_anchor', {});
5020 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
5021 * supported position values.
5022 * @param {Mixed} element The element to align to.
5023 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5024 * @param {Array} offsets (optional) Offset the positioning by [x, y]
5025 * @return {Array} [x, y]
5027 getAlignToXY : function(el, p, o){
5031 throw "Element.alignToXY with an element that doesn't exist";
5035 p = (!p || p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
5043 //constrain the aligned el to viewport if necessary
5047 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
5048 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
5056 docElement = doc.documentElement,
5058 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
5059 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
5060 c = false, //constrain to viewport
5063 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
5066 throw "Element.alignTo with an invalid alignment " + p;
5073 //Subtract the aligned el's internal xy from the target's offset xy
5074 //plus custom offset to get the aligned el's new offset xy
5075 a1 = me.getAnchorXY(p1, true);
5076 a2 = el.getAnchorXY(p2, false);
5078 x = a2[0] - a1[0] + o[0];
5079 y = a2[1] - a1[1] + o[1];
5085 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
5086 //perpendicular to the vp border, allow the aligned el to slide on that border,
5087 //otherwise swap the aligned el to the opposite border of the target.
5089 p1x = p1.charAt(p1.length-1);
5091 p2x = p2.charAt(p2.length-1);
5092 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
5093 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
5096 if (x + w > dw + scrollX) {
5097 x = swapX ? r.left-w : dw+scrollX-w;
5100 x = swapX ? r.right : scrollX;
5102 if (y + h > dh + scrollY) {
5103 y = swapY ? r.top-h : dh+scrollY-h;
5106 y = swapY ? r.bottom : scrollY;
5113 * Aligns this element with another element relative to the specified anchor points. If the other element is the
5114 * document it aligns it to the viewport.
5115 * The position parameter is optional, and can be specified in any one of the following formats:
5117 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
5118 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
5119 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
5120 * deprecated in favor of the newer two anchor syntax below</i>.</li>
5121 * <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
5122 * element's anchor point, and the second value is used as the target's anchor point.</li>
5124 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
5125 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
5126 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
5127 * that specified in order to enforce the viewport constraints.
5128 * Following are all of the supported anchor positions:
5131 ----- -----------------------------
5132 tl The top left corner (default)
5133 t The center of the top edge
5134 tr The top right corner
5135 l The center of the left edge
5136 c In the center of the element
5137 r The center of the right edge
5138 bl The bottom left corner
5139 b The center of the bottom edge
5140 br The bottom right corner
5144 // align el to other-el using the default positioning ("tl-bl", non-constrained)
5145 el.alignTo("other-el");
5147 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
5148 el.alignTo("other-el", "tr?");
5150 // align the bottom right corner of el with the center left edge of other-el
5151 el.alignTo("other-el", "br-l?");
5153 // align the center of el with the bottom left corner of other-el and
5154 // adjust the x position by -6 pixels (and the y position by 0)
5155 el.alignTo("other-el", "c-bl", [-6, 0]);
5157 * @param {Mixed} element The element to align to.
5158 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
5159 * @param {Array} offsets (optional) Offset the positioning by [x, y]
5160 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
5161 * @return {Ext.Element} this
5163 alignTo : function(element, position, offsets, animate){
5165 return me.setXY(me.getAlignToXY(element, position, offsets),
5166 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
5169 // private ==> used outside of core
5170 adjustForConstraints : function(xy, parent, offsets){
5171 return this.getConstrainToXY(parent || document, false, offsets, xy) || xy;
5174 // private ==> used outside of core
5175 getConstrainToXY : function(el, local, offsets, proposedXY){
5176 var os = {top:0, left:0, bottom:0, right: 0};
5178 return function(el, local, offsets, proposedXY){
5180 offsets = offsets ? Ext.applyIf(offsets, os) : os;
5182 var vw, vh, vx = 0, vy = 0;
5183 if(el.dom == document.body || el.dom == document){
5184 vw =Ext.lib.Dom.getViewWidth();
5185 vh = Ext.lib.Dom.getViewHeight();
5187 vw = el.dom.clientWidth;
5188 vh = el.dom.clientHeight;
5190 var vxy = el.getXY();
5196 var s = el.getScroll();
5198 vx += offsets.left + s.left;
5199 vy += offsets.top + s.top;
5201 vw -= offsets.right;
5202 vh -= offsets.bottom;
5207 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
5208 var x = xy[0], y = xy[1];
5209 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
5211 // only move it if it needs it
5214 // first validate right/bottom
5223 // then make sure top/left isn't negative
5232 return moved ? [x, y] : false;
5238 // el = Ext.get(el);
5239 // offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
5243 // s = el.getScroll(),
5244 // vxy = el.getXY(),
5245 // vx = offsets.left + s.left,
5246 // vy = offsets.top + s.top,
5247 // vw = -offsets.right,
5248 // vh = -offsets.bottom,
5251 // xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
5254 // w = me.dom.offsetWidth, h = me.dom.offsetHeight,
5255 // moved = false; // only move it if it needs it
5258 // if(el.dom == doc.body || el.dom == doc){
5259 // vw += Ext.lib.Dom.getViewWidth();
5260 // vh += Ext.lib.Dom.getViewHeight();
5262 // vw += el.dom.clientWidth;
5263 // vh += el.dom.clientHeight;
5270 // // first validate right/bottom
5271 // if(x + w > vx + vw){
5275 // if(y + h > vy + vh){
5279 // // then make sure top/left isn't negative
5288 // return moved ? [x, y] : false;
5292 * Calculates the x, y to center this element on the screen
5293 * @return {Array} The x, y values [x, y]
5295 getCenterXY : function(){
5296 return this.getAlignToXY(document, 'c-c');
5300 * Centers the Element in either the viewport, or another Element.
5301 * @param {Mixed} centerIn (optional) The element in which to center the element.
5303 center : function(centerIn){
5304 return this.alignTo(centerIn || document, 'c-c');
5308 * @class Ext.Element
\r
5310 Ext.Element.addMethods(function(){
\r
5311 var PARENTNODE = 'parentNode',
\r
5312 NEXTSIBLING = 'nextSibling',
\r
5313 PREVIOUSSIBLING = 'previousSibling',
\r
5314 DQ = Ext.DomQuery,
\r
5319 * 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)
\r
5320 * @param {String} selector The simple selector to test
\r
5321 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
\r
5322 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
\r
5323 * @return {HTMLElement} The matching DOM node (or null if no match was found)
\r
5325 findParent : function(simpleSelector, maxDepth, returnEl){
\r
5327 b = document.body,
\r
5330 if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {
\r
5333 maxDepth = maxDepth || 50;
\r
5334 if (isNaN(maxDepth)) {
\r
5335 stopEl = Ext.getDom(maxDepth);
\r
5336 maxDepth = Number.MAX_VALUE;
\r
5338 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
\r
5339 if(DQ.is(p, simpleSelector)){
\r
5340 return returnEl ? GET(p) : p;
\r
5349 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
\r
5350 * @param {String} selector The simple selector to test
\r
5351 * @param {Number/Mixed} maxDepth (optional) The max depth to
\r
5352 search as a number or element (defaults to 10 || document.body)
\r
5353 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
\r
5354 * @return {HTMLElement} The matching DOM node (or null if no match was found)
\r
5356 findParentNode : function(simpleSelector, maxDepth, returnEl){
\r
5357 var p = Ext.fly(this.dom.parentNode, '_internal');
\r
5358 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
\r
5362 * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
\r
5363 * This is a shortcut for findParentNode() that always returns an Ext.Element.
\r
5364 * @param {String} selector The simple selector to test
\r
5365 * @param {Number/Mixed} maxDepth (optional) The max depth to
\r
5366 search as a number or element (defaults to 10 || document.body)
\r
5367 * @return {Ext.Element} The matching DOM node (or null if no match was found)
\r
5369 up : function(simpleSelector, maxDepth){
\r
5370 return this.findParentNode(simpleSelector, maxDepth, true);
\r
5374 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
\r
5375 * @param {String} selector The CSS selector
\r
5376 * @return {CompositeElement/CompositeElementLite} The composite element
\r
5378 select : function(selector){
\r
5379 return Ext.Element.select(selector, this.dom);
\r
5383 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
\r
5384 * @param {String} selector The CSS selector
\r
5385 * @return {Array} An array of the matched nodes
\r
5387 query : function(selector){
\r
5388 return DQ.select(selector, this.dom);
\r
5392 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
\r
5393 * @param {String} selector The CSS selector
\r
5394 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
\r
5395 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
\r
5397 child : function(selector, returnDom){
\r
5398 var n = DQ.selectNode(selector, this.dom);
\r
5399 return returnDom ? n : GET(n);
\r
5403 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
\r
5404 * @param {String} selector The CSS selector
\r
5405 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
\r
5406 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
\r
5408 down : function(selector, returnDom){
\r
5409 var n = DQ.selectNode(" > " + selector, this.dom);
\r
5410 return returnDom ? n : GET(n);
\r
5414 * Gets the parent node for this element, optionally chaining up trying to match a selector
\r
5415 * @param {String} selector (optional) Find a parent node that matches the passed simple selector
\r
5416 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
\r
5417 * @return {Ext.Element/HTMLElement} The parent node or null
\r
5419 parent : function(selector, returnDom){
\r
5420 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);
\r
5424 * Gets the next sibling, skipping text nodes
\r
5425 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
\r
5426 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
\r
5427 * @return {Ext.Element/HTMLElement} The next sibling or null
\r
5429 next : function(selector, returnDom){
\r
5430 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);
\r
5434 * Gets the previous sibling, skipping text nodes
\r
5435 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
\r
5436 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
\r
5437 * @return {Ext.Element/HTMLElement} The previous sibling or null
\r
5439 prev : function(selector, returnDom){
\r
5440 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);
\r
5445 * Gets the first child, skipping text nodes
\r
5446 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
\r
5447 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
\r
5448 * @return {Ext.Element/HTMLElement} The first child or null
\r
5450 first : function(selector, returnDom){
\r
5451 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);
\r
5455 * Gets the last child, skipping text nodes
\r
5456 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
\r
5457 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
\r
5458 * @return {Ext.Element/HTMLElement} The last child or null
\r
5460 last : function(selector, returnDom){
\r
5461 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);
\r
5464 matchNode : function(dir, start, selector, returnDom){
\r
5465 var n = this.dom[start];
\r
5467 if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){
\r
5468 return !returnDom ? GET(n) : n;
\r
5476 * @class Ext.Element
\r
5478 Ext.Element.addMethods({
\r
5480 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
\r
5481 * @param {String} selector The CSS selector
\r
5482 * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)
\r
5483 * @return {CompositeElement/CompositeElementLite} The composite element
\r
5485 select : function(selector, unique){
\r
5486 return Ext.Element.select(selector, unique, this.dom);
\r
5489 * @class Ext.Element
\r
5491 Ext.Element.addMethods(
\r
5493 var GETDOM = Ext.getDom,
\r
5495 DH = Ext.DomHelper;
\r
5499 * Appends the passed element(s) to this element
\r
5500 * @param {String/HTMLElement/Array/Element/CompositeElement} el
\r
5501 * @return {Ext.Element} this
\r
5503 appendChild: function(el){
\r
5504 return GET(el).appendTo(this);
\r
5508 * Appends this element to the passed element
\r
5509 * @param {Mixed} el The new parent element
\r
5510 * @return {Ext.Element} this
\r
5512 appendTo: function(el){
\r
5513 GETDOM(el).appendChild(this.dom);
\r
5518 * Inserts this element before the passed element in the DOM
\r
5519 * @param {Mixed} el The element before which this element will be inserted
\r
5520 * @return {Ext.Element} this
\r
5522 insertBefore: function(el){
\r
5523 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);
\r
5528 * Inserts this element after the passed element in the DOM
\r
5529 * @param {Mixed} el The element to insert after
\r
5530 * @return {Ext.Element} this
\r
5532 insertAfter: function(el){
\r
5533 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);
\r
5538 * Inserts (or creates) an element (or DomHelper config) as the first child of this element
\r
5539 * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
\r
5540 * @return {Ext.Element} The new child
\r
5542 insertFirst: function(el, returnDom){
\r
5544 if(el.nodeType || el.dom || typeof el == 'string'){ // element
\r
5546 this.dom.insertBefore(el, this.dom.firstChild);
\r
5547 return !returnDom ? GET(el) : el;
\r
5548 }else{ // dh config
\r
5549 return this.createChild(el, this.dom.firstChild, returnDom);
\r
5554 * Replaces the passed element with this element
\r
5555 * @param {Mixed} el The element to replace
\r
5556 * @return {Ext.Element} this
\r
5558 replace: function(el){
\r
5560 this.insertBefore(el);
\r
5566 * Replaces this element with the passed element
\r
5567 * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
\r
5568 * @return {Ext.Element} this
\r
5570 replaceWith: function(el){
\r
5573 if(el.nodeType || el.dom || typeof el == 'string'){
\r
5575 me.dom.parentNode.insertBefore(el, me.dom);
\r
5577 el = DH.insertBefore(me.dom, el);
\r
5580 delete Ext.elCache[me.id];
\r
5581 Ext.removeNode(me.dom);
\r
5582 me.id = Ext.id(me.dom = el);
\r
5583 Ext.Element.addToCache(me.isFlyweight ? new Ext.Element(me.dom) : me);
\r
5588 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
\r
5589 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
\r
5590 * automatically generated with the specified attributes.
\r
5591 * @param {HTMLElement} insertBefore (optional) a child element of this element
\r
5592 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
\r
5593 * @return {Ext.Element} The new child element
\r
5595 createChild: function(config, insertBefore, returnDom){
\r
5596 config = config || {tag:'div'};
\r
5597 return insertBefore ?
\r
5598 DH.insertBefore(insertBefore, config, returnDom !== true) :
\r
5599 DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config, returnDom !== true);
\r
5603 * Creates and wraps this element with another element
\r
5604 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
\r
5605 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
\r
5606 * @return {HTMLElement/Element} The newly created wrapper element
\r
5608 wrap: function(config, returnDom){
\r
5609 var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
\r
5610 newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
\r
5615 * Inserts an html fragment into this element
\r
5616 * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
\r
5617 * @param {String} html The HTML fragment
\r
5618 * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
\r
5619 * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
\r
5621 insertHtml : function(where, html, returnEl){
\r
5622 var el = DH.insertHtml(where, this.dom, html);
\r
5623 return returnEl ? Ext.get(el) : el;
\r
5627 * @class Ext.Element
\r
5629 Ext.apply(Ext.Element.prototype, function() {
\r
5630 var GETDOM = Ext.getDom,
\r
5632 DH = Ext.DomHelper;
\r
5636 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
\r
5637 * @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.
\r
5638 * @param {String} where (optional) 'before' or 'after' defaults to before
\r
5639 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
\r
5640 * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
\r
5642 insertSibling: function(el, where, returnDom){
\r
5645 isAfter = (where || 'before').toLowerCase() == 'after',
\r
5648 if(Ext.isArray(el)){
\r
5650 Ext.each(el, function(e) {
\r
5651 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
\r
5661 if(el.nodeType || el.dom){
\r
5662 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);
\r
5667 if (isAfter && !me.dom.nextSibling) {
\r
5668 rt = DH.append(me.dom.parentNode, el, !returnDom);
\r
5670 rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
\r
5677 * @class Ext.Element
5679 Ext.Element.addMethods(function(){
5680 // local style camelizing for speed
5682 camelRe = /(-[a-z])/gi,
5684 view = document.defaultView,
5685 propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
5686 opacityRe = /alpha\(opacity=(.*)\)/i,
5687 trimRe = /^\s+|\s+$/g,
5689 PADDING = "padding",
5699 ISCLIPPED = 'isClipped',
5700 OVERFLOW = 'overflow',
5701 OVERFLOWX = 'overflow-x',
5702 OVERFLOWY = 'overflow-y',
5703 ORIGINALCLIP = 'originalClip',
5704 // special markup used throughout Ext when box wrapping elements
5705 borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
5706 paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
5707 margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
5708 data = Ext.Element.data;
5712 function camelFn(m, a) {
5713 return a.charAt(1).toUpperCase();
5716 function chkCache(prop) {
5717 return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
5721 // private ==> used by Fx
5722 adjustWidth : function(width) {
5724 var isNum = Ext.isNumber(width);
5725 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5726 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
5728 return (isNum && width < 0) ? 0 : width;
5731 // private ==> used by Fx
5732 adjustHeight : function(height) {
5734 var isNum = Ext.isNumber(height);
5735 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
5736 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
5738 return (isNum && height < 0) ? 0 : height;
5743 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
5744 * @param {String/Array} className The CSS class to add, or an array of classes
5745 * @return {Ext.Element} this
5747 addClass : function(className){
5748 var me = this, i, len, v;
5749 className = Ext.isArray(className) ? className : [className];
5750 for (i=0, len = className.length; i < len; i++) {
5753 me.dom.className += (!me.hasClass(v) && v ? " " + v : "");
5760 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
5761 * @param {String/Array} className The CSS class to add, or an array of classes
5762 * @return {Ext.Element} this
5764 radioClass : function(className){
5765 var cn = this.dom.parentNode.childNodes, v;
5766 className = Ext.isArray(className) ? className : [className];
5767 for (var i=0, len = cn.length; i < len; i++) {
5769 if(v && v.nodeType == 1) {
5770 Ext.fly(v, '_internal').removeClass(className);
5773 return this.addClass(className);
5777 * Removes one or more CSS classes from the element.
5778 * @param {String/Array} className The CSS class to remove, or an array of classes
5779 * @return {Ext.Element} this
5781 removeClass : function(className){
5783 className = Ext.isArray(className) ? className : [className];
5784 if (me.dom && me.dom.className) {
5785 for (var i=0, len=className.length; i < len; i++) {
5788 me.dom.className = me.dom.className.replace(
5789 classReCache[v] = classReCache[v] || new RegExp('(?:^|\\s+)' + v + '(?:\\s+|$)', "g"), " "
5798 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
5799 * @param {String} className The CSS class to toggle
5800 * @return {Ext.Element} this
5802 toggleClass : function(className){
5803 return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
5807 * Checks if the specified CSS class exists on this element's DOM node.
5808 * @param {String} className The CSS class to check for
5809 * @return {Boolean} True if the class exists, else false
5811 hasClass : function(className){
5812 return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
5816 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
5817 * @param {String} oldClassName The CSS class to replace
5818 * @param {String} newClassName The replacement CSS class
5819 * @return {Ext.Element} this
5821 replaceClass : function(oldClassName, newClassName){
5822 return this.removeClass(oldClassName).addClass(newClassName);
5825 isStyle : function(style, val) {
5826 return this.getStyle(style) == val;
5830 * Normalizes currentStyle and computedStyle.
5831 * @param {String} property The style property whose value is returned.
5832 * @return {String} The current value of the style property for this element.
5834 getStyle : function(){
5835 return view && view.getComputedStyle ?
5848 prop = chkCache(prop);
5849 // Fix bug caused by this: https://bugs.webkit.org/show_bug.cgi?id=13343
5850 if(wk && /marginRight/.test(prop)){
5851 display = this.getStyle('display');
5852 el.style.display = 'inline-block';
5854 out = (v = el.style[prop]) ? v :
5855 (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
5857 // Webkit returns rgb values for transparent.
5859 if(out == 'rgba(0, 0, 0, 0)'){
5860 out = 'transparent';
5862 el.style.display = display;
5872 if(el == document) return null;
5873 if (prop == 'opacity') {
5874 if (el.style.filter.match) {
5875 if(m = el.style.filter.match(opacityRe)){
5876 var fv = parseFloat(m[1]);
5878 return fv ? fv / 100 : 0;
5884 prop = chkCache(prop);
5885 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
5890 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
5891 * are convert to standard 6 digit hex color.
5892 * @param {String} attr The css attribute
5893 * @param {String} defaultValue The default value to use when a valid color isn't found
5894 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
5897 getColor : function(attr, defaultValue, prefix){
5898 var v = this.getStyle(attr),
5899 color = Ext.isDefined(prefix) ? prefix : '#',
5902 if(!v || /transparent|inherit/.test(v)){
5903 return defaultValue;
5906 Ext.each(v.slice(4, v.length -1).split(','), function(s){
5907 h = parseInt(s, 10);
5908 color += (h < 16 ? '0' : '') + h.toString(16);
5911 v = v.replace('#', '');
5912 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
5914 return(color.length > 5 ? color.toLowerCase() : defaultValue);
5918 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
5919 * @param {String/Object} property The style property to be set, or an object of multiple styles.
5920 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
5921 * @return {Ext.Element} this
5923 setStyle : function(prop, value){
5927 if (!Ext.isObject(prop)) {
5932 for (style in prop) {
5933 value = prop[style];
5934 style == 'opacity' ?
5935 this.setOpacity(value) :
5936 this.dom.style[chkCache(style)] = value;
5942 * Set the opacity of the element
5943 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
5944 * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
5945 * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
5946 * @return {Ext.Element} this
5948 setOpacity : function(opacity, animate){
5952 if(!animate || !me.anim){
5954 var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
5955 val = s.filter.replace(opacityRe, '').replace(trimRe, '');
5958 s.filter = val + (val.length > 0 ? ' ' : '') + opac;
5960 s.opacity = opacity;
5963 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
5969 * Clears any opacity settings from this element. Required in some cases for IE.
5970 * @return {Ext.Element} this
5972 clearOpacity : function(){
5973 var style = this.dom.style;
5975 if(!Ext.isEmpty(style.filter)){
5976 style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
5979 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
5985 * Returns the offset height of the element
5986 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
5987 * @return {Number} The element's height
5989 getHeight : function(contentHeight){
5992 hidden = Ext.isIE && me.isStyle('display', 'none'),
5993 h = MATH.max(dom.offsetHeight, hidden ? 0 : dom.clientHeight) || 0;
5995 h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
5996 return h < 0 ? 0 : h;
6000 * Returns the offset width of the element
6001 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
6002 * @return {Number} The element's width
6004 getWidth : function(contentWidth){
6007 hidden = Ext.isIE && me.isStyle('display', 'none'),
6008 w = MATH.max(dom.offsetWidth, hidden ? 0 : dom.clientWidth) || 0;
6009 w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
6010 return w < 0 ? 0 : w;
6014 * Set the width of this Element.
6015 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
6016 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
6017 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
6019 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6020 * @return {Ext.Element} this
6022 setWidth : function(width, animate){
6024 width = me.adjustWidth(width);
6025 !animate || !me.anim ?
6026 me.dom.style.width = me.addUnits(width) :
6027 me.anim({width : {to : width}}, me.preanim(arguments, 1));
6032 * Set the height of this Element.
6034 // change the height to 200px and animate with default configuration
6035 Ext.fly('elementId').setHeight(200, true);
6037 // change the height to 150px and animate with a custom configuration
6038 Ext.fly('elId').setHeight(150, {
6039 duration : .5, // animation will have a duration of .5 seconds
6040 // will change the content to "finished"
6041 callback: function(){ this.{@link #update}("finished"); }
6044 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
6045 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
6046 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
6048 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
6049 * @return {Ext.Element} this
6051 setHeight : function(height, animate){
6053 height = me.adjustHeight(height);
6054 !animate || !me.anim ?
6055 me.dom.style.height = me.addUnits(height) :
6056 me.anim({height : {to : height}}, me.preanim(arguments, 1));
6061 * Gets the width of the border(s) for the specified side(s)
6062 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6063 * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
6064 * @return {Number} The width of the sides passed added together
6066 getBorderWidth : function(side){
6067 return this.addStyles(side, borders);
6071 * Gets the width of the padding(s) for the specified side(s)
6072 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
6073 * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
6074 * @return {Number} The padding of the sides passed added together
6076 getPadding : function(side){
6077 return this.addStyles(side, paddings);
6081 * Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
6082 * @return {Ext.Element} this
6088 if(!data(dom, ISCLIPPED)){
6089 data(dom, ISCLIPPED, true);
6090 data(dom, ORIGINALCLIP, {
6091 o: me.getStyle(OVERFLOW),
6092 x: me.getStyle(OVERFLOWX),
6093 y: me.getStyle(OVERFLOWY)
6095 me.setStyle(OVERFLOW, HIDDEN);
6096 me.setStyle(OVERFLOWX, HIDDEN);
6097 me.setStyle(OVERFLOWY, HIDDEN);
6103 * Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
6104 * @return {Ext.Element} this
6106 unclip : function(){
6110 if(data(dom, ISCLIPPED)){
6111 data(dom, ISCLIPPED, false);
6112 var o = data(dom, ORIGINALCLIP);
6114 me.setStyle(OVERFLOW, o.o);
6117 me.setStyle(OVERFLOWX, o.x);
6120 me.setStyle(OVERFLOWY, o.y);
6127 addStyles : function(sides, styles){
6129 m = sides.match(/\w/g),
6131 for (var i=0, len=m.length; i<len; i++) {
6132 s = m[i] && parseInt(this.getStyle(styles[m[i]]), 10);
6145 * @class Ext.Element
\r
6148 // special markup used throughout Ext when box wrapping elements
\r
6149 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>';
\r
6151 Ext.Element.addMethods(function(){
\r
6152 var INTERNAL = "_internal",
\r
6153 pxMatch = /(\d+\.?\d+)px/;
\r
6156 * More flexible version of {@link #setStyle} for setting style properties.
\r
6157 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
\r
6158 * a function which returns such a specification.
\r
6159 * @return {Ext.Element} this
\r
6161 applyStyles : function(style){
\r
6162 Ext.DomHelper.applyStyles(this.dom, style);
\r
6167 * Returns an object with properties matching the styles requested.
\r
6168 * For example, el.getStyles('color', 'font-size', 'width') might return
\r
6169 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
\r
6170 * @param {String} style1 A style name
\r
6171 * @param {String} style2 A style name
\r
6172 * @param {String} etc.
\r
6173 * @return {Object} The style object
\r
6175 getStyles : function(){
\r
6177 Ext.each(arguments, function(v) {
\r
6178 ret[v] = this.getStyle(v);
\r
6184 // private ==> used by ext full
\r
6185 setOverflow : function(v){
\r
6186 var dom = this.dom;
\r
6187 if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
\r
6188 dom.style.overflow = 'hidden';
\r
6189 (function(){dom.style.overflow = 'auto';}).defer(1);
\r
6191 dom.style.overflow = v;
\r
6196 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
\r
6197 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
\r
6198 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
\r
6199 * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}). The markup
\r
6200 * is of this form:</p>
\r
6202 Ext.Element.boxMarkup =
\r
6203 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
\r
6204 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
\r
6205 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
\r
6207 * <p>Example usage:</p>
\r
6210 Ext.get("foo").boxWrap();
\r
6212 // You can also add a custom class and use CSS inheritance rules to customize the box look.
\r
6213 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
\r
6214 // for how to create a custom box wrap style.
\r
6215 Ext.get("foo").boxWrap().addClass("x-box-blue");
\r
6217 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
\r
6218 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
\r
6219 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
\r
6220 * also supply all of the necessary rules.
\r
6221 * @return {Ext.Element} The outermost wrapping element of the created box structure.
\r
6223 boxWrap : function(cls){
\r
6224 cls = cls || 'x-box';
\r
6225 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)));
\r
6226 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
\r
6231 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
\r
6232 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
\r
6233 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
\r
6234 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
\r
6235 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
\r
6237 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
\r
6238 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
\r
6239 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
\r
6241 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6242 * @return {Ext.Element} this
\r
6244 setSize : function(width, height, animate){
\r
6246 if(Ext.isObject(width)){ // in case of object from getSize()
\r
6247 height = width.height;
\r
6248 width = width.width;
\r
6250 width = me.adjustWidth(width);
\r
6251 height = me.adjustHeight(height);
\r
6252 if(!animate || !me.anim){
\r
6253 me.dom.style.width = me.addUnits(width);
\r
6254 me.dom.style.height = me.addUnits(height);
\r
6256 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
\r
6262 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
\r
6263 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
\r
6264 * if a height has not been set using CSS.
\r
6265 * @return {Number}
\r
6267 getComputedHeight : function(){
\r
6269 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
\r
6271 h = parseFloat(me.getStyle('height')) || 0;
\r
6272 if(!me.isBorderBox()){
\r
6273 h += me.getFrameWidth('tb');
\r
6280 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
\r
6281 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
\r
6282 * if a width has not been set using CSS.
\r
6283 * @return {Number}
\r
6285 getComputedWidth : function(){
\r
6286 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
\r
6288 w = parseFloat(this.getStyle('width')) || 0;
\r
6289 if(!this.isBorderBox()){
\r
6290 w += this.getFrameWidth('lr');
\r
6297 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
\r
6298 for more information about the sides.
\r
6299 * @param {String} sides
\r
6300 * @return {Number}
\r
6302 getFrameWidth : function(sides, onlyContentBox){
\r
6303 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
\r
6307 * Sets up event handlers to add and remove a css class when the mouse is over this element
\r
6308 * @param {String} className
\r
6309 * @return {Ext.Element} this
\r
6311 addClassOnOver : function(className){
\r
6314 Ext.fly(this, INTERNAL).addClass(className);
\r
6317 Ext.fly(this, INTERNAL).removeClass(className);
\r
6324 * Sets up event handlers to add and remove a css class when this element has the focus
\r
6325 * @param {String} className
\r
6326 * @return {Ext.Element} this
\r
6328 addClassOnFocus : function(className){
\r
6329 this.on("focus", function(){
\r
6330 Ext.fly(this, INTERNAL).addClass(className);
\r
6332 this.on("blur", function(){
\r
6333 Ext.fly(this, INTERNAL).removeClass(className);
\r
6339 * 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)
\r
6340 * @param {String} className
\r
6341 * @return {Ext.Element} this
\r
6343 addClassOnClick : function(className){
\r
6344 var dom = this.dom;
\r
6345 this.on("mousedown", function(){
\r
6346 Ext.fly(dom, INTERNAL).addClass(className);
\r
6347 var d = Ext.getDoc(),
\r
6349 Ext.fly(dom, INTERNAL).removeClass(className);
\r
6350 d.removeListener("mouseup", fn);
\r
6352 d.on("mouseup", fn);
\r
6358 * <p>Returns the dimensions of the element available to lay content out in.<p>
\r
6359 * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
\r
6360 * example:<pre><code>
\r
6361 var vpSize = Ext.getBody().getViewSize();
\r
6363 // all Windows created afterwards will have a default value of 90% height and 95% width
\r
6364 Ext.Window.override({
\r
6365 width: vpSize.width * 0.9,
\r
6366 height: vpSize.height * 0.95
\r
6368 // To handle window resizing you would have to hook onto onWindowResize.
\r
6371 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
\r
6372 * To obtain the size including scrollbars, use getStyleSize
\r
6374 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
\r
6377 getViewSize : function(){
\r
6378 var doc = document,
\r
6380 isDoc = (d == doc || d == doc.body);
\r
6382 // If the body, use Ext.lib.Dom
\r
6384 var extdom = Ext.lib.Dom;
\r
6386 width : extdom.getViewWidth(),
\r
6387 height : extdom.getViewHeight()
\r
6390 // Else use clientHeight/clientWidth
\r
6393 width : d.clientWidth,
\r
6394 height : d.clientHeight
\r
6400 * <p>Returns the dimensions of the element available to lay content out in.<p>
\r
6402 * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
\r
6403 * To obtain the size excluding scrollbars, use getViewSize
\r
6405 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
\r
6408 getStyleSize : function(){
\r
6413 isDoc = (d == doc || d == doc.body),
\r
6416 // If the body, use Ext.lib.Dom
\r
6418 var extdom = Ext.lib.Dom;
\r
6420 width : extdom.getViewWidth(),
\r
6421 height : extdom.getViewHeight()
\r
6424 // Use Styles if they are set
\r
6425 if(s.width && s.width != 'auto'){
\r
6426 w = parseFloat(s.width);
\r
6427 if(me.isBorderBox()){
\r
6428 w -= me.getFrameWidth('lr');
\r
6431 // Use Styles if they are set
\r
6432 if(s.height && s.height != 'auto'){
\r
6433 h = parseFloat(s.height);
\r
6434 if(me.isBorderBox()){
\r
6435 h -= me.getFrameWidth('tb');
\r
6438 // Use getWidth/getHeight if style not set.
\r
6439 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
\r
6443 * Returns the size of the element.
\r
6444 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
\r
6445 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
\r
6447 getSize : function(contentSize){
\r
6448 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
\r
6452 * Forces the browser to repaint this element
\r
6453 * @return {Ext.Element} this
\r
6455 repaint : function(){
\r
6456 var dom = this.dom;
\r
6457 this.addClass("x-repaint");
\r
6458 setTimeout(function(){
\r
6459 Ext.fly(dom).removeClass("x-repaint");
\r
6465 * Disables text selection for this element (normalized across browsers)
\r
6466 * @return {Ext.Element} this
\r
6468 unselectable : function(){
\r
6469 this.dom.unselectable = "on";
\r
6470 return this.swallowEvent("selectstart", true).
\r
6471 applyStyles("-moz-user-select:none;-khtml-user-select:none;").
\r
6472 addClass("x-unselectable");
\r
6476 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
\r
6477 * then it returns the calculated width of the sides (see getPadding)
\r
6478 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
\r
6479 * @return {Object/Number}
\r
6481 getMargins : function(side){
\r
6484 hash = {t:"top", l:"left", r:"right", b: "bottom"},
\r
6488 for (key in me.margins){
\r
6489 o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
\r
6493 return me.addStyles.call(me, side, me.margins);
\r
6499 * @class Ext.Element
\r
6502 var D = Ext.lib.Dom,
\r
6506 BOTTOM = "bottom",
\r
6507 POSITION = "position",
\r
6508 STATIC = "static",
\r
6509 RELATIVE = "relative",
\r
6511 ZINDEX = "z-index";
\r
6513 Ext.Element.addMethods({
\r
6515 * 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).
\r
6516 * @return {Number} The X position of the element
\r
6518 getX : function(){
\r
6519 return D.getX(this.dom);
\r
6523 * 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).
\r
6524 * @return {Number} The Y position of the element
\r
6526 getY : function(){
\r
6527 return D.getY(this.dom);
\r
6531 * 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).
\r
6532 * @return {Array} The XY position of the element
\r
6534 getXY : function(){
\r
6535 return D.getXY(this.dom);
\r
6539 * 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.
\r
6540 * @param {Mixed} element The element to get the offsets from.
\r
6541 * @return {Array} The XY page offsets (e.g. [100, -200])
\r
6543 getOffsetsTo : function(el){
\r
6544 var o = this.getXY(),
\r
6545 e = Ext.fly(el, '_internal').getXY();
\r
6546 return [o[0]-e[0],o[1]-e[1]];
\r
6550 * 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).
\r
6551 * @param {Number} The X position of the element
\r
6552 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
6553 * @return {Ext.Element} this
\r
6555 setX : function(x, animate){
\r
6556 return this.setXY([x, this.getY()], this.animTest(arguments, animate, 1));
\r
6560 * 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).
\r
6561 * @param {Number} The Y position of the element
\r
6562 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
6563 * @return {Ext.Element} this
\r
6565 setY : function(y, animate){
\r
6566 return this.setXY([this.getX(), y], this.animTest(arguments, animate, 1));
\r
6570 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
\r
6571 * @param {String} left The left CSS property value
\r
6572 * @return {Ext.Element} this
\r
6574 setLeft : function(left){
\r
6575 this.setStyle(LEFT, this.addUnits(left));
\r
6580 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
\r
6581 * @param {String} top The top CSS property value
\r
6582 * @return {Ext.Element} this
\r
6584 setTop : function(top){
\r
6585 this.setStyle(TOP, this.addUnits(top));
\r
6590 * Sets the element's CSS right style.
\r
6591 * @param {String} right The right CSS property value
\r
6592 * @return {Ext.Element} this
\r
6594 setRight : function(right){
\r
6595 this.setStyle(RIGHT, this.addUnits(right));
\r
6600 * Sets the element's CSS bottom style.
\r
6601 * @param {String} bottom The bottom CSS property value
\r
6602 * @return {Ext.Element} this
\r
6604 setBottom : function(bottom){
\r
6605 this.setStyle(BOTTOM, this.addUnits(bottom));
\r
6610 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
\r
6611 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
\r
6612 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
\r
6613 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
6614 * @return {Ext.Element} this
\r
6616 setXY : function(pos, animate){
\r
6618 if(!animate || !me.anim){
\r
6619 D.setXY(me.dom, pos);
\r
6621 me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');
\r
6627 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
\r
6628 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
\r
6629 * @param {Number} x X value for new position (coordinates are page-based)
\r
6630 * @param {Number} y Y value for new position (coordinates are page-based)
\r
6631 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
6632 * @return {Ext.Element} this
\r
6634 setLocation : function(x, y, animate){
\r
6635 return this.setXY([x, y], this.animTest(arguments, animate, 2));
\r
6639 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
\r
6640 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
\r
6641 * @param {Number} x X value for new position (coordinates are page-based)
\r
6642 * @param {Number} y Y value for new position (coordinates are page-based)
\r
6643 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
6644 * @return {Ext.Element} this
\r
6646 moveTo : function(x, y, animate){
\r
6647 return this.setXY([x, y], this.animTest(arguments, animate, 2));
\r
6651 * Gets the left X coordinate
\r
6652 * @param {Boolean} local True to get the local css position instead of page coordinate
\r
6653 * @return {Number}
\r
6655 getLeft : function(local){
\r
6656 return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
\r
6660 * Gets the right X coordinate of the element (element X position + element width)
\r
6661 * @param {Boolean} local True to get the local css position instead of page coordinate
\r
6662 * @return {Number}
\r
6664 getRight : function(local){
\r
6666 return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
\r
6670 * Gets the top Y coordinate
\r
6671 * @param {Boolean} local True to get the local css position instead of page coordinate
\r
6672 * @return {Number}
\r
6674 getTop : function(local) {
\r
6675 return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
\r
6679 * Gets the bottom Y coordinate of the element (element Y position + element height)
\r
6680 * @param {Boolean} local True to get the local css position instead of page coordinate
\r
6681 * @return {Number}
\r
6683 getBottom : function(local){
\r
6685 return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
\r
6689 * Initializes positioning on this element. If a desired position is not passed, it will make the
\r
6690 * the element positioned relative IF it is not already positioned.
\r
6691 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
\r
6692 * @param {Number} zIndex (optional) The zIndex to apply
\r
6693 * @param {Number} x (optional) Set the page X position
\r
6694 * @param {Number} y (optional) Set the page Y position
\r
6696 position : function(pos, zIndex, x, y){
\r
6699 if(!pos && me.isStyle(POSITION, STATIC)){
\r
6700 me.setStyle(POSITION, RELATIVE);
\r
6702 me.setStyle(POSITION, pos);
\r
6705 me.setStyle(ZINDEX, zIndex);
\r
6707 if(x || y) me.setXY([x || false, y || false]);
\r
6711 * Clear positioning back to the default when the document was loaded
\r
6712 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
\r
6713 * @return {Ext.Element} this
\r
6715 clearPositioning : function(value){
\r
6716 value = value || '';
\r
6729 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
\r
6730 * snapshot before performing an update and then restoring the element.
\r
6731 * @return {Object}
\r
6733 getPositioning : function(){
\r
6734 var l = this.getStyle(LEFT);
\r
6735 var t = this.getStyle(TOP);
\r
6737 "position" : this.getStyle(POSITION),
\r
6739 "right" : l ? "" : this.getStyle(RIGHT),
\r
6741 "bottom" : t ? "" : this.getStyle(BOTTOM),
\r
6742 "z-index" : this.getStyle(ZINDEX)
\r
6747 * Set positioning with an object returned by getPositioning().
\r
6748 * @param {Object} posCfg
\r
6749 * @return {Ext.Element} this
\r
6751 setPositioning : function(pc){
\r
6753 style = me.dom.style;
\r
6757 if(pc.right == AUTO){
\r
6760 if(pc.bottom == AUTO){
\r
6761 style.bottom = "";
\r
6768 * Translates the passed page coordinates into left/top css values for this element
\r
6769 * @param {Number/Array} x The page x or an array containing [x, y]
\r
6770 * @param {Number} y (optional) The page y, required if x is not an array
\r
6771 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
\r
6773 translatePoints : function(x, y){
\r
6774 y = isNaN(x[1]) ? y : x[1];
\r
6775 x = isNaN(x[0]) ? x : x[0];
\r
6777 relative = me.isStyle(POSITION, RELATIVE),
\r
6779 l = parseInt(me.getStyle(LEFT), 10),
\r
6780 t = parseInt(me.getStyle(TOP), 10);
\r
6782 l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
\r
6783 t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);
\r
6785 return {left: (x - o[0] + l), top: (y - o[1] + t)};
\r
6788 animTest : function(args, animate, i) {
\r
6789 return !!animate && this.preanim ? this.preanim(args, i) : false;
\r
6793 * @class Ext.Element
\r
6795 Ext.Element.addMethods({
\r
6797 * 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.
\r
6798 * @param {Object} box The box to fill {x, y, width, height}
\r
6799 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
\r
6800 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6801 * @return {Ext.Element} this
\r
6803 setBox : function(box, adjust, animate){
\r
6807 if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
\r
6808 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
\r
6809 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
\r
6811 me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
\r
6816 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
\r
6817 * set another Element's size/location to match this element.
\r
6818 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
\r
6819 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
\r
6820 * @return {Object} box An object in the format<pre><code>
\r
6822 x: <Element's X position>,
\r
6823 y: <Element's Y position>,
\r
6824 width: <Element's width>,
\r
6825 height: <Element's height>,
\r
6826 bottom: <Element's lower bound>,
\r
6827 right: <Element's rightmost bound>
\r
6830 * The returned object may also be addressed as an Array where index 0 contains the X position
\r
6831 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
\r
6833 getBox : function(contentBox, local) {
\r
6838 getBorderWidth = me.getBorderWidth,
\r
6839 getPadding = me.getPadding,
\r
6847 left = parseInt(me.getStyle("left"), 10) || 0;
\r
6848 top = parseInt(me.getStyle("top"), 10) || 0;
\r
6851 var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
\r
6853 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
\r
6855 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
\r
6856 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
\r
6857 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
\r
6858 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
\r
6859 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)};
\r
6861 bx.right = bx.x + bx.width;
\r
6862 bx.bottom = bx.y + bx.height;
\r
6867 * Move this element relative to its current position.
\r
6868 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
\r
6869 * @param {Number} distance How far to move the element in pixels
\r
6870 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6871 * @return {Ext.Element} this
\r
6873 move : function(direction, distance, animate){
\r
6878 left = [x - distance, y],
\r
6879 right = [x + distance, y],
\r
6880 top = [x, y - distance],
\r
6881 bottom = [x, y + distance],
\r
6895 direction = direction.toLowerCase();
\r
6896 me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
\r
6900 * Quick set left and top adding default units
\r
6901 * @param {String} left The left CSS property value
\r
6902 * @param {String} top The top CSS property value
\r
6903 * @return {Ext.Element} this
\r
6905 setLeftTop : function(left, top){
\r
6907 style = me.dom.style;
\r
6908 style.left = me.addUnits(left);
\r
6909 style.top = me.addUnits(top);
\r
6914 * Returns the region of the given element.
\r
6915 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
\r
6916 * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
\r
6918 getRegion : function(){
\r
6919 return Ext.lib.Dom.getRegion(this.dom);
\r
6923 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
\r
6924 * @param {Number} x X value for new position (coordinates are page-based)
\r
6925 * @param {Number} y Y value for new position (coordinates are page-based)
\r
6926 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
\r
6927 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
\r
6928 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
\r
6930 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
\r
6931 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
\r
6932 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
\r
6934 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6935 * @return {Ext.Element} this
\r
6937 setBounds : function(x, y, width, height, animate){
\r
6939 if (!animate || !me.anim) {
\r
6940 me.setSize(width, height);
\r
6941 me.setLocation(x, y);
\r
6943 me.anim({points: {to: [x, y]},
\r
6944 width: {to: me.adjustWidth(width)},
\r
6945 height: {to: me.adjustHeight(height)}},
\r
6946 me.preanim(arguments, 4),
\r
6953 * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
\r
6954 * @param {Ext.lib.Region} region The region to fill
\r
6955 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6956 * @return {Ext.Element} this
\r
6958 setRegion : function(region, animate) {
\r
6959 return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
\r
6962 * @class Ext.Element
\r
6964 Ext.Element.addMethods({
\r
6966 * Returns true if this element is scrollable.
\r
6967 * @return {Boolean}
\r
6969 isScrollable : function(){
\r
6970 var dom = this.dom;
\r
6971 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
\r
6975 * 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().
\r
6976 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
\r
6977 * @param {Number} value The new scroll value.
\r
6978 * @return {Element} this
\r
6980 scrollTo : function(side, value){
\r
6981 this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;
\r
6986 * Returns the current scroll position of the element.
\r
6987 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
\r
6989 getScroll : function(){
\r
6990 var d = this.dom,
\r
6993 docElement = doc.documentElement,
\r
6998 if(d == doc || d == body){
\r
6999 if(Ext.isIE && Ext.isStrict){
\r
7000 l = docElement.scrollLeft;
\r
7001 t = docElement.scrollTop;
\r
7003 l = window.pageXOffset;
\r
7004 t = window.pageYOffset;
\r
7006 ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
\r
7008 ret = {left: d.scrollLeft, top: d.scrollTop};
\r
7013 * @class Ext.Element
\r
7015 Ext.Element.addMethods({
\r
7017 * 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().
\r
7018 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
\r
7019 * @param {Number} value The new scroll value
\r
7020 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
7021 * @return {Element} this
\r
7023 scrollTo : function(side, value, animate){
\r
7024 var top = /top/i.test(side), //check if we're scrolling top or left
\r
7028 if (!animate || !me.anim) {
\r
7029 prop = 'scroll' + (top ? 'Top' : 'Left'), // just setting the value, so grab the direction
\r
7030 dom[prop] = value;
\r
7032 prop = 'scroll' + (top ? 'Left' : 'Top'), // if scrolling top, we need to grab scrollLeft, if left, scrollTop
\r
7033 me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}},
\r
7034 me.preanim(arguments, 2), 'scroll');
\r
7040 * Scrolls this element into view within the passed container.
\r
7041 * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a
\r
7042 * string (id), dom node, or Ext.Element.
\r
7043 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
\r
7044 * @return {Ext.Element} this
\r
7046 scrollIntoView : function(container, hscroll){
\r
7047 var c = Ext.getDom(container) || Ext.getBody().dom,
\r
7049 o = this.getOffsetsTo(c),
\r
7050 l = o[0] + c.scrollLeft,
\r
7051 t = o[1] + c.scrollTop,
\r
7052 b = t + el.offsetHeight,
\r
7053 r = l + el.offsetWidth,
\r
7054 ch = c.clientHeight,
\r
7055 ct = parseInt(c.scrollTop, 10),
\r
7056 cl = parseInt(c.scrollLeft, 10),
\r
7058 cr = cl + c.clientWidth;
\r
7060 if (el.offsetHeight > ch || t < ct) {
\r
7062 } else if (b > cb){
\r
7063 c.scrollTop = b-ch;
\r
7065 c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore
\r
7067 if(hscroll !== false){
\r
7068 if(el.offsetWidth > c.clientWidth || l < cl){
\r
7071 c.scrollLeft = r - c.clientWidth;
\r
7073 c.scrollLeft = c.scrollLeft;
\r
7079 scrollChildIntoView : function(child, hscroll){
\r
7080 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
\r
7084 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
\r
7085 * within this element's scrollable range.
\r
7086 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
\r
7087 * @param {Number} distance How far to scroll the element in pixels
\r
7088 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
7089 * @return {Boolean} Returns true if a scroll was triggered or false if the element
\r
7090 * was scrolled as far as it could go.
\r
7092 scroll : function(direction, distance, animate){
\r
7093 if(!this.isScrollable()){
\r
7096 var el = this.dom,
\r
7097 l = el.scrollLeft, t = el.scrollTop,
\r
7098 w = el.scrollWidth, h = el.scrollHeight,
\r
7099 cw = el.clientWidth, ch = el.clientHeight,
\r
7100 scrolled = false, v,
\r
7102 l: Math.min(l + distance, w-cw),
\r
7103 r: v = Math.max(l - distance, 0),
\r
7104 t: Math.max(t - distance, 0),
\r
7105 b: Math.min(t + distance, h-ch)
\r
7110 direction = direction.substr(0, 1);
\r
7111 if((v = hash[direction]) > -1){
\r
7113 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
\r
7118 * @class Ext.Element
\r
7121 * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
\r
7125 Ext.Element.VISIBILITY = 1;
\r
7127 * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
\r
7131 Ext.Element.DISPLAY = 2;
\r
7133 Ext.Element.addMethods(function(){
\r
7134 var VISIBILITY = "visibility",
\r
7135 DISPLAY = "display",
\r
7136 HIDDEN = "hidden",
\r
7138 ORIGINALDISPLAY = 'originalDisplay',
\r
7139 VISMODE = 'visibilityMode',
\r
7140 ELDISPLAY = Ext.Element.DISPLAY,
\r
7141 data = Ext.Element.data,
\r
7142 getDisplay = function(dom){
\r
7143 var d = data(dom, ORIGINALDISPLAY);
\r
7144 if(d === undefined){
\r
7145 data(dom, ORIGINALDISPLAY, d = '');
\r
7149 getVisMode = function(dom){
\r
7150 var m = data(dom, VISMODE);
\r
7151 if(m === undefined){
\r
7152 data(dom, VISMODE, m = 1)
\r
7159 * The element's default display mode (defaults to "")
\r
7162 originalDisplay : "",
\r
7163 visibilityMode : 1,
\r
7166 * Sets the element's visibility mode. When setVisible() is called it
\r
7167 * will use this to determine whether to set the visibility or the display property.
\r
7168 * @param {Number} visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
\r
7169 * @return {Ext.Element} this
\r
7171 setVisibilityMode : function(visMode){
\r
7172 data(this.dom, VISMODE, visMode);
\r
7177 * Perform custom animation on this element.
\r
7178 * <div><ul class="mdetail-params">
\r
7179 * <li><u>Animation Properties</u></li>
\r
7181 * <p>The Animation Control Object enables gradual transitions for any member of an
\r
7182 * element's style object that takes a numeric value including but not limited to
\r
7183 * these properties:</p><div><ul class="mdetail-params">
\r
7184 * <li><tt>bottom, top, left, right</tt></li>
\r
7185 * <li><tt>height, width</tt></li>
\r
7186 * <li><tt>margin, padding</tt></li>
\r
7187 * <li><tt>borderWidth</tt></li>
\r
7188 * <li><tt>opacity</tt></li>
\r
7189 * <li><tt>fontSize</tt></li>
\r
7190 * <li><tt>lineHeight</tt></li>
\r
7194 * <li><u>Animation Property Attributes</u></li>
\r
7196 * <p>Each Animation Property is a config object with optional properties:</p>
\r
7197 * <div><ul class="mdetail-params">
\r
7198 * <li><tt>by</tt>* : relative change - start at current value, change by this value</li>
\r
7199 * <li><tt>from</tt> : ignore current value, start from this value</li>
\r
7200 * <li><tt>to</tt>* : start at current value, go to this value</li>
\r
7201 * <li><tt>unit</tt> : any allowable unit specification</li>
\r
7202 * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>
\r
7205 * <li><u>Animation Types</u></li>
\r
7207 * <p>The supported animation types:</p><div><ul class="mdetail-params">
\r
7208 * <li><tt>'run'</tt> : Default
\r
7210 var el = Ext.get('complexEl');
\r
7212 // animation control object
\r
7214 borderWidth: {to: 3, from: 0},
\r
7215 opacity: {to: .3, from: 1},
\r
7216 height: {to: 50, from: el.getHeight()},
\r
7217 width: {to: 300, from: el.getWidth()},
\r
7218 top : {by: - 100, unit: 'px'},
\r
7220 0.35, // animation duration
\r
7222 'easeOut', // easing method
\r
7223 'run' // animation type ('run','color','motion','scroll')
\r
7227 * <li><tt>'color'</tt>
\r
7228 * <p>Animates transition of background, text, or border colors.</p>
\r
7231 // animation control object
\r
7233 color: { to: '#06e' },
\r
7234 backgroundColor: { to: '#e06' }
\r
7236 0.35, // animation duration
\r
7238 'easeOut', // easing method
\r
7239 'color' // animation type ('run','color','motion','scroll')
\r
7244 * <li><tt>'motion'</tt>
\r
7245 * <p>Animates the motion of an element to/from specific points using optional bezier
\r
7246 * way points during transit.</p>
\r
7249 // animation control object
\r
7251 borderWidth: {to: 3, from: 0},
\r
7252 opacity: {to: .3, from: 1},
\r
7253 height: {to: 50, from: el.getHeight()},
\r
7254 width: {to: 300, from: el.getWidth()},
\r
7255 top : {by: - 100, unit: 'px'},
\r
7257 to: [50, 100], // go to this point
\r
7258 control: [ // optional bezier way points
\r
7264 3000, // animation duration (milliseconds!)
\r
7266 'easeOut', // easing method
\r
7267 'motion' // animation type ('run','color','motion','scroll')
\r
7271 * <li><tt>'scroll'</tt>
\r
7272 * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>
\r
7275 // animation control object
\r
7277 scroll: {to: [400, 300]}
\r
7279 0.35, // animation duration
\r
7281 'easeOut', // easing method
\r
7282 'scroll' // animation type ('run','color','motion','scroll')
\r
7290 * @param {Object} args The animation control args
\r
7291 * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)
\r
7292 * @param {Function} onComplete (optional) Function to call when animation completes
\r
7293 * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)
\r
7294 * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,
\r
7295 * <tt>'motion'</tt>, or <tt>'scroll'</tt>
\r
7296 * @return {Ext.Element} this
\r
7298 animate : function(args, duration, onComplete, easing, animType){
\r
7299 this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
\r
7304 * @private Internal animation call
\r
7306 anim : function(args, opt, animType, defaultDur, defaultEase, cb){
\r
7307 animType = animType || 'run';
\r
7310 anim = Ext.lib.Anim[animType](
\r
7313 (opt.duration || defaultDur) || .35,
\r
7314 (opt.easing || defaultEase) || 'easeOut',
\r
7316 if(cb) cb.call(me);
\r
7317 if(opt.callback) opt.callback.call(opt.scope || me, me, opt);
\r
7325 // private legacy anim prep
\r
7326 preanim : function(a, i){
\r
7327 return !a[i] ? false : (Ext.isObject(a[i]) ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
\r
7331 * Checks whether the element is currently visible using both visibility and display properties.
\r
7332 * @return {Boolean} True if the element is currently visible, else false
\r
7334 isVisible : function() {
\r
7335 return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);
\r
7339 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
\r
7340 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
\r
7341 * @param {Boolean} visible Whether the element is visible
\r
7342 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
7343 * @return {Ext.Element} this
\r
7345 setVisible : function(visible, animate){
\r
7348 isDisplay = getVisMode(this.dom) == ELDISPLAY;
\r
7350 if (!animate || !me.anim) {
\r
7352 me.setDisplayed(visible);
\r
7355 dom.style.visibility = visible ? "visible" : HIDDEN;
\r
7358 // closure for composites
\r
7360 me.setOpacity(.01);
\r
7361 me.setVisible(true);
\r
7363 me.anim({opacity: { to: (visible?1:0) }},
\r
7364 me.preanim(arguments, 1),
\r
7370 dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;
\r
7371 Ext.fly(dom).setOpacity(1);
\r
7379 * Toggles the element's visibility or display, depending on visibility mode.
\r
7380 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
7381 * @return {Ext.Element} this
\r
7383 toggle : function(animate){
\r
7385 me.setVisible(!me.isVisible(), me.preanim(arguments, 0));
\r
7390 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
\r
7391 * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
\r
7392 * @return {Ext.Element} this
\r
7394 setDisplayed : function(value) {
\r
7395 if(typeof value == "boolean"){
\r
7396 value = value ? getDisplay(this.dom) : NONE;
\r
7398 this.setStyle(DISPLAY, value);
\r
7403 fixDisplay : function(){
\r
7405 if(me.isStyle(DISPLAY, NONE)){
\r
7406 me.setStyle(VISIBILITY, HIDDEN);
\r
7407 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
\r
7408 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
\r
7409 me.setStyle(DISPLAY, "block");
\r
7415 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
\r
7416 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
7417 * @return {Ext.Element} this
\r
7419 hide : function(animate){
\r
7420 this.setVisible(false, this.preanim(arguments, 0));
\r
7425 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
\r
7426 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
7427 * @return {Ext.Element} this
\r
7429 show : function(animate){
\r
7430 this.setVisible(true, this.preanim(arguments, 0));
\r
7435 * @class Ext.Element
\r
7437 Ext.Element.addMethods(
\r
7439 var VISIBILITY = "visibility",
\r
7440 DISPLAY = "display",
\r
7441 HIDDEN = "hidden",
\r
7443 XMASKED = "x-masked",
\r
7444 XMASKEDRELATIVE = "x-masked-relative",
\r
7445 data = Ext.Element.data;
\r
7449 * Checks whether the element is currently visible using both visibility and display properties.
\r
7450 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
\r
7451 * @return {Boolean} True if the element is currently visible, else false
\r
7453 isVisible : function(deep) {
\r
7454 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),
\r
7455 p = this.dom.parentNode;
\r
7456 if(deep !== true || !vis){
\r
7459 while(p && !/^body/i.test(p.tagName)){
\r
7460 if(!Ext.fly(p, '_isVisible').isVisible()){
\r
7469 * Returns true if display is not "none"
\r
7470 * @return {Boolean}
\r
7472 isDisplayed : function() {
\r
7473 return !this.isStyle(DISPLAY, NONE);
\r
7477 * Convenience method for setVisibilityMode(Element.DISPLAY)
\r
7478 * @param {String} display (optional) What to set display to when visible
\r
7479 * @return {Ext.Element} this
\r
7481 enableDisplayMode : function(display){
\r
7482 this.setVisibilityMode(Ext.Element.DISPLAY);
\r
7483 if(!Ext.isEmpty(display)){
\r
7484 data(this.dom, 'originalDisplay', display);
\r
7490 * Puts a mask over this element to disable user interaction. Requires core.css.
\r
7491 * This method can only be applied to elements which accept child nodes.
\r
7492 * @param {String} msg (optional) A message to display in the mask
\r
7493 * @param {String} msgCls (optional) A css class to apply to the msg element
\r
7494 * @return {Element} The mask element
\r
7496 mask : function(msg, msgCls){
\r
7499 dh = Ext.DomHelper,
\r
7500 EXTELMASKMSG = "ext-el-mask-msg",
\r
7504 if(me.getStyle("position") == "static"){
\r
7505 me.addClass(XMASKEDRELATIVE);
\r
7507 if((el = data(dom, 'maskMsg'))){
\r
7510 if((el = data(dom, 'mask'))){
\r
7514 mask = dh.append(dom, {cls : "ext-el-mask"}, true);
\r
7515 data(dom, 'mask', mask);
\r
7517 me.addClass(XMASKED);
\r
7518 mask.setDisplayed(true);
\r
7519 if(typeof msg == 'string'){
\r
7520 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
\r
7521 data(dom, 'maskMsg', mm);
\r
7522 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
\r
7523 mm.dom.firstChild.innerHTML = msg;
\r
7524 mm.setDisplayed(true);
\r
7527 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically
\r
7528 mask.setSize(undefined, me.getHeight());
\r
7534 * Removes a previously applied mask.
\r
7536 unmask : function(){
\r
7539 mask = data(dom, 'mask'),
\r
7540 maskMsg = data(dom, 'maskMsg');
\r
7544 data(dom, 'maskMsg', undefined);
\r
7547 data(dom, 'mask', undefined);
\r
7549 me.removeClass([XMASKED, XMASKEDRELATIVE]);
\r
7553 * Returns true if this element is masked
\r
7554 * @return {Boolean}
\r
7556 isMasked : function(){
\r
7557 var m = data(this.dom, 'mask');
\r
7558 return m && m.isVisible();
\r
7562 * Creates an iframe shim for this element to keep selects and other windowed objects from
\r
7563 * showing through.
\r
7564 * @return {Ext.Element} The new shim element
\r
7566 createShim : function(){
\r
7567 var el = document.createElement('iframe'),
\r
7569 el.frameBorder = '0';
\r
7570 el.className = 'ext-shim';
\r
7571 el.src = Ext.SSL_SECURE_URL;
\r
7572 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
\r
7573 shim.autoBoxAdjust = false;
\r
7578 * @class Ext.Element
\r
7580 Ext.Element.addMethods({
\r
7582 * Convenience method for constructing a KeyMap
\r
7583 * @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:
\r
7584 * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
\r
7585 * @param {Function} fn The function to call
\r
7586 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
\r
7587 * @return {Ext.KeyMap} The KeyMap created
\r
7589 addKeyListener : function(key, fn, scope){
\r
7591 if(!Ext.isObject(key) || Ext.isArray(key)){
\r
7600 shift : key.shift,
\r
7607 return new Ext.KeyMap(this, config);
\r
7611 * Creates a KeyMap for this element
\r
7612 * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
\r
7613 * @return {Ext.KeyMap} The KeyMap created
\r
7615 addKeyMap : function(config){
\r
7616 return new Ext.KeyMap(this, config);
\r
7621 UNDEFINED = undefined,
\r
7628 BOTTOM = "bottom",
\r
7631 HEIGHT = "height",
\r
7633 POINTS = "points",
\r
7634 HIDDEN = "hidden",
\r
7635 ABSOLUTE = "absolute",
\r
7636 VISIBLE = "visible",
\r
7637 MOTION = "motion",
\r
7638 POSITION = "position",
\r
7639 EASEOUT = "easeOut",
\r
7641 * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element
\r
7643 flyEl = new Ext.Element.Flyweight(),
\r
7645 getObject = function(o){
\r
7648 fly = function(dom){
\r
7650 flyEl.id = Ext.id(dom);
\r
7654 * Queueing now stored outside of the element due to closure issues
\r
7656 getQueue = function(id){
\r
7660 return queues[id];
\r
7662 setQueue = function(id, value){
\r
7663 queues[id] = value;
\r
7666 //Notifies Element that fx methods are available
\r
7667 Ext.enableFx = TRUE;
\r
7671 * <p>A class to provide basic animation and visual effects support. <b>Note:</b> This class is automatically applied
\r
7672 * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.
\r
7673 * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be
\r
7674 * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>
\r
7676 * <p><b><u>Method Chaining</u></b></p>
\r
7677 * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
\r
7678 * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
\r
7679 * method chain. The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
\r
7680 * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately. For this reason,
\r
7681 * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
\r
7682 * expected results and should be done with care. Also see <tt>{@link #callback}</tt>.</p><br/>
\r
7684 * <p><b><u>Anchor Options for Motion Effects</u></b></p>
\r
7685 * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
\r
7686 * that will serve as either the start or end point of the animation. Following are all of the supported anchor positions:</p>
\r
7689 ----- -----------------------------
\r
7690 tl The top left corner
\r
7691 t The center of the top edge
\r
7692 tr The top right corner
\r
7693 l The center of the left edge
\r
7694 r The center of the right edge
\r
7695 bl The bottom left corner
\r
7696 b The center of the bottom edge
\r
7697 br The bottom right corner
\r
7699 * <b>Note</b>: some Fx methods accept specific custom config parameters. The options shown in the Config Options
\r
7700 * section below are common options that can be passed to any Fx method unless otherwise noted.</b>
\r
7702 * @cfg {Function} callback A function called when the effect is finished. Note that effects are queued internally by the
\r
7703 * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together
\r
7704 * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>
\r
7705 * el.slideIn().highlight();
\r
7707 * The callback is intended for any additional code that should run once a particular effect has completed. The Element
\r
7708 * being operated upon is passed as the first parameter.
\r
7710 * @cfg {Object} scope The scope (<code>this</code> reference) in which the <tt>{@link #callback}</tt> function is executed. Defaults to the browser window.
\r
7712 * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>
\r
7713 * <li><b><tt>backBoth</tt></b></li>
\r
7714 * <li><b><tt>backIn</tt></b></li>
\r
7715 * <li><b><tt>backOut</tt></b></li>
\r
7716 * <li><b><tt>bounceBoth</tt></b></li>
\r
7717 * <li><b><tt>bounceIn</tt></b></li>
\r
7718 * <li><b><tt>bounceOut</tt></b></li>
\r
7719 * <li><b><tt>easeBoth</tt></b></li>
\r
7720 * <li><b><tt>easeBothStrong</tt></b></li>
\r
7721 * <li><b><tt>easeIn</tt></b></li>
\r
7722 * <li><b><tt>easeInStrong</tt></b></li>
\r
7723 * <li><b><tt>easeNone</tt></b></li>
\r
7724 * <li><b><tt>easeOut</tt></b></li>
\r
7725 * <li><b><tt>easeOutStrong</tt></b></li>
\r
7726 * <li><b><tt>elasticBoth</tt></b></li>
\r
7727 * <li><b><tt>elasticIn</tt></b></li>
\r
7728 * <li><b><tt>elasticOut</tt></b></li>
\r
7731 * @cfg {String} afterCls A css class to apply after the effect
\r
7732 * @cfg {Number} duration The length of time (in seconds) that the effect should last
\r
7734 * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between
\r
7735 * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.
\r
7737 * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
\r
7738 * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to
\r
7739 * effects that end with the element being visually hidden, ignored otherwise)
\r
7740 * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object
\r
7741 * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the
\r
7742 * Element after the effect finishes.
\r
7743 * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
\r
7744 * @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
\r
7745 * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)
\r
7749 // private - calls the function taking arguments from the argHash based on the key. Returns the return value of the function.
\r
7750 // this is useful for replacing switch statements (for example).
\r
7751 switchStatements : function(key, fn, argHash){
\r
7752 return fn.apply(this, argHash[key]);
\r
7756 * Slides the element into view. An anchor point can be optionally passed to set the point of
\r
7757 * origin for the slide effect. This function automatically handles wrapping the element with
\r
7758 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
\r
7761 // default: slide the element in from the top
\r
7764 // custom: slide the element in from the right with a 2-second duration
\r
7765 el.slideIn('r', { duration: 2 });
\r
7767 // common config options shown with default values
\r
7769 easing: 'easeOut',
\r
7773 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
\r
7774 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7775 * @return {Ext.Element} The Element
\r
7777 slideIn : function(anchor, o){
\r
7793 anchor = anchor || "t";
\r
7795 me.queueFx(o, function(){
\r
7796 xy = fly(dom).getXY();
\r
7797 // fix display to visibility
\r
7798 fly(dom).fixDisplay();
\r
7800 // restore values after effect
\r
7801 r = fly(dom).getFxRestore();
\r
7802 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
\r
7803 b.right = b.x + b.width;
\r
7804 b.bottom = b.y + b.height;
\r
7806 // fixed size for slide
\r
7807 fly(dom).setWidth(b.width).setHeight(b.height);
\r
7810 wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);
\r
7812 st.visibility = VISIBLE;
\r
7813 st.position = ABSOLUTE;
\r
7815 // clear out temp styles after slide and unwrap
\r
7817 fly(dom).fxUnwrap(wrap, r.pos, o);
\r
7818 st.width = r.width;
\r
7819 st.height = r.height;
\r
7820 fly(dom).afterFx(o);
\r
7823 // time to calculate the positions
\r
7824 pt = {to: [b.x, b.y]};
\r
7825 bw = {to: b.width};
\r
7826 bh = {to: b.height};
\r
7828 function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){
\r
7830 fly(wrap).setWidth(ww).setHeight(wh);
\r
7831 if(fly(wrap)[sXY]){
\r
7832 fly(wrap)[sXY](sXYval);
\r
7834 style[s1] = style[s2] = "0";
\r
7847 args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
\r
7848 t : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],
\r
7849 l : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],
\r
7850 r : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],
\r
7851 b : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],
\r
7852 tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],
\r
7853 bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],
\r
7854 br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],
\r
7855 tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]
\r
7858 st.visibility = VISIBLE;
\r
7861 arguments.callee.anim = fly(wrap).fxanim(args,
\r
7872 * Slides the element out of view. An anchor point can be optionally passed to set the end point
\r
7873 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
\r
7874 * 'hidden') but block elements will still take up space in the document. The element must be removed
\r
7875 * from the DOM using the 'remove' config option if desired. This function automatically handles
\r
7876 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
\r
7879 // default: slide the element out to the top
\r
7882 // custom: slide the element out to the right with a 2-second duration
\r
7883 el.slideOut('r', { duration: 2 });
\r
7885 // common config options shown with default values
\r
7886 el.slideOut('t', {
\r
7887 easing: 'easeOut',
\r
7893 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
\r
7894 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7895 * @return {Ext.Element} The Element
\r
7897 slideOut : function(anchor, o){
\r
7909 anchor = anchor || "t";
\r
7911 me.queueFx(o, function(){
\r
7913 // restore values after effect
\r
7914 r = fly(dom).getFxRestore();
\r
7915 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
\r
7916 b.right = b.x + b.width;
\r
7917 b.bottom = b.y + b.height;
\r
7919 // fixed size for slide
\r
7920 fly(dom).setWidth(b.width).setHeight(b.height);
\r
7923 wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);
\r
7925 st.visibility = VISIBLE;
\r
7926 st.position = ABSOLUTE;
\r
7927 fly(wrap).setWidth(b.width).setHeight(b.height);
\r
7930 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
\r
7931 fly(dom).fxUnwrap(wrap, r.pos, o);
\r
7932 st.width = r.width;
\r
7933 st.height = r.height;
\r
7934 fly(dom).afterFx(o);
\r
7937 function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){
\r
7940 style[s1] = style[s2] = "0";
\r
7952 a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
\r
7953 t : [st, LEFT, BOTTOM, HEIGHT, zero],
\r
7954 l : [st, RIGHT, TOP, WIDTH, zero],
\r
7955 r : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],
\r
7956 b : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
\r
7957 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],
\r
7958 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
\r
7959 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],
\r
7960 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]
\r
7963 arguments.callee.anim = fly(wrap).fxanim(a,
\r
7974 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
\r
7975 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
\r
7976 * The element must be removed from the DOM using the 'remove' config option if desired.
\r
7982 // common config options shown with default values
\r
7984 easing: 'easeOut',
\r
7990 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7991 * @return {Ext.Element} The Element
\r
7993 puff : function(o){
\r
8002 me.queueFx(o, function(){
\r
8003 width = fly(dom).getWidth();
\r
8004 height = fly(dom).getHeight();
\r
8005 fly(dom).clearOpacity();
\r
8008 // restore values after effect
\r
8009 r = fly(dom).getFxRestore();
\r
8012 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
\r
8013 fly(dom).clearOpacity();
\r
8014 fly(dom).setPositioning(r.pos);
\r
8015 st.width = r.width;
\r
8016 st.height = r.height;
\r
8018 fly(dom).afterFx(o);
\r
8021 arguments.callee.anim = fly(dom).fxanim({
\r
8022 width : {to : fly(dom).adjustWidth(width * 2)},
\r
8023 height : {to : fly(dom).adjustHeight(height * 2)},
\r
8024 points : {by : [-width * .5, -height * .5]},
\r
8025 opacity : {to : 0},
\r
8026 fontSize: {to : 200, unit: "%"}
\r
8038 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
\r
8039 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
\r
8040 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
\r
8046 // all config options shown with default values
\r
8054 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
8055 * @return {Ext.Element} The Element
\r
8057 switchOff : function(o){
\r
8064 me.queueFx(o, function(){
\r
8065 fly(dom).clearOpacity();
\r
8068 // restore values after effect
\r
8069 r = fly(dom).getFxRestore();
\r
8072 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
\r
8073 fly(dom).clearOpacity();
\r
8074 fly(dom).setPositioning(r.pos);
\r
8075 st.width = r.width;
\r
8076 st.height = r.height;
\r
8077 fly(dom).afterFx(o);
\r
8080 fly(dom).fxanim({opacity : {to : 0.3}},
\r
8086 fly(dom).clearOpacity();
\r
8089 height : {to : 1},
\r
8090 points : {by : [0, fly(dom).getHeight() * .5]}
\r
8104 * Highlights the Element by setting a color (applies to the background-color by default, but can be
\r
8105 * changed using the "attr" config option) and then fading back to the original color. If no original
\r
8106 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
\r
8109 // default: highlight background to yellow
\r
8112 // custom: highlight foreground text to blue for 2 seconds
\r
8113 el.highlight("0000ff", { attr: 'color', duration: 2 });
\r
8115 // common config options shown with default values
\r
8116 el.highlight("ffff9c", {
\r
8117 attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
\r
8118 endColor: (current color) or "ffffff",
\r
8123 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
\r
8124 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
8125 * @return {Ext.Element} The Element
\r
8127 highlight : function(color, o){
\r
8131 attr = o.attr || "backgroundColor",
\r
8135 me.queueFx(o, function(){
\r
8136 fly(dom).clearOpacity();
\r
8140 dom.style[attr] = restore;
\r
8141 fly(dom).afterFx(o);
\r
8143 restore = dom.style[attr];
\r
8144 a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};
\r
8145 arguments.callee.anim = fly(dom).fxanim(a,
\r
8156 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
\r
8159 // default: a single light blue ripple
\r
8162 // custom: 3 red ripples lasting 3 seconds total
\r
8163 el.frame("ff0000", 3, { duration: 3 });
\r
8165 // common config options shown with default values
\r
8166 el.frame("C3DAF9", 1, {
\r
8167 duration: 1 //duration of each individual ripple.
\r
8168 // Note: Easing is not configurable and will be ignored if included
\r
8171 * @param {String} color (optional) The color of the border. Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
\r
8172 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
\r
8173 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
8174 * @return {Ext.Element} The Element
\r
8176 frame : function(color, count, o){
\r
8183 me.queueFx(o, function(){
\r
8184 color = color || '#C3DAF9';
\r
8185 if(color.length == 6){
\r
8186 color = '#' + color;
\r
8188 count = count || 1;
\r
8191 var xy = fly(dom).getXY(),
\r
8192 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},
\r
8193 queue = function(){
\r
8194 proxy = fly(document.body || document.documentElement).createChild({
\r
8196 position : ABSOLUTE,
\r
8197 'z-index': 35000, // yee haw
\r
8198 border : '0px solid ' + color
\r
8201 return proxy.queueFx({}, animFn);
\r
8205 arguments.callee.anim = {
\r
8207 stop: function() {
\r
8213 function animFn(){
\r
8214 var scale = Ext.isBorderBox ? 2 : 1;
\r
8215 active = proxy.anim({
\r
8216 top : {from : b.y, to : b.y - 20},
\r
8217 left : {from : b.x, to : b.x - 20},
\r
8218 borderWidth : {from : 0, to : 10},
\r
8219 opacity : {from : 1, to : 0},
\r
8220 height : {from : b.height, to : b.height + 20 * scale},
\r
8221 width : {from : b.width, to : b.width + 20 * scale}
\r
8223 duration: o.duration || 1,
\r
8224 callback: function() {
\r
8226 --count > 0 ? queue() : fly(dom).afterFx(o);
\r
8229 arguments.callee.anim = {
\r
8242 * Creates a pause before any subsequent queued effects begin. If there are
\r
8243 * no effects queued after the pause it will have no effect.
\r
8248 * @param {Number} seconds The length of time to pause (in seconds)
\r
8249 * @return {Ext.Element} The Element
\r
8251 pause : function(seconds){
\r
8252 var dom = this.dom,
\r
8255 this.queueFx({}, function(){
\r
8256 t = setTimeout(function(){
\r
8257 fly(dom).afterFx({});
\r
8258 }, seconds * 1000);
\r
8259 arguments.callee.anim = {
\r
8263 fly(dom).afterFx({});
\r
8271 * Fade an element in (from transparent to opaque). The ending opacity can be specified
\r
8272 * using the <tt>{@link #endOpacity}</tt> config option.
\r
8275 // default: fade in from opacity 0 to 100%
\r
8278 // custom: fade in from opacity 0 to 75% over 2 seconds
\r
8279 el.fadeIn({ endOpacity: .75, duration: 2});
\r
8281 // common config options shown with default values
\r
8283 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
\r
8284 easing: 'easeOut',
\r
8288 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
8289 * @return {Ext.Element} The Element
\r
8291 fadeIn : function(o){
\r
8295 to = o.endOpacity || 1;
\r
8297 me.queueFx(o, function(){
\r
8298 fly(dom).setOpacity(0);
\r
8299 fly(dom).fixDisplay();
\r
8300 dom.style.visibility = VISIBLE;
\r
8301 arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},
\r
8302 o, NULL, .5, EASEOUT, function(){
\r
8304 fly(dom).clearOpacity();
\r
8306 fly(dom).afterFx(o);
\r
8313 * Fade an element out (from opaque to transparent). The ending opacity can be specified
\r
8314 * using the <tt>{@link #endOpacity}</tt> config option. Note that IE may require
\r
8315 * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
\r
8318 // default: fade out from the element's current opacity to 0
\r
8321 // custom: fade out from the element's current opacity to 25% over 2 seconds
\r
8322 el.fadeOut({ endOpacity: .25, duration: 2});
\r
8324 // common config options shown with default values
\r
8326 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
\r
8327 easing: 'easeOut',
\r
8333 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
8334 * @return {Ext.Element} The Element
\r
8336 fadeOut : function(o){
\r
8340 style = dom.style,
\r
8341 to = o.endOpacity || 0;
\r
8343 me.queueFx(o, function(){
\r
8344 arguments.callee.anim = fly(dom).fxanim({
\r
8345 opacity : {to : to}},
\r
8352 Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ?
\r
8353 style.display = "none" :
\r
8354 style.visibility = HIDDEN;
\r
8356 fly(dom).clearOpacity();
\r
8358 fly(dom).afterFx(o);
\r
8365 * Animates the transition of an element's dimensions from a starting height/width
\r
8366 * to an ending height/width. This method is a convenience implementation of {@link shift}.
\r
8369 // change height and width to 100x100 pixels
\r
8370 el.scale(100, 100);
\r
8372 // common config options shown with default values. The height and width will default to
\r
8373 // the element's existing values if passed as null.
\r
8375 [element's width],
\r
8376 [element's height], {
\r
8377 easing: 'easeOut',
\r
8382 * @param {Number} width The new width (pass undefined to keep the original width)
\r
8383 * @param {Number} height The new height (pass undefined to keep the original height)
\r
8384 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
8385 * @return {Ext.Element} The Element
\r
8387 scale : function(w, h, o){
\r
8388 this.shift(Ext.apply({}, o, {
\r
8396 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
\r
8397 * Any of these properties not specified in the config object will not be changed. This effect
\r
8398 * requires that at least one new dimension, position or opacity setting must be passed in on
\r
8399 * the config object in order for the function to have any effect.
\r
8402 // slide the element horizontally to x position 200 while changing the height and opacity
\r
8403 el.shift({ x: 200, height: 50, opacity: .8 });
\r
8405 // common config options shown with default values.
\r
8407 width: [element's width],
\r
8408 height: [element's height],
\r
8409 x: [element's x position],
\r
8410 y: [element's y position],
\r
8411 opacity: [element's opacity],
\r
8412 easing: 'easeOut',
\r
8416 * @param {Object} options Object literal with any of the Fx config options
\r
8417 * @return {Ext.Element} The Element
\r
8419 shift : function(o){
\r
8421 var dom = this.dom,
\r
8424 this.queueFx(o, function(){
\r
8425 for (var prop in o) {
\r
8426 if (o[prop] != UNDEFINED) {
\r
8427 a[prop] = {to : o[prop]};
\r
8431 a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;
\r
8432 a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;
\r
8434 if (a.x || a.y || a.xy) {
\r
8435 a.points = a.xy ||
\r
8436 {to : [ a.x ? a.x.to : fly(dom).getX(),
\r
8437 a.y ? a.y.to : fly(dom).getY()]};
\r
8440 arguments.callee.anim = fly(dom).fxanim(a,
\r
8446 fly(dom).afterFx(o);
\r
8453 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
\r
8454 * ending point of the effect.
\r
8457 // default: slide the element downward while fading out
\r
8460 // custom: slide the element out to the right with a 2-second duration
\r
8461 el.ghost('r', { duration: 2 });
\r
8463 // common config options shown with default values
\r
8465 easing: 'easeOut',
\r
8471 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
\r
8472 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
8473 * @return {Ext.Element} The Element
\r
8475 ghost : function(anchor, o){
\r
8480 a = {opacity: {to: 0}, points: {}},
\r
8486 anchor = anchor || "b";
\r
8488 me.queueFx(o, function(){
\r
8489 // restore values after effect
\r
8490 r = fly(dom).getFxRestore();
\r
8491 w = fly(dom).getWidth();
\r
8492 h = fly(dom).getHeight();
\r
8495 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
\r
8496 fly(dom).clearOpacity();
\r
8497 fly(dom).setPositioning(r.pos);
\r
8498 st.width = r.width;
\r
8499 st.height = r.height;
\r
8500 fly(dom).afterFx(o);
\r
8503 pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {
\r
8514 arguments.callee.anim = fly(dom).fxanim(a,
\r
8524 * Ensures that all effects queued after syncFx is called on the element are
\r
8525 * run concurrently. This is the opposite of {@link #sequenceFx}.
\r
8526 * @return {Ext.Element} The Element
\r
8528 syncFx : function(){
\r
8530 me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
\r
8532 concurrent : TRUE,
\r
8539 * Ensures that all effects queued after sequenceFx is called on the element are
\r
8540 * run in sequence. This is the opposite of {@link #syncFx}.
\r
8541 * @return {Ext.Element} The Element
\r
8543 sequenceFx : function(){
\r
8545 me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
\r
8547 concurrent : FALSE,
\r
8554 nextFx : function(){
\r
8555 var ef = getQueue(this.dom.id)[0];
\r
8562 * Returns true if the element has any effects actively running or queued, else returns false.
\r
8563 * @return {Boolean} True if element has active effects, else false
\r
8565 hasActiveFx : function(){
\r
8566 return getQueue(this.dom.id)[0];
\r
8570 * Stops any running effects and clears the element's internal effects queue if it contains
\r
8571 * any additional effects that haven't started yet.
\r
8572 * @return {Ext.Element} The Element
\r
8574 stopFx : function(finish){
\r
8577 if(me.hasActiveFx()){
\r
8578 var cur = getQueue(id)[0];
\r
8579 if(cur && cur.anim){
\r
8580 if(cur.anim.isAnimated){
\r
8581 setQueue(id, [cur]); //clear
\r
8582 cur.anim.stop(finish !== undefined ? finish : TRUE);
\r
8592 beforeFx : function(o){
\r
8593 if(this.hasActiveFx() && !o.concurrent){
\r
8604 * Returns true if the element is currently blocking so that no other effect can be queued
\r
8605 * until this effect is finished, else returns false if blocking is not set. This is commonly
\r
8606 * used to ensure that an effect initiated by a user action runs to completion prior to the
\r
8607 * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
\r
8608 * @return {Boolean} True if blocking, else false
\r
8610 hasFxBlock : function(){
\r
8611 var q = getQueue(this.dom.id);
\r
8612 return q && q[0] && q[0].block;
\r
8616 queueFx : function(o, fn){
\r
8617 var me = fly(this.dom);
\r
8618 if(!me.hasFxBlock()){
\r
8619 Ext.applyIf(o, me.fxDefaults);
\r
8620 if(!o.concurrent){
\r
8621 var run = me.beforeFx(o);
\r
8622 fn.block = o.block;
\r
8623 getQueue(me.dom.id).push(fn);
\r
8635 fxWrap : function(pos, o, vis){
\r
8636 var dom = this.dom,
\r
8639 if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){
\r
8640 if(o.fixPosition){
\r
8641 wrapXY = fly(dom).getXY();
\r
8643 var div = document.createElement("div");
\r
8644 div.style.visibility = vis;
\r
8645 wrap = dom.parentNode.insertBefore(div, dom);
\r
8646 fly(wrap).setPositioning(pos);
\r
8647 if(fly(wrap).isStyle(POSITION, "static")){
\r
8648 fly(wrap).position("relative");
\r
8650 fly(dom).clearPositioning('auto');
\r
8652 wrap.appendChild(dom);
\r
8654 fly(wrap).setXY(wrapXY);
\r
8661 fxUnwrap : function(wrap, pos, o){
\r
8662 var dom = this.dom;
\r
8663 fly(dom).clearPositioning();
\r
8664 fly(dom).setPositioning(pos);
\r
8666 var pn = fly(wrap).dom.parentNode;
8667 pn.insertBefore(dom, wrap);
\r
8668 fly(wrap).remove();
\r
8673 getFxRestore : function(){
\r
8674 var st = this.dom.style;
\r
8675 return {pos: this.getPositioning(), width: st.width, height : st.height};
\r
8679 afterFx : function(o){
\r
8680 var dom = this.dom,
\r
8683 fly(dom).setStyle(o.afterStyle);
\r
8686 fly(dom).addClass(o.afterCls);
\r
8688 if(o.remove == TRUE){
\r
8689 fly(dom).remove();
\r
8692 o.callback.call(o.scope, fly(dom));
\r
8694 if(!o.concurrent){
\r
8695 getQueue(id).shift();
\r
8696 fly(dom).nextFx();
\r
8701 fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
\r
8702 animType = animType || 'run';
\r
8704 var anim = Ext.lib.Anim[animType](
\r
8707 (opt.duration || defaultDur) || .35,
\r
8708 (opt.easing || defaultEase) || EASEOUT,
\r
8717 // backwards compat
\r
8718 Ext.Fx.resize = Ext.Fx.scale;
\r
8720 //When included, Ext.Fx is automatically applied to Element so that all basic
\r
8721 //effects are available directly via the Element API
\r
8722 Ext.Element.addMethods(Ext.Fx);
\r
8725 * @class Ext.CompositeElementLite
\r
8726 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
\r
8727 * members, or to perform collective actions upon the whole set.</p>
\r
8728 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
\r
8729 * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
\r
8730 * Example:<pre><code>
\r
8731 var els = Ext.select("#some-el div.some-class");
\r
8732 // or select directly from an existing element
\r
8733 var el = Ext.get('some-el');
\r
8734 el.select('div.some-class');
\r
8736 els.setWidth(100); // all elements become 100 width
\r
8737 els.hide(true); // all elements fade out and hide
\r
8739 els.setWidth(100).hide(true);
\r
8742 Ext.CompositeElementLite = function(els, root){
\r
8744 * <p>The Array of DOM elements which this CompositeElement encapsulates. Read-only.</p>
\r
8745 * <p>This will not <i>usually</i> be accessed in developers' code, but developers wishing
\r
8746 * to augment the capabilities of the CompositeElementLite class may use it when adding
\r
8747 * methods to the class.</p>
\r
8748 * <p>For example to add the <code>nextAll</code> method to the class to <b>add</b> all
\r
8749 * following siblings of selected elements, the code would be</p><code><pre>
\r
8750 Ext.override(Ext.CompositeElementLite, {
\r
8751 nextAll: function() {
\r
8752 var els = this.elements, i, l = els.length, n, r = [], ri = -1;
\r
8754 // Loop through all elements in this Composite, accumulating
\r
8755 // an Array of all siblings.
\r
8756 for (i = 0; i < l; i++) {
\r
8757 for (n = els[i].nextSibling; n; n = n.nextSibling) {
\r
8762 // Add all found siblings to this Composite
\r
8763 return this.add(r);
\r
8767 * @property elements
\r
8769 this.elements = [];
\r
8770 this.add(els, root);
\r
8771 this.el = new Ext.Element.Flyweight();
\r
8774 Ext.CompositeElementLite.prototype = {
\r
8775 isComposite: true,
\r
8778 getElement : function(el){
\r
8779 // Set the shared flyweight dom property to the current element
\r
8787 transformElement : function(el){
\r
8788 return Ext.getDom(el);
\r
8792 * Returns the number of elements in this Composite.
\r
8795 getCount : function(){
\r
8796 return this.elements.length;
\r
8799 * Adds elements to this Composite object.
\r
8800 * @param {Mixed} els Either an Array of DOM elements to add, or another Composite object who's elements should be added.
\r
8801 * @return {CompositeElement} This Composite object.
\r
8803 add : function(els, root){
\r
8805 elements = me.elements;
\r
8809 if(Ext.isString(els)){
\r
8810 els = Ext.Element.selectorFunction(els, root);
\r
8811 }else if(els.isComposite){
\r
8812 els = els.elements;
\r
8813 }else if(!Ext.isIterable(els)){
\r
8817 for(var i = 0, len = els.length; i < len; ++i){
\r
8818 elements.push(me.transformElement(els[i]));
\r
8823 invoke : function(fn, args){
\r
8825 els = me.elements,
\r
8826 len = els.length,
\r
8830 for(i = 0; i < len; i++) {
\r
8833 Ext.Element.prototype[fn].apply(me.getElement(e), args);
\r
8839 * Returns a flyweight Element of the dom element object at the specified index
\r
8840 * @param {Number} index
\r
8841 * @return {Ext.Element}
\r
8843 item : function(index){
\r
8845 el = me.elements[index],
\r
8849 out = me.getElement(el);
\r
8854 // fixes scope with flyweight
\r
8855 addListener : function(eventName, handler, scope, opt){
\r
8856 var els = this.elements,
\r
8860 for(i = 0; i<len; i++) {
\r
8863 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
\r
8869 * <p>Calls the passed function for each element in this composite.</p>
\r
8870 * @param {Function} fn The function to call. The function is passed the following parameters:<ul>
\r
8871 * <li><b>el</b> : Element<div class="sub-desc">The current Element in the iteration.
\r
8872 * <b>This is the flyweight (shared) Ext.Element instance, so if you require a
\r
8873 * a reference to the dom node, use el.dom.</b></div></li>
\r
8874 * <li><b>c</b> : Composite<div class="sub-desc">This Composite object.</div></li>
\r
8875 * <li><b>idx</b> : Number<div class="sub-desc">The zero-based index in the iteration.</div></li>
\r
8877 * @param {Object} scope (optional) The scope (<i>this</i> reference) in which the function is executed. (defaults to the Element)
\r
8878 * @return {CompositeElement} this
\r
8880 each : function(fn, scope){
\r
8882 els = me.elements,
\r
8886 for(i = 0; i<len; i++) {
\r
8889 e = this.getElement(e);
\r
8890 if(fn.call(scope || e, e, me, i) === false){
\r
8899 * Clears this Composite and adds the elements passed.
\r
8900 * @param {Mixed} els Either an array of DOM elements, or another Composite from which to fill this Composite.
\r
8901 * @return {CompositeElement} this
\r
8903 fill : function(els){
\r
8911 * Filters this composite to only elements that match the passed selector.
\r
8912 * @param {String/Function} selector A string CSS selector or a comparison function.
\r
8913 * The comparison function will be called with the following arguments:<ul>
\r
8914 * <li><code>el</code> : Ext.Element<div class="sub-desc">The current DOM element.</div></li>
\r
8915 * <li><code>index</code> : Number<div class="sub-desc">The current index within the collection.</div></li>
\r
8917 * @return {CompositeElement} this
\r
8919 filter : function(selector){
\r
8922 elements = me.elements,
\r
8923 fn = Ext.isFunction(selector) ? selector
\r
8925 return el.is(selector);
\r
8929 me.each(function(el, self, i){
\r
8930 if(fn(el, i) !== false){
\r
8931 els[els.length] = me.transformElement(el);
\r
8934 me.elements = els;
\r
8939 * Find the index of the passed element within the composite collection.
\r
8940 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
\r
8941 * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
\r
8943 indexOf : function(el){
\r
8944 return this.elements.indexOf(this.transformElement(el));
\r
8948 * Replaces the specified element with the passed element.
\r
8949 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
\r
8951 * @param {Mixed} replacement The id of an element or the Element itself.
\r
8952 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
\r
8953 * @return {CompositeElement} this
\r
8955 replaceElement : function(el, replacement, domReplace){
\r
8956 var index = !isNaN(el) ? el : this.indexOf(el),
\r
8959 replacement = Ext.getDom(replacement);
\r
8961 d = this.elements[index];
\r
8962 d.parentNode.insertBefore(replacement, d);
\r
8963 Ext.removeNode(d);
\r
8965 this.elements.splice(index, 1, replacement);
\r
8971 * Removes all elements.
\r
8973 clear : function(){
\r
8974 this.elements = [];
\r
8978 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
\r
8982 ElProto = Ext.Element.prototype,
\r
8983 CelProto = Ext.CompositeElementLite.prototype;
\r
8985 for(fnName in ElProto){
\r
8986 if(Ext.isFunction(ElProto[fnName])){
\r
8987 (function(fnName){
\r
8988 CelProto[fnName] = CelProto[fnName] || function(){
\r
8989 return this.invoke(fnName, arguments);
\r
8991 }).call(CelProto, fnName);
\r
8998 Ext.Element.selectorFunction = Ext.DomQuery.select;
\r
9002 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
\r
9003 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
\r
9004 * {@link Ext.CompositeElementLite CompositeElementLite} object.
\r
9005 * @param {String/Array} selector The CSS selector or an array of elements
\r
9006 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
\r
9007 * @return {CompositeElementLite/CompositeElement}
\r
9008 * @member Ext.Element
\r
9011 Ext.Element.select = function(selector, root){
\r
9013 if(typeof selector == "string"){
\r
9014 els = Ext.Element.selectorFunction(selector, root);
\r
9015 }else if(selector.length !== undefined){
\r
9018 throw "Invalid selector";
\r
9020 return new Ext.CompositeElementLite(els);
\r
9023 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
\r
9024 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
\r
9025 * {@link Ext.CompositeElementLite CompositeElementLite} object.
\r
9026 * @param {String/Array} selector The CSS selector or an array of elements
\r
9027 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
\r
9028 * @return {CompositeElementLite/CompositeElement}
\r
9032 Ext.select = Ext.Element.select;/**
\r
9033 * @class Ext.CompositeElementLite
\r
9035 Ext.apply(Ext.CompositeElementLite.prototype, {
\r
9036 addElements : function(els, root){
\r
9040 if(typeof els == "string"){
\r
9041 els = Ext.Element.selectorFunction(els, root);
\r
9043 var yels = this.elements;
\r
9044 Ext.each(els, function(e) {
\r
9045 yels.push(Ext.get(e));
\r
9051 * Returns the first Element
\r
9052 * @return {Ext.Element}
\r
9054 first : function(){
\r
9055 return this.item(0);
\r
9059 * Returns the last Element
\r
9060 * @return {Ext.Element}
\r
9062 last : function(){
\r
9063 return this.item(this.getCount()-1);
\r
9067 * Returns true if this composite contains the passed element
\r
9068 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
\r
9071 contains : function(el){
\r
9072 return this.indexOf(el) != -1;
\r
9076 * Removes the specified element(s).
\r
9077 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
\r
9078 * or an array of any of those.
\r
9079 * @param {Boolean} removeDom (optional) True to also remove the element from the document
\r
9080 * @return {CompositeElement} this
\r
9082 removeElement : function(keys, removeDom){
\r
9084 els = this.elements,
\r
9086 Ext.each(keys, function(val){
\r
9087 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
\r
9092 Ext.removeNode(el);
\r
9095 els.splice(val, 1);
\r
9102 * @class Ext.CompositeElement
\r
9103 * @extends Ext.CompositeElementLite
\r
9104 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
\r
9105 * members, or to perform collective actions upon the whole set.</p>
\r
9106 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
\r
9107 * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
\r
9108 * <p>All methods return <i>this</i> and can be chained.</p>
\r
9111 var els = Ext.select("#some-el div.some-class", true);
\r
9112 // or select directly from an existing element
\r
9113 var el = Ext.get('some-el');
\r
9114 el.select('div.some-class', true);
\r
9116 els.setWidth(100); // all elements become 100 width
\r
9117 els.hide(true); // all elements fade out and hide
\r
9119 els.setWidth(100).hide(true);
\r
9122 Ext.CompositeElement = function(els, root){
\r
9123 this.elements = [];
\r
9124 this.add(els, root);
\r
9127 Ext.extend(Ext.CompositeElement, Ext.CompositeElementLite, {
\r
9130 getElement : function(el){
\r
9131 // In this case just return it, since we already have a reference to it
\r
9136 transformElement : function(el){
\r
9137 return Ext.get(el);
\r
9141 * Adds elements to this composite.
\r
9142 * @param {String/Array} els A string CSS selector, an array of elements or an element
\r
9143 * @return {CompositeElement} this
\r
9147 * Returns the Element object at the specified index
\r
9148 * @param {Number} index
\r
9149 * @return {Ext.Element}
\r
9153 * Iterates each <code>element</code> in this <code>composite</code>
\r
9154 * calling the supplied function using {@link Ext#each}.
\r
9155 * @param {Function} fn The function to be called with each
\r
9156 * <code>element</code>. If the supplied function returns <tt>false</tt>,
\r
9157 * iteration stops. This function is called with the following arguments:
\r
9158 * <div class="mdetail-params"><ul>
\r
9159 * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>
\r
9160 * in the <code>composite</code></div></li>
\r
9161 * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>
\r
9162 * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>
\r
9164 * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.
\r
9165 * Defaults to the <code>element</code> at the current <code>index</code>
\r
9166 * within the composite.
\r
9167 * @return {CompositeElement} this
\r
9172 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
\r
9173 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
\r
9174 * {@link Ext.CompositeElementLite CompositeElementLite} object.
\r
9175 * @param {String/Array} selector The CSS selector or an array of elements
\r
9176 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
\r
9177 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
\r
9178 * @return {CompositeElementLite/CompositeElement}
\r
9179 * @member Ext.Element
\r
9182 Ext.Element.select = function(selector, unique, root){
\r
9184 if(typeof selector == "string"){
\r
9185 els = Ext.Element.selectorFunction(selector, root);
\r
9186 }else if(selector.length !== undefined){
\r
9189 throw "Invalid selector";
\r
9192 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
\r
9196 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
\r
9197 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
\r
9198 * {@link Ext.CompositeElementLite CompositeElementLite} object.
\r
9199 * @param {String/Array} selector The CSS selector or an array of elements
\r
9200 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
\r
9201 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
\r
9202 * @return {CompositeElementLite/CompositeElement}
\r
9203 * @member Ext.Element
\r
9206 Ext.select = Ext.Element.select;(function(){
\r
9207 var BEFOREREQUEST = "beforerequest",
\r
9208 REQUESTCOMPLETE = "requestcomplete",
\r
9209 REQUESTEXCEPTION = "requestexception",
\r
9210 UNDEFINED = undefined,
\r
9217 * @class Ext.data.Connection
\r
9218 * @extends Ext.util.Observable
\r
9219 * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made
\r
9220 * either to a configured URL, or to a URL specified at request time.</p>
\r
9221 * <p>Requests made by this class are asynchronous, and will return immediately. No data from
\r
9222 * the server will be available to the statement immediately following the {@link #request} call.
\r
9223 * To process returned data, use a
\r
9224 * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>
\r
9225 * in the request options object,
\r
9226 * or an {@link #requestcomplete event listener}.</p>
\r
9227 * <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
\r
9228 * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
\r
9229 * manner with the DOM <tt><form></tt> element temporarily modified to have its
\r
9230 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
\r
9231 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
\r
9232 * but removed after the return data has been gathered.</p>
\r
9233 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
\r
9234 * server is using JSON to send the return object, then the
\r
9235 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
\r
9236 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
\r
9237 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
\r
9238 * "<" as "&lt;", "&" as "&amp;" etc.</p>
\r
9239 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
\r
9240 * is created containing a <tt>responseText</tt> property in order to conform to the
\r
9241 * requirements of event handlers and callbacks.</p>
\r
9242 * <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>
\r
9243 * and some server technologies (notably JEE) may require some custom processing in order to
\r
9244 * retrieve parameter names and parameter values from the packet content.</p>
\r
9246 * @param {Object} config a configuration object.
\r
9248 Ext.data.Connection = function(config){
\r
9249 Ext.apply(this, config);
\r
9252 * @event beforerequest
\r
9253 * Fires before a network request is made to retrieve a data object.
\r
9254 * @param {Connection} conn This Connection object.
\r
9255 * @param {Object} options The options config object passed to the {@link #request} method.
\r
9259 * @event requestcomplete
\r
9260 * Fires if the request was successfully completed.
\r
9261 * @param {Connection} conn This Connection object.
\r
9262 * @param {Object} response The XHR object containing the response data.
\r
9263 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
\r
9265 * @param {Object} options The options config object passed to the {@link #request} method.
\r
9269 * @event requestexception
\r
9270 * Fires if an error HTTP status was returned from the server.
\r
9271 * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
\r
9272 * for details of HTTP status codes.
\r
9273 * @param {Connection} conn This Connection object.
\r
9274 * @param {Object} response The XHR object containing the response data.
\r
9275 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
\r
9277 * @param {Object} options The options config object passed to the {@link #request} method.
\r
9281 Ext.data.Connection.superclass.constructor.call(this);
\r
9284 Ext.extend(Ext.data.Connection, Ext.util.Observable, {
\r
9286 * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>
\r
9287 * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope
\r
9288 * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>
\r
9291 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
\r
9292 * extra parameters to each request made by this object. (defaults to undefined)
\r
9295 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
\r
9296 * to each request made by this object. (defaults to undefined)
\r
9299 * @cfg {String} method (Optional) The default HTTP method to be used for requests.
\r
9300 * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;
\r
9301 * otherwise, GET will be used.)
\r
9304 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
\r
9308 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
\r
9314 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
\r
9317 disableCaching: true,
\r
9320 * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
\r
9321 * through a cache buster. Defaults to '_dc'
\r
9324 disableCachingParam: '_dc',
\r
9327 * <p>Sends an HTTP request to a remote server.</p>
\r
9328 * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
\r
9329 * return before the response has been received. Process any returned data
\r
9330 * in a callback function.</p>
\r
9332 Ext.Ajax.request({
\r
9333 url: 'ajax_demo/sample.json',
\r
9334 success: function(response, opts) {
\r
9335 var obj = Ext.decode(response.responseText);
\r
9338 failure: function(response, opts) {
\r
9339 console.log('server-side failure with status code ' + response.status);
\r
9343 * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
\r
9344 * @param {Object} options An object which may contain the following properties:<ul>
\r
9345 * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
\r
9346 * which to send the request, or a function to call which returns a URL string. The scope of the
\r
9347 * function is specified by the <tt>scope</tt> option. Defaults to the configured
\r
9348 * <tt>{@link #url}</tt>.</div></li>
\r
9349 * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
\r
9350 * An object containing properties which are used as parameters to the
\r
9351 * request, a url encoded string or a function to call to get either. The scope of the function
\r
9352 * is specified by the <tt>scope</tt> option.</div></li>
\r
9353 * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
\r
9354 * for the request. Defaults to the configured method, or if no method was configured,
\r
9355 * "GET" if no parameters are being sent, and "POST" if parameters are being sent. Note that
\r
9356 * the method name is case-sensitive and should be all caps.</div></li>
\r
9357 * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
\r
9358 * function to be called upon receipt of the HTTP response. The callback is
\r
9359 * called regardless of success or failure and is passed the following
\r
9361 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
\r
9362 * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
\r
9363 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
\r
9364 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
\r
9365 * accessing elements of the response.</div></li>
\r
9366 * </ul></div></li>
\r
9367 * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
\r
9368 * to be called upon success of the request. The callback is passed the following
\r
9370 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
\r
9371 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
\r
9372 * </ul></div></li>
\r
9373 * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
\r
9374 * to be called upon failure of the request. The callback is passed the
\r
9375 * following parameters:<ul>
\r
9376 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
\r
9377 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
\r
9378 * </ul></div></li>
\r
9379 * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
\r
9380 * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
\r
9381 * specified as functions from which to draw values, then this also serves as the scope for those function calls.
\r
9382 * Defaults to the browser window.</div></li>
\r
9383 * <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>
\r
9384 * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt><form></tt>
\r
9385 * Element or the id of the <tt><form></tt> to pull parameters from.</div></li>
\r
9386 * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
\r
9387 * with the <tt>form</tt> option</b>.
\r
9388 * <p>True if the form object is a file upload (will be set automatically if the form was
\r
9389 * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
\r
9390 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
\r
9391 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
\r
9392 * DOM <tt><form></tt> element temporarily modified to have its
\r
9393 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
\r
9394 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
\r
9395 * but removed after the return data has been gathered.</p>
\r
9396 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
\r
9397 * server is using JSON to send the return object, then the
\r
9398 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
\r
9399 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
\r
9400 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
\r
9401 * is created containing a <tt>responseText</tt> property in order to conform to the
\r
9402 * requirements of event handlers and callbacks.</p>
\r
9403 * <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>
\r
9404 * and some server technologies (notably JEE) may require some custom processing in order to
\r
9405 * retrieve parameter names and parameter values from the packet content.</p>
\r
9407 * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
\r
9408 * headers to set for the request.</div></li>
\r
9409 * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
\r
9410 * to use for the post. Note: This will be used instead of params for the post
\r
9411 * data. Any params will be appended to the URL.</div></li>
\r
9412 * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
\r
9413 * data to use as the post. Note: This will be used instead of params for the post
\r
9414 * data. Any params will be appended to the URL.</div></li>
\r
9415 * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
\r
9416 * to add a unique cache-buster param to GET requests.</div></li>
\r
9418 * <p>The options object may also contain any other property which might be needed to perform
\r
9419 * postprocessing in a callback because it is passed to callback functions.</p>
\r
9420 * @return {Number} transactionId The id of the server transaction. This may be used
\r
9421 * to cancel the request.
\r
9423 request : function(o){
\r
9425 if(me.fireEvent(BEFOREREQUEST, me, o)){
\r
9427 if(!Ext.isEmpty(o.indicatorText)){
\r
9428 me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";
\r
9430 if(me.indicatorText) {
\r
9431 Ext.getDom(o.el).innerHTML = me.indicatorText;
\r
9433 o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {
\r
9434 Ext.getDom(o.el).innerHTML = response.responseText;
\r
9439 url = o.url || me.url,
\r
9441 cb = {success: me.handleResponse,
\r
9442 failure: me.handleFailure,
\r
9444 argument: {options: o},
\r
9445 timeout : o.timeout || me.timeout
\r
9451 if (Ext.isFunction(p)) {
\r
9452 p = p.call(o.scope||WINDOW, o);
\r
9455 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);
\r
9457 if (Ext.isFunction(url)) {
\r
9458 url = url.call(o.scope || WINDOW, o);
\r
9461 if((form = Ext.getDom(o.form))){
\r
9462 url = url || form.action;
\r
9463 if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) {
\r
9464 return me.doFormUpload.call(me, o, p, url);
\r
9466 serForm = Ext.lib.Ajax.serializeForm(form);
\r
9467 p = p ? (p + '&' + serForm) : serForm;
\r
9470 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);
\r
9472 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
\r
9473 var dcp = o.disableCachingParam || me.disableCachingParam;
\r
9474 url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));
\r
9477 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});
\r
9479 if(o.autoAbort === true || me.autoAbort) {
\r
9483 if((method == GET || o.xmlData || o.jsonData) && p){
\r
9484 url = Ext.urlAppend(url, p);
\r
9487 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));
\r
9489 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;
\r
9494 * Determine whether this object has a request outstanding.
\r
9495 * @param {Number} transactionId (Optional) defaults to the last transaction
\r
9496 * @return {Boolean} True if there is an outstanding request.
\r
9498 isLoading : function(transId){
\r
9499 return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;
\r
9503 * Aborts any outstanding request.
\r
9504 * @param {Number} transactionId (Optional) defaults to the last transaction
\r
9506 abort : function(transId){
\r
9507 if(transId || this.isLoading()){
\r
9508 Ext.lib.Ajax.abort(transId || this.transId);
\r
9513 handleResponse : function(response){
\r
9514 this.transId = false;
\r
9515 var options = response.argument.options;
\r
9516 response.argument = options ? options.argument : null;
\r
9517 this.fireEvent(REQUESTCOMPLETE, this, response, options);
\r
9518 if(options.success){
\r
9519 options.success.call(options.scope, response, options);
\r
9521 if(options.callback){
\r
9522 options.callback.call(options.scope, options, true, response);
\r
9527 handleFailure : function(response, e){
\r
9528 this.transId = false;
\r
9529 var options = response.argument.options;
\r
9530 response.argument = options ? options.argument : null;
\r
9531 this.fireEvent(REQUESTEXCEPTION, this, response, options, e);
\r
9532 if(options.failure){
\r
9533 options.failure.call(options.scope, response, options);
\r
9535 if(options.callback){
\r
9536 options.callback.call(options.scope, options, false, response);
\r
9541 doFormUpload : function(o, ps, url){
\r
9542 var id = Ext.id(),
\r
9544 frame = doc.createElement('iframe'),
\r
9545 form = Ext.getDom(o.form),
\r
9548 encoding = 'multipart/form-data',
\r
9550 target: form.target,
\r
9551 method: form.method,
\r
9552 encoding: form.encoding,
\r
9553 enctype: form.enctype,
\r
9554 action: form.action
\r
9557 Ext.fly(frame).set({
\r
9564 doc.body.appendChild(frame);
\r
9566 //Reset the Frame to neutral domain
\r
9567 Ext.fly(frame).set({
\r
9568 src : Ext.SSL_SECURE_URL
\r
9571 // This is required so that IE doesn't pop the response up in a new window.
\r
9573 document.frames[id].name = id;
\r
9577 Ext.fly(form).set({
\r
9580 enctype: encoding,
\r
9581 encoding: encoding,
\r
9582 action: url || buf.action
\r
9585 // add dynamic params
\r
9586 Ext.iterate(Ext.urlDecode(ps, false), function(k, v){
\r
9587 hd = doc.createElement('input');
\r
9593 form.appendChild(hd);
\r
9599 // bogus response object
\r
9600 r = {responseText : '',
\r
9601 responseXML : null,
\r
9602 argument : o.argument},
\r
9607 doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
\r
9610 if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea
\r
9611 r.responseText = firstChild.value;
\r
9613 r.responseText = doc.body.innerHTML;
\r
9616 //in IE the document may still have a body even if returns XML.
\r
9617 r.responseXML = doc.XMLDocument || doc;
\r
9622 Ext.EventManager.removeListener(frame, LOAD, cb, me);
\r
9624 me.fireEvent(REQUESTCOMPLETE, me, r, o);
\r
9626 function runCallback(fn, scope, args){
\r
9627 if(Ext.isFunction(fn)){
\r
9628 fn.apply(scope, args);
\r
9632 runCallback(o.success, o.scope, [r, o]);
\r
9633 runCallback(o.callback, o.scope, [o, true, r]);
\r
9635 if(!me.debugUploads){
\r
9636 setTimeout(function(){Ext.removeNode(frame);}, 100);
\r
9640 Ext.EventManager.on(frame, LOAD, cb, this);
\r
9643 Ext.fly(form).set(buf);
\r
9644 Ext.each(hiddens, function(h) {
\r
9645 Ext.removeNode(h);
\r
9653 * @extends Ext.data.Connection
\r
9654 * <p>The global Ajax request class that provides a simple way to make Ajax requests
\r
9655 * with maximum flexibility.</p>
\r
9656 * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once
\r
9657 * and override them at the request function level only if necessary.</p>
\r
9658 * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>
\r
9659 * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>
\r
9660 * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>
\r
9661 * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>
\r
9664 // Default headers to pass in every request
\r
9665 Ext.Ajax.defaultHeaders = {
\r
9666 'Powered-By': 'Ext'
\r
9670 * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>
\r
9671 * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>
\r
9672 * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>
\r
9673 * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>
\r
9676 // Example: show a spinner during all Ajax requests
\r
9677 Ext.Ajax.on('beforerequest', this.showSpinner, this);
\r
9678 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
\r
9679 Ext.Ajax.on('requestexception', this.hideSpinner, this);
\r
9682 * <p>An example request:</p>
\r
9685 Ext.Ajax.{@link Ext.data.Connection#request request}({
\r
9690 'my-header': 'foo'
\r
9692 params: { foo: 'bar' }
\r
9695 // Simple ajax form submission
\r
9696 Ext.Ajax.{@link Ext.data.Connection#request request}({
\r
9697 form: 'some-form',
\r
9704 Ext.Ajax = new Ext.data.Connection({
\r
9706 * @cfg {String} url @hide
\r
9709 * @cfg {Object} extraParams @hide
\r
9712 * @cfg {Object} defaultHeaders @hide
\r
9715 * @cfg {String} method (Optional) @hide
\r
9718 * @cfg {Number} timeout (Optional) @hide
\r
9721 * @cfg {Boolean} autoAbort (Optional) @hide
\r
9725 * @cfg {Boolean} disableCaching (Optional) @hide
\r
9729 * @property disableCaching
\r
9730 * True to add a unique cache-buster param to GET requests. (defaults to true)
\r
9735 * The default URL to be used for requests to the server. (defaults to undefined)
\r
9736 * If the server receives all requests through one URL, setting this once is easier than
\r
9737 * entering it on every request.
\r
9741 * @property extraParams
\r
9742 * An object containing properties which are used as extra parameters to each request made
\r
9743 * by this object (defaults to undefined). Session information and other data that you need
\r
9744 * to pass with each request are commonly put here.
\r
9748 * @property defaultHeaders
\r
9749 * An object containing request headers which are added to each request made by this object
\r
9750 * (defaults to undefined).
\r
9754 * @property method
\r
9755 * The default HTTP method to be used for requests. Note that this is case-sensitive and
\r
9756 * should be all caps (defaults to undefined; if not set but params are present will use
\r
9757 * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
\r
9761 * @property timeout
\r
9762 * The timeout in milliseconds to be used for requests. (defaults to 30000)
\r
9767 * @property autoAbort
\r
9768 * Whether a new request should abort any pending requests. (defaults to false)
\r
9771 autoAbort : false,
\r
9774 * Serialize the passed form into a url encoded string
\r
9775 * @param {String/HTMLElement} form
\r
9776 * @return {String}
\r
9778 serializeForm : function(form){
\r
9779 return Ext.lib.Ajax.serializeForm(form);
\r
9783 * @class Ext.Updater
9784 * @extends Ext.util.Observable
9785 * Provides AJAX-style update capabilities for Element objects. Updater can be used to {@link #update}
9786 * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9787 * {@link Ext.Element Element} on a specific interval.<br><br>
9790 * var el = Ext.get("foo"); // Get Ext.Element object
9791 * var mgr = el.getUpdater();
9793 url: "http://myserver.com/index.php",
9800 * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
9802 * // or directly (returns the same Updater instance)
9803 * var mgr = new Ext.Updater("myElementId");
9804 * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
9805 * mgr.on("update", myFcnNeedsToKnow);
9807 * // short handed call directly from the element object
9808 * Ext.get("foo").load({
9811 params: "param1=foo&param2=bar",
9812 text: "Loading Foo..."
9816 * Create new Updater directly.
9817 * @param {Mixed} el The element to update
9818 * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
9819 * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
9821 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
9823 var BEFOREUPDATE = "beforeupdate",
9825 FAILURE = "failure";
9828 function processSuccess(response){
9830 me.transaction = null;
9831 if (response.argument.form && response.argument.reset) {
9832 try { // put in try/catch since some older FF releases had problems with this
9833 response.argument.form.reset();
9836 if (me.loadScripts) {
9837 me.renderer.render(me.el, response, me,
9838 updateComplete.createDelegate(me, [response]));
9840 me.renderer.render(me.el, response, me);
9841 updateComplete.call(me, response);
9846 function updateComplete(response, type, success){
9847 this.fireEvent(type || UPDATE, this.el, response);
9848 if(Ext.isFunction(response.argument.callback)){
9849 response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
9854 function processFailure(response){
9855 updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
9859 constructor: function(el, forceNew){
9862 if(!forceNew && el.updateManager){
9863 return el.updateManager;
9866 * The Element object
9871 * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
9874 me.defaultUrl = null;
9878 * @event beforeupdate
9879 * Fired before an update is made, return false from your handler and the update is cancelled.
9880 * @param {Ext.Element} el
9881 * @param {String/Object/Function} url
9882 * @param {String/Object} params
9887 * Fired after successful update is made.
9888 * @param {Ext.Element} el
9889 * @param {Object} oResponseObject The response Object
9894 * Fired on update failure.
9895 * @param {Ext.Element} el
9896 * @param {Object} oResponseObject The response Object
9901 Ext.apply(me, Ext.Updater.defaults);
9903 * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
9904 * @property sslBlankUrl
9908 * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
9909 * @property disableCaching
9913 * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
9914 * @property indicatorText
9918 * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
9919 * @property showLoadIndicator
9923 * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
9928 * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
9929 * @property loadScripts
9934 * Transaction object of the current executing transaction, or null if there is no active transaction.
9936 me.transaction = null;
9938 * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
9941 me.refreshDelegate = me.refresh.createDelegate(me);
9943 * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
9946 me.updateDelegate = me.update.createDelegate(me);
9948 * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
9951 me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
9954 * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
9956 me.renderer = me.renderer || me.getDefaultRenderer();
9958 Ext.Updater.superclass.constructor.call(me);
9962 * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9963 * @param {Object} renderer The object implementing the render() method
9965 setRenderer : function(renderer){
9966 this.renderer = renderer;
9970 * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9973 getRenderer : function(){
9974 return this.renderer;
9978 * This is an overrideable method which returns a reference to a default
9979 * renderer class if none is specified when creating the Ext.Updater.
9980 * Defaults to {@link Ext.Updater.BasicRenderer}
9982 getDefaultRenderer: function() {
9983 return new Ext.Updater.BasicRenderer();
9987 * Sets the default URL used for updates.
9988 * @param {String/Function} defaultUrl The url or a function to call to get the url
9990 setDefaultUrl : function(defaultUrl){
9991 this.defaultUrl = defaultUrl;
9995 * Get the Element this Updater is bound to
9996 * @return {Ext.Element} The element
10003 * Performs an <b>asynchronous</b> request, updating this element with the response.
10004 * If params are specified it uses POST, otherwise it uses GET.<br><br>
10005 * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
10006 * will not have been fully updated when the function returns. To post-process the returned
10007 * data, use the callback option, or an <b><code>update</code></b> event handler.
10008 * @param {Object} options A config object containing any of the following options:<ul>
10009 * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
10010 * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
10011 * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
10012 * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
10013 * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
10014 * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
10015 * string, or as an object containing properties which represent parameters,
10016 * or as a function, which returns such an object.</p></li>
10017 * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
10018 * any <script> tags embedded in the response text will be extracted
10019 * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
10020 * the callback will be called <i>after</i> the execution of the scripts.</p></li>
10021 * <li>callback : <b>Function</b><p class="sub-desc">A function to
10022 * be called when the response from the server arrives. The following
10023 * parameters are passed:<ul>
10024 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10025 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10026 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
10027 * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
10029 * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
10030 * to execute the callback (The callback's <code>this</code> reference.) If the
10031 * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
10032 * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
10033 * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
10034 * calls. To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
10035 * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
10036 * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
10037 * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
10038 * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...'). To replace the entire div, not
10039 * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
10040 * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
10041 * requests, this option causes an extra, auto-generated parameter to be appended to the request
10042 * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
10047 url: "your-url.php",
10048 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
10049 callback: yourFunction,
10050 scope: yourObject, //(optional scope)
10053 text: "Loading...",
10055 scripts: false // Save time by avoiding RegExp execution.
10059 update : function(url, params, callback, discardUrl){
10064 if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
10065 if(Ext.isObject(url)){ // must be config object
10068 params = params || cfg.params;
10069 callback = callback || cfg.callback;
10070 discardUrl = discardUrl || cfg.discardUrl;
10071 callerScope = cfg.scope;
10072 if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
10073 if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
10074 if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
10075 if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
10080 me.defaultUrl = url;
10082 if(Ext.isFunction(url)){
10083 url = url.call(me);
10086 var o = Ext.apply({}, {
10088 params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
10089 success: processSuccess,
10090 failure: processFailure,
10092 callback: undefined,
10093 timeout: (me.timeout*1000),
10094 disableCaching: me.disableCaching,
10099 "callback": callback,
10100 "scope": callerScope || window,
10105 me.transaction = Ext.Ajax.request(o);
10110 * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
10111 * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
10112 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
10113 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
10114 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
10115 * DOM <code><form></code> element temporarily modified to have its
10116 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
10117 * to a dynamically generated, hidden <code><iframe></code> which is inserted into the document
10118 * but removed after the return data has been gathered.</p>
10119 * <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>
10120 * and some server technologies (notably JEE) may require some custom processing in order to
10121 * retrieve parameter names and parameter values from the packet content.</p>
10122 * @param {String/HTMLElement} form The form Id or form element
10123 * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
10124 * @param {Boolean} reset (optional) Whether to try to reset the form after the update
10125 * @param {Function} callback (optional) Callback when transaction is complete. The following
10126 * parameters are passed:<ul>
10127 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
10128 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
10129 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
10131 formUpdate : function(form, url, reset, callback){
10133 if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
10134 if(Ext.isFunction(url)){
10135 url = url.call(me);
10137 form = Ext.getDom(form)
10138 me.transaction = Ext.Ajax.request({
10141 success: processSuccess,
10142 failure: processFailure,
10144 timeout: (me.timeout*1000),
10148 "callback": callback,
10152 me.showLoading.defer(1, me);
10157 * Set this element to auto refresh. Can be canceled by calling {@link #stopAutoRefresh}.
10158 * @param {Number} interval How often to update (in seconds).
10159 * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
10160 * supported by {@link #load}, or a function to call to get the url (defaults to the last used url). Note that while
10161 * the url used in a load call can be reused by this method, other load config options will not be reused and must be
10162 * sepcified as part of a config object passed as this paramter if needed.
10163 * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
10164 * "¶m1=1¶m2=2" or as an object {param1: 1, param2: 2}
10165 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10166 * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
10168 startAutoRefresh : function(interval, url, params, callback, refreshNow){
10171 me.update(url || me.defaultUrl, params, callback, true);
10173 if(me.autoRefreshProcId){
10174 clearInterval(me.autoRefreshProcId);
10176 me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
10180 * Stop auto refresh on this element.
10182 stopAutoRefresh : function(){
10183 if(this.autoRefreshProcId){
10184 clearInterval(this.autoRefreshProcId);
10185 delete this.autoRefreshProcId;
10190 * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
10192 isAutoRefreshing : function(){
10193 return !!this.autoRefreshProcId;
10197 * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
10198 * method may be overridden to perform a custom action while this Updater is actively updating its contents.
10200 showLoading : function(){
10201 if(this.showLoadIndicator){
10202 this.el.dom.innerHTML = this.indicatorText;
10207 * Aborts the currently executing transaction, if any.
10209 abort : function(){
10210 if(this.transaction){
10211 Ext.Ajax.abort(this.transaction);
10216 * Returns true if an update is in progress, otherwise false.
10217 * @return {Boolean}
10219 isUpdating : function(){
10220 return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
10224 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
10225 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10227 refresh : function(callback){
10228 if(this.defaultUrl){
10229 this.update(this.defaultUrl, null, callback, true);
10236 * @class Ext.Updater.defaults
10237 * The defaults collection enables customizing the default properties of Updater
10239 Ext.Updater.defaults = {
10241 * Timeout for requests or form posts in seconds (defaults to 30 seconds).
10246 * True to append a unique parameter to GET requests to disable caching (defaults to false).
10249 disableCaching : false,
10251 * Whether or not to show {@link #indicatorText} during loading (defaults to true).
10254 showLoadIndicator : true,
10256 * Text for loading indicator (defaults to '<div class="loading-indicator">Loading...</div>').
10259 indicatorText : '<div class="loading-indicator">Loading...</div>',
10261 * True to process scripts by default (defaults to false).
10264 loadScripts : false,
10266 * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
10269 sslBlankUrl : Ext.SSL_SECURE_URL
10274 * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
10276 * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
10277 * @param {Mixed} el The element to update
10278 * @param {String} url The url
10279 * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
10280 * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
10281 * example: {disableCaching:true, indicatorText: "Loading data..."}
10284 * @member Ext.Updater
10286 Ext.Updater.updateElement = function(el, url, params, options){
10287 var um = Ext.get(el).getUpdater();
10288 Ext.apply(um, options);
10289 um.update(url, params, options ? options.callback : null);
10293 * @class Ext.Updater.BasicRenderer
10294 * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
10295 * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
10296 * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
10298 Ext.Updater.BasicRenderer = function(){};
10300 Ext.Updater.BasicRenderer.prototype = {
10302 * This method is called when an Ajax response is received, and an Element needs updating.
10303 * @param {Ext.Element} el The element being rendered
10304 * @param {Object} xhr The XMLHttpRequest object
10305 * @param {Updater} updateManager The calling update manager
10306 * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
10308 render : function(el, response, updateManager, callback){
10309 el.update(response.responseText, updateManager.loadScripts, callback);
10314 * The date parsing and formatting syntax contains a subset of
10315 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
10316 * supported will provide results equivalent to their PHP versions.
10318 * The following is a list of all currently supported formats:
10320 Format Description Example returned values
10321 ------ ----------------------------------------------------------------------- -----------------------
10322 d Day of the month, 2 digits with leading zeros 01 to 31
10323 D A short textual representation of the day of the week Mon to Sun
10324 j Day of the month without leading zeros 1 to 31
10325 l A full textual representation of the day of the week Sunday to Saturday
10326 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
10327 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
10328 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
10329 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
10330 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
10331 F A full textual representation of a month, such as January or March January to December
10332 m Numeric representation of a month, with leading zeros 01 to 12
10333 M A short textual representation of a month Jan to Dec
10334 n Numeric representation of a month, without leading zeros 1 to 12
10335 t Number of days in the given month 28 to 31
10336 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
10337 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
10338 belongs to the previous or next year, that year is used instead)
10339 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
10340 y A two digit representation of a year Examples: 99 or 03
10341 a Lowercase Ante meridiem and Post meridiem am or pm
10342 A Uppercase Ante meridiem and Post meridiem AM or PM
10343 g 12-hour format of an hour without leading zeros 1 to 12
10344 G 24-hour format of an hour without leading zeros 0 to 23
10345 h 12-hour format of an hour with leading zeros 01 to 12
10346 H 24-hour format of an hour with leading zeros 00 to 23
10347 i Minutes, with leading zeros 00 to 59
10348 s Seconds, with leading zeros 00 to 59
10349 u Decimal fraction of a second Examples:
10350 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
10351 100 (i.e. 0.100s) or
10352 999 (i.e. 0.999s) or
10353 999876543210 (i.e. 0.999876543210s)
10354 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
10355 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
10356 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
10357 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
10360 1) If unspecified, the month / day defaults to the current month / day, 1991 or
10361 the time defaults to midnight, while the timezone defaults to the 1992-10 or
10362 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
10363 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
10364 are optional. 1995-07-18T17:21:28-02:00 or
10365 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
10366 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
10367 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
10368 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
10369 date-time granularity which are supported, or see 2000-02-13T21:25:33
10370 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
10371 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
10372 M$ Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
10373 \/Date(1238606590509+0800)\/
10376 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
10379 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
10381 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
10382 document.write(dt.format('Y-m-d')); // 2007-01-10
10383 document.write(dt.format('F j, Y, g:i a')); // January 10, 2007, 3:05 pm
10384 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
10387 * Here are some standard date/time patterns that you might find helpful. They
10388 * are not part of the source of Date.js, but to use them you can simply copy this
10389 * block of code into any script that is included after Date.js and they will also become
10390 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
10393 ISO8601Long:"Y-m-d H:i:s",
10394 ISO8601Short:"Y-m-d",
10395 ShortDate: "n/j/Y",
10396 LongDate: "l, F d, Y",
10397 FullDateTime: "l, F d, Y g:i:s A",
10399 ShortTime: "g:i A",
10400 LongTime: "g:i:s A",
10401 SortableDateTime: "Y-m-d\\TH:i:s",
10402 UniversalSortableDateTime: "Y-m-d H:i:sO",
10409 var dt = new Date();
10410 document.write(dt.format(Date.patterns.ShortDate));
10412 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
10413 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
10417 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
10418 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
10419 * They generate precompiled functions from format patterns instead of parsing and
10420 * processing each pattern every time a date is formatted. These functions are available
10421 * on every Date object.
10427 * Global flag which determines if strict date parsing should be used.
10428 * Strict date parsing will not roll-over invalid dates, which is the
10429 * default behaviour of javascript Date objects.
10430 * (see {@link #parseDate} for more information)
10431 * Defaults to <tt>false</tt>.
10435 Date.useStrict = false;
10438 // create private copy of Ext's String.format() method
10439 // - to remove unnecessary dependency
10440 // - to resolve namespace conflict with M$-Ajax's implementation
10441 function xf(format) {
10442 var args = Array.prototype.slice.call(arguments, 1);
10443 return format.replace(/\{(\d+)\}/g, function(m, i) {
10450 Date.formatCodeToRegex = function(character, currentGroup) {
10451 // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
10452 var p = Date.parseCodes[character];
10455 p = typeof p == 'function'? p() : p;
10456 Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
10459 return p? Ext.applyIf({
10460 c: p.c? xf(p.c, currentGroup || "{0}") : p.c
10464 s:Ext.escapeRe(character) // treat unrecognised characters as literals
10468 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
10469 var $f = Date.formatCodeToRegex;
10473 * <p>An object hash in which each property is a date parsing function. The property name is the
10474 * format string which that function parses.</p>
10475 * <p>This object is automatically populated with date parsing functions as
10476 * date formats are requested for Ext standard formatting strings.</p>
10477 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
10478 * may be used as a format string to {@link #parseDate}.<p>
10479 * <p>Example:</p><pre><code>
10480 Date.parseFunctions['x-date-format'] = myDateParser;
10482 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
10483 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
10484 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
10485 * (i.e. prevent javascript Date "rollover") (The default must be false).
10486 * Invalid date strings should return null when parsed.</div></li>
10488 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
10489 * formatting function must be placed into the {@link #formatFunctions} property.
10490 * @property parseFunctions
10495 "M$": function(input, strict) {
10496 // note: the timezone offset is ignored since the M$ Ajax server sends
10497 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
10498 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
10499 var r = (input || '').match(re);
10500 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
10506 * <p>An object hash in which each property is a date formatting function. The property name is the
10507 * format string which corresponds to the produced formatted date string.</p>
10508 * <p>This object is automatically populated with date formatting functions as
10509 * date formats are requested for Ext standard formatting strings.</p>
10510 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
10511 * may be used as a format string to {@link #format}. Example:</p><pre><code>
10512 Date.formatFunctions['x-date-format'] = myDateFormatter;
10514 * <p>A formatting function should return a string repesentation of the passed Date object:<div class="mdetail-params"><ul>
10515 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
10517 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
10518 * parsing function must be placed into the {@link #parseFunctions} property.
10519 * @property formatFunctions
10525 // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
10526 return '\\/Date(' + this.getTime() + ')\\/';
10533 * Date interval constant
10540 * Date interval constant
10547 * Date interval constant
10553 /** Date interval constant
10560 * Date interval constant
10567 * Date interval constant
10574 * Date interval constant
10581 * <p>An object hash containing default date values used during date parsing.</p>
10582 * <p>The following properties are available:<div class="mdetail-params"><ul>
10583 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
10584 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
10585 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
10586 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
10587 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
10588 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
10589 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
10591 * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
10592 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
10593 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
10594 * It is the responsiblity of the developer to account for this.</b></p>
10597 // set default day value to the first day of the month
10598 Date.defaults.d = 1;
10600 // parse a February date string containing only year and month values.
10601 // setting the default day value to 1 prevents weird date rollover issues
10602 // when attempting to parse the following date string on, for example, March 31st 2009.
10603 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
10605 * @property defaults
10612 * An array of textual day names.
10613 * Override these values for international dates.
10617 'SundayInYourLang',
10618 'MondayInYourLang',
10636 * An array of textual month names.
10637 * Override these values for international dates.
10640 Date.monthNames = [
10665 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
10666 * Override these values for international dates.
10669 Date.monthNumbers = {
10670 'ShortJanNameInYourLang':0,
10671 'ShortFebNameInYourLang':1,
10694 * Get the short month name for the given month number.
10695 * Override this function for international dates.
10696 * @param {Number} month A zero-based javascript month number.
10697 * @return {String} The short month name.
10700 getShortMonthName : function(month) {
10701 return Date.monthNames[month].substring(0, 3);
10705 * Get the short day name for the given day number.
10706 * Override this function for international dates.
10707 * @param {Number} day A zero-based javascript day number.
10708 * @return {String} The short day name.
10711 getShortDayName : function(day) {
10712 return Date.dayNames[day].substring(0, 3);
10716 * Get the zero-based javascript month number for the given short/full month name.
10717 * Override this function for international dates.
10718 * @param {String} name The short/full month name.
10719 * @return {Number} The zero-based javascript month number.
10722 getMonthNumber : function(name) {
10723 // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
10724 return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
10728 * The base format-code to formatting-function hashmap used by the {@link #format} method.
10729 * Formatting functions are strings (or functions which return strings) which
10730 * will return the appropriate value when evaluated in the context of the Date object
10731 * from which the {@link #format} method is called.
10732 * Add to / override these mappings for custom date formatting.
10733 * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
10736 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
10737 (new Date()).format("X"); // returns the current day of the month
10743 d: "String.leftPad(this.getDate(), 2, '0')",
10744 D: "Date.getShortDayName(this.getDay())", // get localised short day name
10745 j: "this.getDate()",
10746 l: "Date.dayNames[this.getDay()]",
10747 N: "(this.getDay() ? this.getDay() : 7)",
10748 S: "this.getSuffix()",
10749 w: "this.getDay()",
10750 z: "this.getDayOfYear()",
10751 W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10752 F: "Date.monthNames[this.getMonth()]",
10753 m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10754 M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10755 n: "(this.getMonth() + 1)",
10756 t: "this.getDaysInMonth()",
10757 L: "(this.isLeapYear() ? 1 : 0)",
10758 o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10759 Y: "this.getFullYear()",
10760 y: "('' + this.getFullYear()).substring(2, 4)",
10761 a: "(this.getHours() < 12 ? 'am' : 'pm')",
10762 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10763 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10764 G: "this.getHours()",
10765 h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10766 H: "String.leftPad(this.getHours(), 2, '0')",
10767 i: "String.leftPad(this.getMinutes(), 2, '0')",
10768 s: "String.leftPad(this.getSeconds(), 2, '0')",
10769 u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10770 O: "this.getGMTOffset()",
10771 P: "this.getGMTOffset(true)",
10772 T: "this.getTimezone()",
10773 Z: "(this.getTimezoneOffset() * -60)",
10775 c: function() { // ISO-8601 -- GMT format
10776 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10777 var e = c.charAt(i);
10778 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10780 return code.join(" + ");
10783 c: function() { // ISO-8601 -- UTC format
10785 "this.getUTCFullYear()", "'-'",
10786 "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10787 "String.leftPad(this.getUTCDate(), 2, '0')",
10789 "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10790 "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10791 "String.leftPad(this.getUTCSeconds(), 2, '0')",
10797 U: "Math.round(this.getTime() / 1000)"
10801 * Checks if the passed Date parameters will cause a javascript Date "rollover".
10802 * @param {Number} year 4-digit year
10803 * @param {Number} month 1-based month-of-year
10804 * @param {Number} day Day of month
10805 * @param {Number} hour (optional) Hour
10806 * @param {Number} minute (optional) Minute
10807 * @param {Number} second (optional) Second
10808 * @param {Number} millisecond (optional) Millisecond
10809 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
10812 isValid : function(y, m, d, h, i, s, ms) {
10819 var dt = new Date(y, m - 1, d, h, i, s, ms);
10821 return y == dt.getFullYear() &&
10822 m == dt.getMonth() + 1 &&
10823 d == dt.getDate() &&
10824 h == dt.getHours() &&
10825 i == dt.getMinutes() &&
10826 s == dt.getSeconds() &&
10827 ms == dt.getMilliseconds();
10831 * Parses the passed string using the specified date format.
10832 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
10833 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
10834 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
10835 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
10836 * Keep in mind that the input date string must precisely match the specified format string
10837 * in order for the parse operation to be successful (failed parse operations return a null value).
10838 * <p>Example:</p><pre><code>
10839 //dt = Fri May 25 2007 (current date)
10840 var dt = new Date();
10842 //dt = Thu May 25 2006 (today's month/day in 2006)
10843 dt = Date.parseDate("2006", "Y");
10845 //dt = Sun Jan 15 2006 (all date parts specified)
10846 dt = Date.parseDate("2006-01-15", "Y-m-d");
10848 //dt = Sun Jan 15 2006 15:20:01
10849 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
10851 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
10852 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
10854 * @param {String} input The raw date string.
10855 * @param {String} format The expected date string format.
10856 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
10857 (defaults to false). Invalid date strings will return null when parsed.
10858 * @return {Date} The parsed Date.
10861 parseDate : function(input, format, strict) {
10862 var p = Date.parseFunctions;
10863 if (p[format] == null) {
10864 Date.createParser(format);
10866 return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
10870 getFormatCode : function(character) {
10871 var f = Date.formatCodes[character];
10874 f = typeof f == 'function'? f() : f;
10875 Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
10878 // note: unknown characters are treated as literals
10879 return f || ("'" + String.escape(character) + "'");
10883 createFormat : function(format) {
10888 for (var i = 0; i < format.length; ++i) {
10889 ch = format.charAt(i);
10890 if (!special && ch == "\\") {
10892 } else if (special) {
10894 code.push("'" + String.escape(ch) + "'");
10896 code.push(Date.getFormatCode(ch))
10899 Date.formatFunctions[format] = new Function("return " + code.join('+'));
10903 createParser : function() {
10905 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
10906 "def = Date.defaults,",
10907 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
10912 "if(u != null){", // i.e. unix time is defined
10913 "v = new Date(u * 1000);", // give top priority to UNIX time
10915 // create Date object representing midnight of the current day;
10916 // this will provide us with our date defaults
10917 // (note: clearTime() handles Daylight Saving Time automatically)
10918 "dt = (new Date()).clearTime();",
10920 // date calculations (note: these calculations create a dependency on Ext.num())
10921 "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
10922 "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
10923 "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
10925 // time calculations (note: these calculations create a dependency on Ext.num())
10926 "h = Ext.num(h, Ext.num(def.h, dt.getHours()));",
10927 "i = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
10928 "s = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
10929 "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
10931 "if(z >= 0 && y >= 0){",
10932 // both the year and zero-based day of year are defined and >= 0.
10933 // these 2 values alone provide sufficient info to create a full date object
10935 // create Date object representing January 1st for the given year
10936 "v = new Date(y, 0, 1, h, i, s, ms);",
10938 // then add day of year, checking for Date "rollover" if necessary
10939 "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
10940 "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
10941 "v = null;", // invalid date, so return null
10943 // plain old Date object
10944 "v = new Date(y, m, d, h, i, s, ms);",
10950 // favour UTC offset over GMT offset
10952 // reset to UTC, then add offset
10953 "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
10955 // reset to GMT, then add offset
10956 "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
10963 return function(format) {
10964 var regexNum = Date.parseRegexes.length,
10971 for (var i = 0; i < format.length; ++i) {
10972 ch = format.charAt(i);
10973 if (!special && ch == "\\") {
10975 } else if (special) {
10977 regex.push(String.escape(ch));
10979 var obj = $f(ch, currentGroup);
10980 currentGroup += obj.g;
10982 if (obj.g && obj.c) {
10988 Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", "i");
10989 Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
10997 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
10998 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
10999 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
11003 c:"d = parseInt(results[{0}], 10);\n",
11004 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
11008 c:"d = parseInt(results[{0}], 10);\n",
11009 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
11012 for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
11016 s:"(?:" + a.join("|") +")"
11023 s:"(?:" + Date.dayNames.join("|") + ")"
11029 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
11034 s:"(?:st|nd|rd|th)"
11039 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
11043 c:"z = parseInt(results[{0}], 10);\n",
11044 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
11049 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
11054 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
11055 s:"(" + Date.monthNames.join("|") + ")"
11059 for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
11060 return Ext.applyIf({
11061 s:"(" + a.join("|") + ")"
11066 c:"m = parseInt(results[{0}], 10) - 1;\n",
11067 s:"(\\d{2})" // month number with leading zeros (01 - 12)
11071 c:"m = parseInt(results[{0}], 10) - 1;\n",
11072 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
11077 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
11089 c:"y = parseInt(results[{0}], 10);\n",
11090 s:"(\\d{4})" // 4-digit year
11094 c:"var ty = parseInt(results[{0}], 10);\n"
11095 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
11100 c:"if (results[{0}] == 'am') {\n"
11101 + "if (!h || h == 12) { h = 0; }\n"
11102 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11107 c:"if (results[{0}] == 'AM') {\n"
11108 + "if (!h || h == 12) { h = 0; }\n"
11109 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
11117 c:"h = parseInt(results[{0}], 10);\n",
11118 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
11125 c:"h = parseInt(results[{0}], 10);\n",
11126 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
11130 c:"i = parseInt(results[{0}], 10);\n",
11131 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
11135 c:"s = parseInt(results[{0}], 10);\n",
11136 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
11140 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
11141 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
11146 "o = results[{0}];",
11147 "var sn = o.substring(0,1),", // get + / - sign
11148 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11149 "mn = o.substring(3,5) % 60;", // get minutes
11150 "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
11152 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
11157 "o = results[{0}];",
11158 "var sn = o.substring(0,1),", // get + / - sign
11159 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
11160 "mn = o.substring(4,6) % 60;", // get minutes
11161 "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
11163 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
11168 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
11172 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
11173 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
11174 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
11179 $f("Y", 1), // year
11180 $f("m", 2), // month
11182 $f("h", 4), // hour
11183 $f("i", 5), // minute
11184 $f("s", 6), // second
11185 {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)
11186 {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
11187 "if(results[8]) {", // timezone specified
11188 "if(results[8] == 'Z'){",
11190 "}else if (results[8].indexOf(':') > -1){",
11191 $f("P", 8).c, // timezone offset with colon separator
11193 $f("O", 8).c, // timezone offset without colon separator
11199 for (var i = 0, l = arr.length; i < l; ++i) {
11200 calc.push(arr[i].c);
11207 arr[0].s, // year (required)
11208 "(?:", "-", arr[1].s, // month (optional)
11209 "(?:", "-", arr[2].s, // day (optional)
11211 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
11212 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
11213 "(?::", arr[5].s, ")?", // seconds (optional)
11214 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
11215 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
11224 c:"u = parseInt(results[{0}], 10);\n",
11225 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
11232 Ext.apply(Date.prototype, {
11234 dateFormat : function(format) {
11235 if (Date.formatFunctions[format] == null) {
11236 Date.createFormat(format);
11238 return Date.formatFunctions[format].call(this);
11242 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
11244 * Note: The date string returned by the javascript Date object's toString() method varies
11245 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
11246 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
11247 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
11248 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
11249 * from the GMT offset portion of the date string.
11250 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
11252 getTimezone : function() {
11253 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
11255 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
11256 // 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)
11257 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
11258 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
11259 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
11261 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
11262 // step 1: (?:\((.*)\) -- find timezone in parentheses
11263 // 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
11264 // step 3: remove all non uppercase characters found in step 1 and 2
11265 return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
11269 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
11270 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
11271 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
11273 getGMTOffset : function(colon) {
11274 return (this.getTimezoneOffset() > 0 ? "-" : "+")
11275 + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
11276 + (colon ? ":" : "")
11277 + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
11281 * Get the numeric day number of the year, adjusted for leap year.
11282 * @return {Number} 0 to 364 (365 in leap years).
11284 getDayOfYear: function() {
11287 m = this.getMonth(),
11290 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
11291 num += d.getDaysInMonth();
11293 return num + this.getDate() - 1;
11297 * Get the numeric ISO-8601 week number of the year.
11298 * (equivalent to the format specifier 'W', but without a leading zero).
11299 * @return {Number} 1 to 53
11301 getWeekOfYear : function() {
11302 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
11303 var ms1d = 864e5, // milliseconds in a day
11304 ms7d = 7 * ms1d; // milliseconds in a week
11306 return function() { // return a closure so constants get calculated only once
11307 var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
11308 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
11309 Wyr = new Date(AWN * ms7d).getUTCFullYear();
11311 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
11316 * Checks if the current date falls within a leap year.
11317 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
11319 isLeapYear : function() {
11320 var year = this.getFullYear();
11321 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
11325 * Get the first day of the current month, adjusted for leap year. The returned value
11326 * is the numeric day index within the week (0-6) which can be used in conjunction with
11327 * the {@link #monthNames} array to retrieve the textual day name.
11330 var dt = new Date('1/10/2007');
11331 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
11333 * @return {Number} The day number (0-6).
11335 getFirstDayOfMonth : function() {
11336 var day = (this.getDay() - (this.getDate() - 1)) % 7;
11337 return (day < 0) ? (day + 7) : day;
11341 * Get the last day of the current month, adjusted for leap year. The returned value
11342 * is the numeric day index within the week (0-6) which can be used in conjunction with
11343 * the {@link #monthNames} array to retrieve the textual day name.
11346 var dt = new Date('1/10/2007');
11347 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
11349 * @return {Number} The day number (0-6).
11351 getLastDayOfMonth : function() {
11352 return this.getLastDateOfMonth().getDay();
11357 * Get the date of the first day of the month in which this date resides.
11360 getFirstDateOfMonth : function() {
11361 return new Date(this.getFullYear(), this.getMonth(), 1);
11365 * Get the date of the last day of the month in which this date resides.
11368 getLastDateOfMonth : function() {
11369 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
11373 * Get the number of days in the current month, adjusted for leap year.
11374 * @return {Number} The number of days in the month.
11376 getDaysInMonth: function() {
11377 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
11379 return function() { // return a closure for efficiency
11380 var m = this.getMonth();
11382 return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
11387 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
11388 * @return {String} 'st, 'nd', 'rd' or 'th'.
11390 getSuffix : function() {
11391 switch (this.getDate()) {
11408 * Creates and returns a new Date instance with the exact same date value as the called instance.
11409 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
11410 * variable will also be changed. When the intention is to create a new variable that will not
11411 * modify the original instance, you should create a clone.
11413 * Example of correctly cloning a date:
11416 var orig = new Date('10/1/2006');
11419 document.write(orig); //returns 'Thu Oct 05 2006'!
11422 var orig = new Date('10/1/2006');
11423 var copy = orig.clone();
11425 document.write(orig); //returns 'Thu Oct 01 2006'
11427 * @return {Date} The new Date instance.
11429 clone : function() {
11430 return new Date(this.getTime());
11434 * Checks if the current date is affected by Daylight Saving Time (DST).
11435 * @return {Boolean} True if the current date is affected by DST.
11437 isDST : function() {
11438 // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
11439 // courtesy of @geoffrey.mcgill
11440 return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
11444 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
11445 * automatically adjusting for Daylight Saving Time (DST) where applicable.
11446 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
11447 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
11448 * @return {Date} this or the clone.
11450 clearTime : function(clone) {
11452 return this.clone().clearTime();
11455 // get current date before clearing time
11456 var d = this.getDate();
11460 this.setMinutes(0);
11461 this.setSeconds(0);
11462 this.setMilliseconds(0);
11464 if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
11465 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
11466 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
11468 // increment hour until cloned date == current date
11469 for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
11472 this.setHours(c.getHours());
11479 * Provides a convenient method for performing basic date arithmetic. This method
11480 * does not modify the Date instance being called - it creates and returns
11481 * a new Date instance containing the resulting date value.
11486 var dt = new Date('10/29/2006').add(Date.DAY, 5);
11487 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
11489 // Negative values will be subtracted:
11490 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
11491 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
11493 // You can even chain several calls together in one line:
11494 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
11495 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
11498 * @param {String} interval A valid date interval enum value.
11499 * @param {Number} value The amount to add to the current date.
11500 * @return {Date} The new Date instance.
11502 add : function(interval, value) {
11503 var d = this.clone();
11504 if (!interval || value === 0) return d;
11506 switch(interval.toLowerCase()) {
11508 d.setMilliseconds(this.getMilliseconds() + value);
11511 d.setSeconds(this.getSeconds() + value);
11514 d.setMinutes(this.getMinutes() + value);
11517 d.setHours(this.getHours() + value);
11520 d.setDate(this.getDate() + value);
11523 var day = this.getDate();
11525 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
11528 d.setMonth(this.getMonth() + value);
11531 d.setFullYear(this.getFullYear() + value);
11538 * Checks if this date falls on or between the given start and end dates.
11539 * @param {Date} start Start date
11540 * @param {Date} end End date
11541 * @return {Boolean} true if this date falls on or between the given start and end dates.
11543 between : function(start, end) {
11544 var t = this.getTime();
11545 return start.getTime() <= t && t <= end.getTime();
11551 * Formats a date given the supplied format string.
11552 * @param {String} format The format string.
11553 * @return {String} The formatted date.
11556 Date.prototype.format = Date.prototype.dateFormat;
11560 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
11561 Ext.apply(Date.prototype, {
11562 _xMonth : Date.prototype.setMonth,
11563 _xDate : Date.prototype.setDate,
11565 // Bug in Safari 1.3, 2.0 (WebKit build < 420)
11566 // Date.setMonth does not work consistently if iMonth is not 0-11
11567 setMonth : function(num) {
11569 var n = Math.ceil(-num),
11570 back_year = Math.ceil(n / 12),
11571 month = (n % 12) ? 12 - n % 12 : 0;
11573 this.setFullYear(this.getFullYear() - back_year);
11575 return this._xMonth(month);
11577 return this._xMonth(num);
11581 // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
11582 // The parameter for Date.setDate() is converted to a signed byte integer in Safari
11583 // http://brianary.blogspot.com/2006/03/safari-date-bug.html
11584 setDate : function(d) {
11585 // use setTime() to workaround setDate() bug
11586 // subtract current day of month in milliseconds, then add desired day of month in milliseconds
11587 return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
11594 /* Some basic Date tests... (requires Firebug)
11596 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
11597 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
11600 console.group('Standard Date.parseDate() Tests');
11601 console.log('Date.parseDate("2009-01-05T11:38:56", "c") = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
11602 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
11603 console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c") = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
11604 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
11605 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
11606 console.groupEnd();
11608 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
11609 // -- accepts ALL 6 levels of date-time granularity
11610 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
11611 console.log('Date.parseDate("1997", "c") = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
11612 console.log('Date.parseDate("1997-07", "c") = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
11613 console.log('Date.parseDate("1997-07-16", "c") = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
11614 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)
11615 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)
11616 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)
11617 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)
11618 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
11619 console.groupEnd();
11622 * @class Ext.util.MixedCollection
11623 * @extends Ext.util.Observable
11624 * A Collection class that maintains both numeric indexes and keys and exposes events.
11626 * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11627 * function should add function references to the collection. Defaults to
11629 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
11630 * and return the key value for that item. This is used when available to look up the key on items that
11631 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
11632 * equivalent to providing an implementation for the {@link #getKey} method.
11634 Ext.util.MixedCollection = function(allowFunctions, keyFn){
11642 * Fires when the collection is cleared.
11647 * Fires when an item is added to the collection.
11648 * @param {Number} index The index at which the item was added.
11649 * @param {Object} o The item added.
11650 * @param {String} key The key associated with the added item.
11655 * Fires when an item is replaced in the collection.
11656 * @param {String} key he key associated with the new added.
11657 * @param {Object} old The item being replaced.
11658 * @param {Object} new The new item.
11663 * Fires when an item is removed from the collection.
11664 * @param {Object} o The item being removed.
11665 * @param {String} key (optional) The key associated with the removed item.
11670 this.allowFunctions = allowFunctions === true;
11672 this.getKey = keyFn;
11674 Ext.util.MixedCollection.superclass.constructor.call(this);
11677 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
11680 * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
11681 * function should add function references to the collection. Defaults to
11684 allowFunctions : false,
11687 * Adds an item to the collection. Fires the {@link #add} event when complete.
11688 * @param {String} key <p>The key to associate with the item, or the new item.</p>
11689 * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
11690 * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
11691 * the MixedCollection will be able to <i>derive</i> the key for the new item.
11692 * In this case just pass the new item in this parameter.</p>
11693 * @param {Object} o The item to add.
11694 * @return {Object} The item added.
11696 add : function(key, o){
11697 if(arguments.length == 1){
11699 key = this.getKey(o);
11701 if(typeof key != 'undefined' && key !== null){
11702 var old = this.map[key];
11703 if(typeof old != 'undefined'){
11704 return this.replace(key, o);
11709 this.items.push(o);
11710 this.keys.push(key);
11711 this.fireEvent('add', this.length-1, o, key);
11716 * MixedCollection has a generic way to fetch keys if you implement getKey. The default implementation
11717 * simply returns <b><code>item.id</code></b> but you can provide your own implementation
11718 * to return a different value as in the following examples:<pre><code>
11720 var mc = new Ext.util.MixedCollection();
11721 mc.add(someEl.dom.id, someEl);
11722 mc.add(otherEl.dom.id, otherEl);
11726 var mc = new Ext.util.MixedCollection();
11727 mc.getKey = function(el){
11733 // or via the constructor
11734 var mc = new Ext.util.MixedCollection(false, function(el){
11740 * @param {Object} item The item for which to find the key.
11741 * @return {Object} The key for the passed item.
11743 getKey : function(o){
11748 * Replaces an item in the collection. Fires the {@link #replace} event when complete.
11749 * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
11750 * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
11751 * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
11752 * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
11753 * with one having the same key value, then just pass the replacement item in this parameter.</p>
11754 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
11756 * @return {Object} The new item.
11758 replace : function(key, o){
11759 if(arguments.length == 1){
11761 key = this.getKey(o);
11763 var old = this.map[key];
11764 if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
11765 return this.add(key, o);
11767 var index = this.indexOfKey(key);
11768 this.items[index] = o;
11770 this.fireEvent('replace', key, old, o);
11775 * Adds all elements of an Array or an Object to the collection.
11776 * @param {Object/Array} objs An Object containing properties which will be added
11777 * to the collection, or an Array of values, each of which are added to the collection.
11778 * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
11779 * has been set to <tt>true</tt>.
11781 addAll : function(objs){
11782 if(arguments.length > 1 || Ext.isArray(objs)){
11783 var args = arguments.length > 1 ? arguments : objs;
11784 for(var i = 0, len = args.length; i < len; i++){
11788 for(var key in objs){
11789 if(this.allowFunctions || typeof objs[key] != 'function'){
11790 this.add(key, objs[key]);
11797 * Executes the specified function once for every item in the collection, passing the following arguments:
11798 * <div class="mdetail-params"><ul>
11799 * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
11800 * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
11801 * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
11803 * The function should return a boolean value. Returning false from the function will stop the iteration.
11804 * @param {Function} fn The function to execute for each item.
11805 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
11807 each : function(fn, scope){
11808 var items = [].concat(this.items); // each safe for removal
11809 for(var i = 0, len = items.length; i < len; i++){
11810 if(fn.call(scope || items[i], items[i], i, len) === false){
11817 * Executes the specified function once for every key in the collection, passing each
11818 * key, and its associated item as the first two parameters.
11819 * @param {Function} fn The function to execute for each item.
11820 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11822 eachKey : function(fn, scope){
11823 for(var i = 0, len = this.keys.length; i < len; i++){
11824 fn.call(scope || window, this.keys[i], this.items[i], i, len);
11829 * Returns the first item in the collection which elicits a true return value from the
11830 * passed selection function.
11831 * @param {Function} fn The selection function to execute for each item.
11832 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
11833 * @return {Object} The first item in the collection which returned true from the selection function.
11835 find : function(fn, scope){
11836 for(var i = 0, len = this.items.length; i < len; i++){
11837 if(fn.call(scope || window, this.items[i], this.keys[i])){
11838 return this.items[i];
11845 * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
11846 * @param {Number} index The index to insert the item at.
11847 * @param {String} key The key to associate with the new item, or the item itself.
11848 * @param {Object} o (optional) If the second parameter was a key, the new item.
11849 * @return {Object} The item inserted.
11851 insert : function(index, key, o){
11852 if(arguments.length == 2){
11854 key = this.getKey(o);
11856 if(this.containsKey(key)){
11857 this.suspendEvents();
11858 this.removeKey(key);
11859 this.resumeEvents();
11861 if(index >= this.length){
11862 return this.add(key, o);
11865 this.items.splice(index, 0, o);
11866 if(typeof key != 'undefined' && key !== null){
11869 this.keys.splice(index, 0, key);
11870 this.fireEvent('add', index, o, key);
11875 * Remove an item from the collection.
11876 * @param {Object} o The item to remove.
11877 * @return {Object} The item removed or false if no item was removed.
11879 remove : function(o){
11880 return this.removeAt(this.indexOf(o));
11884 * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
11885 * @param {Number} index The index within the collection of the item to remove.
11886 * @return {Object} The item removed or false if no item was removed.
11888 removeAt : function(index){
11889 if(index < this.length && index >= 0){
11891 var o = this.items[index];
11892 this.items.splice(index, 1);
11893 var key = this.keys[index];
11894 if(typeof key != 'undefined'){
11895 delete this.map[key];
11897 this.keys.splice(index, 1);
11898 this.fireEvent('remove', o, key);
11905 * Removed an item associated with the passed key fom the collection.
11906 * @param {String} key The key of the item to remove.
11907 * @return {Object} The item removed or false if no item was removed.
11909 removeKey : function(key){
11910 return this.removeAt(this.indexOfKey(key));
11914 * Returns the number of items in the collection.
11915 * @return {Number} the number of items in the collection.
11917 getCount : function(){
11918 return this.length;
11922 * Returns index within the collection of the passed Object.
11923 * @param {Object} o The item to find the index of.
11924 * @return {Number} index of the item. Returns -1 if not found.
11926 indexOf : function(o){
11927 return this.items.indexOf(o);
11931 * Returns index within the collection of the passed key.
11932 * @param {String} key The key to find the index of.
11933 * @return {Number} index of the key.
11935 indexOfKey : function(key){
11936 return this.keys.indexOf(key);
11940 * Returns the item associated with the passed key OR index.
11941 * Key has priority over index. This is the equivalent
11942 * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
11943 * @param {String/Number} key The key or index of the item.
11944 * @return {Object} If the item is found, returns the item. If the item was not found, returns <tt>undefined</tt>.
11945 * If an item was found, but is a Class, returns <tt>null</tt>.
11947 item : function(key){
11948 var mk = this.map[key],
11949 item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
11950 return !Ext.isFunction(item) || this.allowFunctions ? item : null; // for prototype!
11954 * Returns the item at the specified index.
11955 * @param {Number} index The index of the item.
11956 * @return {Object} The item at the specified index.
11958 itemAt : function(index){
11959 return this.items[index];
11963 * Returns the item associated with the passed key.
11964 * @param {String/Number} key The key of the item.
11965 * @return {Object} The item associated with the passed key.
11967 key : function(key){
11968 return this.map[key];
11972 * Returns true if the collection contains the passed Object as an item.
11973 * @param {Object} o The Object to look for in the collection.
11974 * @return {Boolean} True if the collection contains the Object as an item.
11976 contains : function(o){
11977 return this.indexOf(o) != -1;
11981 * Returns true if the collection contains the passed Object as a key.
11982 * @param {String} key The key to look for in the collection.
11983 * @return {Boolean} True if the collection contains the Object as a key.
11985 containsKey : function(key){
11986 return typeof this.map[key] != 'undefined';
11990 * Removes all items from the collection. Fires the {@link #clear} event when complete.
11992 clear : function(){
11997 this.fireEvent('clear');
12001 * Returns the first item in the collection.
12002 * @return {Object} the first item in the collection..
12004 first : function(){
12005 return this.items[0];
12009 * Returns the last item in the collection.
12010 * @return {Object} the last item in the collection..
12013 return this.items[this.length-1];
12018 * @param {String} property Property to sort by ('key', 'value', or 'index')
12019 * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
12020 * @param {Function} fn (optional) Comparison function that defines the sort order.
12021 * Defaults to sorting by numeric value.
12023 _sort : function(property, dir, fn){
12026 dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
12027 c = [], k = this.keys, items = this.items;
12029 fn = fn || function(a, b){
12032 for(i = 0, len = items.length; i < len; i++){
12033 c[c.length] = {key: k[i], value: items[i], index: i};
12035 c.sort(function(a, b){
12036 var v = fn(a[property], b[property]) * dsc;
12038 v = (a.index < b.index ? -1 : 1);
12042 for(i = 0, len = c.length; i < len; i++){
12043 items[i] = c[i].value;
12046 this.fireEvent('sort', this);
12050 * Sorts this collection by <b>item</b> value with the passed comparison function.
12051 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12052 * @param {Function} fn (optional) Comparison function that defines the sort order.
12053 * Defaults to sorting by numeric value.
12055 sort : function(dir, fn){
12056 this._sort('value', dir, fn);
12060 * Sorts this collection by <b>key</b>s.
12061 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
12062 * @param {Function} fn (optional) Comparison function that defines the sort order.
12063 * Defaults to sorting by case insensitive string.
12065 keySort : function(dir, fn){
12066 this._sort('key', dir, fn || function(a, b){
12067 var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
12068 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
12073 * Returns a range of items in this collection
12074 * @param {Number} startIndex (optional) The starting index. Defaults to 0.
12075 * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
12076 * @return {Array} An array of items
12078 getRange : function(start, end){
12079 var items = this.items;
12080 if(items.length < 1){
12083 start = start || 0;
12084 end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
12087 for(i = start; i <= end; i++) {
12088 r[r.length] = items[i];
12091 for(i = start; i >= end; i--) {
12092 r[r.length] = items[i];
12099 * Filter the <i>objects</i> in this collection by a specific property.
12100 * Returns a new collection that has been filtered.
12101 * @param {String} property A property on your objects
12102 * @param {String/RegExp} value Either string that the property values
12103 * should start with or a RegExp to test against the property
12104 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
12105 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
12106 * @return {MixedCollection} The new filtered collection
12108 filter : function(property, value, anyMatch, caseSensitive){
12109 if(Ext.isEmpty(value, false)){
12110 return this.clone();
12112 value = this.createValueMatcher(value, anyMatch, caseSensitive);
12113 return this.filterBy(function(o){
12114 return o && value.test(o[property]);
12119 * Filter by a function. Returns a <i>new</i> collection that has been filtered.
12120 * The passed function will be called with each object in the collection.
12121 * If the function returns true, the value is included otherwise it is filtered.
12122 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12123 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12124 * @return {MixedCollection} The new filtered collection
12126 filterBy : function(fn, scope){
12127 var r = new Ext.util.MixedCollection();
12128 r.getKey = this.getKey;
12129 var k = this.keys, it = this.items;
12130 for(var i = 0, len = it.length; i < len; i++){
12131 if(fn.call(scope||this, it[i], k[i])){
12132 r.add(k[i], it[i]);
12139 * Finds the index of the first matching object in this collection by a specific property/value.
12140 * @param {String} property The name of a property on your objects.
12141 * @param {String/RegExp} value A string that the property values
12142 * should start with or a RegExp to test against the property.
12143 * @param {Number} start (optional) The index to start searching at (defaults to 0).
12144 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
12145 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
12146 * @return {Number} The matched index or -1
12148 findIndex : function(property, value, start, anyMatch, caseSensitive){
12149 if(Ext.isEmpty(value, false)){
12152 value = this.createValueMatcher(value, anyMatch, caseSensitive);
12153 return this.findIndexBy(function(o){
12154 return o && value.test(o[property]);
12159 * Find the index of the first matching object in this collection by a function.
12160 * If the function returns <i>true</i> it is considered a match.
12161 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
12162 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
12163 * @param {Number} start (optional) The index to start searching at (defaults to 0).
12164 * @return {Number} The matched index or -1
12166 findIndexBy : function(fn, scope, start){
12167 var k = this.keys, it = this.items;
12168 for(var i = (start||0), len = it.length; i < len; i++){
12169 if(fn.call(scope||this, it[i], k[i])){
12177 createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
12178 if (!value.exec) { // not a regex
12179 var er = Ext.escapeRe;
12180 value = String(value);
12181 if (anyMatch === true) {
12184 value = '^' + er(value);
12185 if (exactMatch === true) {
12189 value = new RegExp(value, caseSensitive ? '' : 'i');
12195 * Creates a shallow copy of this collection
12196 * @return {MixedCollection}
12198 clone : function(){
12199 var r = new Ext.util.MixedCollection();
12200 var k = this.keys, it = this.items;
12201 for(var i = 0, len = it.length; i < len; i++){
12202 r.add(k[i], it[i]);
12204 r.getKey = this.getKey;
12209 * This method calls {@link #item item()}.
12210 * Returns the item associated with the passed key OR index. Key has priority
12211 * over index. This is the equivalent of calling {@link #key} first, then if
12212 * nothing matched calling {@link #itemAt}.
12213 * @param {String/Number} key The key or index of the item.
12214 * @return {Object} If the item is found, returns the item. If the item was
12215 * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
12216 * returns <tt>null</tt>.
12218 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;/**
12219 * @class Ext.util.JSON
12220 * Modified version of Douglas Crockford"s json.js that doesn"t
12221 * mess with the Object prototype
12222 * http://www.json.org/js.html
12225 Ext.util.JSON = new (function(){
12226 var useHasOwn = !!{}.hasOwnProperty,
12227 isNative = function() {
12228 var useNative = null;
12230 return function() {
12231 if (useNative === null) {
12232 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
12238 pad = function(n) {
12239 return n < 10 ? "0" + n : n;
12241 doDecode = function(json){
12242 return eval("(" + json + ')');
12244 doEncode = function(o){
12245 if(!Ext.isDefined(o) || o === null){
12247 }else if(Ext.isArray(o)){
12248 return encodeArray(o);
12249 }else if(Ext.isDate(o)){
12250 return Ext.util.JSON.encodeDate(o);
12251 }else if(Ext.isString(o)){
12252 return encodeString(o);
12253 }else if(typeof o == "number"){
12254 //don't use isNumber here, since finite checks happen inside isNumber
12255 return isFinite(o) ? String(o) : "null";
12256 }else if(Ext.isBoolean(o)){
12259 var a = ["{"], b, i, v;
12261 // don't encode DOM objects
12262 if(!o.getElementsByTagName){
12263 if(!useHasOwn || o.hasOwnProperty(i)) {
12265 switch (typeof v) {
12274 a.push(doEncode(i), ":",
12275 v === null ? "null" : doEncode(v));
12294 encodeString = function(s){
12295 if (/["\\\x00-\x1f]/.test(s)) {
12296 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12301 c = b.charCodeAt();
12303 Math.floor(c / 16).toString(16) +
12304 (c % 16).toString(16);
12307 return '"' + s + '"';
12309 encodeArray = function(o){
12310 var a = ["["], b, i, l = o.length, v;
12311 for (i = 0; i < l; i += 1) {
12313 switch (typeof v) {
12322 a.push(v === null ? "null" : Ext.util.JSON.encode(v));
12331 * <p>Encodes a Date. This returns the actual string which is inserted into the JSON string as the literal expression.
12332 * <b>The returned value includes enclosing double quotation marks.</b></p>
12333 * <p>The default return format is "yyyy-mm-ddThh:mm:ss".</p>
12334 * <p>To override this:</p><pre><code>
12335 Ext.util.JSON.encodeDate = function(d) {
12336 return d.format('"Y-m-d"');
12339 * @param {Date} d The Date to encode
12340 * @return {String} The string literal to use in a JSON string.
12342 this.encodeDate = function(o){
12343 return '"' + o.getFullYear() + "-" +
12344 pad(o.getMonth() + 1) + "-" +
12345 pad(o.getDate()) + "T" +
12346 pad(o.getHours()) + ":" +
12347 pad(o.getMinutes()) + ":" +
12348 pad(o.getSeconds()) + '"';
12352 * Encodes an Object, Array or other value
12353 * @param {Mixed} o The variable to encode
12354 * @return {String} The JSON string
12356 this.encode = function() {
12358 return function(o) {
12360 // setup encoding function on first access
12361 ec = isNative() ? JSON.stringify : doEncode;
12369 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
12370 * @param {String} json The JSON string
12371 * @return {Object} The resulting object
12373 this.decode = function() {
12375 return function(json) {
12377 // setup decoding function on first access
12378 dc = isNative() ? JSON.parse : doDecode;
12386 * Shorthand for {@link Ext.util.JSON#encode}
12387 * @param {Mixed} o The variable to encode
12388 * @return {String} The JSON string
12392 Ext.encode = Ext.util.JSON.encode;
12394 * Shorthand for {@link Ext.util.JSON#decode}
12395 * @param {String} json The JSON string
12396 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
12397 * @return {Object} The resulting object
12401 Ext.decode = Ext.util.JSON.decode;
12403 * @class Ext.util.Format
\r
12404 * Reusable data formatting functions
\r
12407 Ext.util.Format = function(){
\r
12408 var trimRe = /^\s+|\s+$/g,
\r
12409 stripTagsRE = /<\/?[^>]+>/gi,
\r
12410 stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
\r
12411 nl2brRe = /\r?\n/g;
\r
12415 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
\r
12416 * @param {String} value The string to truncate
\r
12417 * @param {Number} length The maximum length to allow before truncating
\r
12418 * @param {Boolean} word True to try to find a common work break
\r
12419 * @return {String} The converted text
\r
12421 ellipsis : function(value, len, word){
\r
12422 if(value && value.length > len){
\r
12424 var vs = value.substr(0, len - 2),
\r
12425 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
\r
12426 if(index == -1 || index < (len - 15)){
\r
12427 return value.substr(0, len - 3) + "...";
\r
12429 return vs.substr(0, index) + "...";
\r
12432 return value.substr(0, len - 3) + "...";
\r
12439 * Checks a reference and converts it to empty string if it is undefined
\r
12440 * @param {Mixed} value Reference to check
\r
12441 * @return {Mixed} Empty string if converted, otherwise the original value
\r
12443 undef : function(value){
\r
12444 return value !== undefined ? value : "";
\r
12448 * Checks a reference and converts it to the default value if it's empty
\r
12449 * @param {Mixed} value Reference to check
\r
12450 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
\r
12451 * @return {String}
\r
12453 defaultValue : function(value, defaultValue){
\r
12454 return value !== undefined && value !== '' ? value : defaultValue;
\r
12458 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
\r
12459 * @param {String} value The string to encode
\r
12460 * @return {String} The encoded text
\r
12462 htmlEncode : function(value){
\r
12463 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
\r
12467 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
\r
12468 * @param {String} value The string to decode
\r
12469 * @return {String} The decoded text
\r
12471 htmlDecode : function(value){
\r
12472 return !value ? value : String(value).replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&");
\r
12476 * Trims any whitespace from either side of a string
\r
12477 * @param {String} value The text to trim
\r
12478 * @return {String} The trimmed text
\r
12480 trim : function(value){
\r
12481 return String(value).replace(trimRe, "");
\r
12485 * Returns a substring from within an original string
\r
12486 * @param {String} value The original text
\r
12487 * @param {Number} start The start index of the substring
\r
12488 * @param {Number} length The length of the substring
\r
12489 * @return {String} The substring
\r
12491 substr : function(value, start, length){
\r
12492 return String(value).substr(start, length);
\r
12496 * Converts a string to all lower case letters
\r
12497 * @param {String} value The text to convert
\r
12498 * @return {String} The converted text
\r
12500 lowercase : function(value){
\r
12501 return String(value).toLowerCase();
\r
12505 * Converts a string to all upper case letters
\r
12506 * @param {String} value The text to convert
\r
12507 * @return {String} The converted text
\r
12509 uppercase : function(value){
\r
12510 return String(value).toUpperCase();
\r
12514 * Converts the first character only of a string to upper case
\r
12515 * @param {String} value The text to convert
\r
12516 * @return {String} The converted text
\r
12518 capitalize : function(value){
\r
12519 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
\r
12523 call : function(value, fn){
\r
12524 if(arguments.length > 2){
\r
12525 var args = Array.prototype.slice.call(arguments, 2);
\r
12526 args.unshift(value);
\r
12527 return eval(fn).apply(window, args);
\r
12529 return eval(fn).call(window, value);
\r
12534 * Format a number as US currency
\r
12535 * @param {Number/String} value The numeric value to format
\r
12536 * @return {String} The formatted currency string
\r
12538 usMoney : function(v){
\r
12539 v = (Math.round((v-0)*100))/100;
\r
12540 v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
\r
12542 var ps = v.split('.'),
\r
12544 sub = ps[1] ? '.'+ ps[1] : '.00',
\r
12545 r = /(\d+)(\d{3})/;
\r
12546 while (r.test(whole)) {
\r
12547 whole = whole.replace(r, '$1' + ',' + '$2');
\r
12550 if(v.charAt(0) == '-'){
\r
12551 return '-$' + v.substr(1);
\r
12557 * Parse a value into a formatted date using the specified format pattern.
\r
12558 * @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)
\r
12559 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
\r
12560 * @return {String} The formatted date string
\r
12562 date : function(v, format){
\r
12566 if(!Ext.isDate(v)){
\r
12567 v = new Date(Date.parse(v));
\r
12569 return v.dateFormat(format || "m/d/Y");
\r
12573 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
\r
12574 * @param {String} format Any valid date format string
\r
12575 * @return {Function} The date formatting function
\r
12577 dateRenderer : function(format){
\r
12578 return function(v){
\r
12579 return Ext.util.Format.date(v, format);
\r
12584 * Strips all HTML tags
\r
12585 * @param {Mixed} value The text from which to strip tags
\r
12586 * @return {String} The stripped text
\r
12588 stripTags : function(v){
\r
12589 return !v ? v : String(v).replace(stripTagsRE, "");
\r
12593 * Strips all script tags
\r
12594 * @param {Mixed} value The text from which to strip script tags
\r
12595 * @return {String} The stripped text
\r
12597 stripScripts : function(v){
\r
12598 return !v ? v : String(v).replace(stripScriptsRe, "");
\r
12602 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
\r
12603 * @param {Number/String} size The numeric value to format
\r
12604 * @return {String} The formatted file size
\r
12606 fileSize : function(size){
\r
12607 if(size < 1024) {
\r
12608 return size + " bytes";
\r
12609 } else if(size < 1048576) {
\r
12610 return (Math.round(((size*10) / 1024))/10) + " KB";
\r
12612 return (Math.round(((size*10) / 1048576))/10) + " MB";
\r
12617 * It does simple math for use in a template, for example:<pre><code>
\r
12618 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
\r
12620 * @return {Function} A function that operates on the passed value.
\r
12622 math : function(){
\r
12624 return function(v, a){
\r
12626 fns[a] = new Function('v', 'return v ' + a + ';');
\r
12628 return fns[a](v);
\r
12633 * Rounds the passed number to the required decimal precision.
\r
12634 * @param {Number/String} value The numeric value to round.
\r
12635 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
\r
12636 * @return {Number} The rounded value.
\r
12638 round : function(value, precision) {
\r
12639 var result = Number(value);
\r
12640 if (typeof precision == 'number') {
\r
12641 precision = Math.pow(10, precision);
\r
12642 result = Math.round(value * precision) / precision;
\r
12648 * Formats the number according to the format string.
\r
12649 * <div style="margin-left:40px">examples (123456.789):
\r
12650 * <div style="margin-left:10px">
\r
12651 * 0 - (123456) show only digits, no precision<br>
\r
12652 * 0.00 - (123456.78) show only digits, 2 precision<br>
\r
12653 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
\r
12654 * 0,000 - (123,456) show comma and digits, no precision<br>
\r
12655 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
\r
12656 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
\r
12657 * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
\r
12658 * For example: 0.000,00/i
\r
12660 * @param {Number} v The number to format.
\r
12661 * @param {String} format The way you would like to format this text.
\r
12662 * @return {String} The formatted number.
\r
12664 number: function(v, format) {
\r
12668 v = Ext.num(v, NaN);
\r
12678 if(format.substr(format.length - 2) == '/i'){
\r
12679 format = format.substr(0, format.length - 2);
\r
12685 var hasComma = format.indexOf(comma) != -1,
\r
12686 psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
\r
12688 if(1 < psplit.length){
\r
12689 v = v.toFixed(psplit[1].length);
\r
12690 }else if(2 < psplit.length){
\r
12691 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
\r
12693 v = v.toFixed(0);
\r
12696 var fnum = v.toString();
\r
12698 psplit = fnum.split('.');
\r
12700 var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;
\r
12702 for(var i = 0; i < j; i += n){
\r
12706 parr[parr.length] = cnum.substr(i, n);
\r
12709 fnum = parr.join(comma);
\r
12711 fnum += dec + psplit[1];
\r
12715 return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
\r
12719 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
\r
12720 * @param {String} format Any valid number format string for {@link #number}
\r
12721 * @return {Function} The number formatting function
\r
12723 numberRenderer : function(format){
\r
12724 return function(v){
\r
12725 return Ext.util.Format.number(v, format);
\r
12730 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
\r
12731 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
\r
12732 * if the value is 0 or greater than 1.
\r
12733 * @param {Number} value The value to compare against
\r
12734 * @param {String} singular The singular form of the word
\r
12735 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
\r
12737 plural : function(v, s, p){
\r
12738 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
\r
12742 * Converts newline characters to the HTML tag <br/>
\r
12743 * @param {String} The string value to format.
\r
12744 * @return {String} The string with embedded <br/> tags in place of newlines.
\r
12746 nl2br : function(v){
\r
12747 return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
\r
12752 * @class Ext.XTemplate
12753 * @extends Ext.Template
12754 * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
12755 * <li>Autofilling arrays using templates and sub-templates</li>
12756 * <li>Conditional processing with basic comparison operators</li>
12757 * <li>Basic math function support</li>
12758 * <li>Execute arbitrary inline code with special built-in template variables</li>
12759 * <li>Custom member functions</li>
12760 * <li>Many special tags and built-in operators that aren't defined as part of
12761 * the API, but are supported in the templates that can be created</li>
12763 * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
12764 * <li>{@link Ext.DataView}</li>
12765 * <li>{@link Ext.ListView}</li>
12766 * <li>{@link Ext.form.ComboBox}</li>
12767 * <li>{@link Ext.grid.TemplateColumn}</li>
12768 * <li>{@link Ext.grid.GroupingView}</li>
12769 * <li>{@link Ext.menu.Item}</li>
12770 * <li>{@link Ext.layout.MenuLayout}</li>
12771 * <li>{@link Ext.ColorPalette}</li>
12774 * <p>For example usage {@link #XTemplate see the constructor}.</p>
12777 * The {@link Ext.Template#Template Ext.Template constructor} describes
12778 * the acceptable parameters to pass to the constructor. The following
12779 * examples demonstrate all of the supported features.</p>
12781 * <div class="mdetail-params"><ul>
12783 * <li><b><u>Sample Data</u></b>
12784 * <div class="sub-desc">
12785 * <p>This is the data object used for reference in each code example:</p>
12788 name: 'Jack Slocum',
12789 title: 'Lead Developer',
12790 company: 'Ext JS, LLC',
12791 email: 'jack@extjs.com',
12792 address: '4 Red Bulls Drive',
12796 drinks: ['Red Bull', 'Coffee', 'Water'],
12798 name: 'Sara Grace',
12804 name: 'John James',
12813 * <li><b><u>Auto filling of arrays</u></b>
12814 * <div class="sub-desc">
12815 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
12816 * to process the provided data object:
12818 * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
12819 * repeating the template block inside the <tt>tpl</tt> tag for each item in the
12821 * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
12822 * <li>While processing an array, the special variable <tt>{#}</tt>
12823 * will provide the current array index + 1 (starts at 1, not 0).</li>
12827 <tpl <b>for</b>=".">...</tpl> // loop through array at root node
12828 <tpl <b>for</b>="foo">...</tpl> // loop through array at foo node
12829 <tpl <b>for</b>="foo.bar">...</tpl> // loop through array at foo.bar node
12831 * Using the sample data above:
12833 var tpl = new Ext.XTemplate(
12835 '<tpl <b>for</b>=".">', // process the data.kids node
12836 '<p>{#}. {name}</p>', // use current array index to autonumber
12839 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
12841 * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
12842 * to access specified members of the provided data object to populate the template:</p>
12844 var tpl = new Ext.XTemplate(
12845 '<p>Name: {name}</p>',
12846 '<p>Title: {title}</p>',
12847 '<p>Company: {company}</p>',
12849 '<tpl <b>for="kids"</b>>', // interrogate the kids property within the data
12850 '<p>{name}</p>',
12853 tpl.overwrite(panel.body, data); // pass the root node of the data object
12855 * <p>Flat arrays that contain values (and not objects) can be auto-rendered
12856 * using the special <b><tt>{.}</tt></b> variable inside a loop. This variable
12857 * will represent the value of the array at the current index:</p>
12859 var tpl = new Ext.XTemplate(
12860 '<p>{name}\'s favorite beverages:</p>',
12861 '<tpl for="drinks">',
12862 '<div> - {.}</div>',
12865 tpl.overwrite(panel.body, data);
12867 * <p>When processing a sub-template, for example while looping through a child array,
12868 * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
12870 var tpl = new Ext.XTemplate(
12871 '<p>Name: {name}</p>',
12873 '<tpl for="kids">',
12874 '<tpl if="age > 1">',
12875 '<p>{name}</p>',
12876 '<p>Dad: {<b>parent</b>.name}</p>',
12880 tpl.overwrite(panel.body, data);
12886 * <li><b><u>Conditional processing with basic comparison operators</u></b>
12887 * <div class="sub-desc">
12888 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
12889 * to provide conditional checks for deciding whether or not to render specific
12890 * parts of the template. Notes:<div class="sub-desc"><ul>
12891 * <li>Double quotes must be encoded if used within the conditional</li>
12892 * <li>There is no <tt>else</tt> operator — if needed, two opposite
12893 * <tt>if</tt> statements should be used.</li>
12896 <tpl if="age > 1 && age < 10">Child</tpl>
12897 <tpl if="age >= 10 && age < 18">Teenager</tpl>
12898 <tpl <b>if</b>="this.isGirl(name)">...</tpl>
12899 <tpl <b>if</b>="id==\'download\'">...</tpl>
12900 <tpl <b>if</b>="needsIcon"><img src="{icon}" class="{iconCls}"/></tpl>
12902 <tpl if="name == "Jack"">Hello</tpl>
12903 // encode " if it is part of the condition, e.g.
12904 <tpl if="name == &quot;Jack&quot;">Hello</tpl>
12906 * Using the sample data above:
12908 var tpl = new Ext.XTemplate(
12909 '<p>Name: {name}</p>',
12911 '<tpl for="kids">',
12912 '<tpl if="age > 1">',
12913 '<p>{name}</p>',
12917 tpl.overwrite(panel.body, data);
12923 * <li><b><u>Basic math support</u></b>
12924 * <div class="sub-desc">
12925 * <p>The following basic math operators may be applied directly on numeric
12926 * data values:</p><pre>
12931 var tpl = new Ext.XTemplate(
12932 '<p>Name: {name}</p>',
12934 '<tpl for="kids">',
12935 '<tpl if="age &gt; 1">', // <-- Note that the > is encoded
12936 '<p>{#}: {name}</p>', // <-- Auto-number each item
12937 '<p>In 5 Years: {age+5}</p>', // <-- Basic math
12938 '<p>Dad: {parent.name}</p>',
12942 tpl.overwrite(panel.body, data);
12948 * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
12949 * <div class="sub-desc">
12950 * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
12951 * in the scope of the template. There are some special variables available in that code:
12953 * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
12954 * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
12955 * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
12956 * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
12957 * loop you are in (1-based).</li>
12958 * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
12959 * of the array you are looping.</li>
12960 * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
12962 * This example demonstrates basic row striping using an inline code block and the
12963 * <tt>xindex</tt> variable:</p>
12965 var tpl = new Ext.XTemplate(
12966 '<p>Name: {name}</p>',
12967 '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
12969 '<tpl for="kids">',
12970 '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
12975 tpl.overwrite(panel.body, data);
12980 * <li><b><u>Template member functions</u></b>
12981 * <div class="sub-desc">
12982 * <p>One or more member functions can be specified in a configuration
12983 * object passed into the XTemplate constructor for more complex processing:</p>
12985 var tpl = new Ext.XTemplate(
12986 '<p>Name: {name}</p>',
12988 '<tpl for="kids">',
12989 '<tpl if="this.isGirl(name)">',
12990 '<p>Girl: {name} - {age}</p>',
12992 // use opposite if statement to simulate 'else' processing:
12993 '<tpl if="this.isGirl(name) == false">',
12994 '<p>Boy: {name} - {age}</p>',
12996 '<tpl if="this.isBaby(age)">',
12997 '<p>{name} is a baby!</p>',
12999 '</tpl></p>',
13001 // XTemplate configuration:
13003 disableFormats: true,
13004 // member functions:
13005 isGirl: function(name){
13006 return name == 'Sara Grace';
13008 isBaby: function(age){
13013 tpl.overwrite(panel.body, data);
13020 * @param {Mixed} config
13022 Ext.XTemplate = function(){
13023 Ext.XTemplate.superclass.constructor.apply(this, arguments);
13027 re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
13028 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
13029 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
13030 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
13038 RETURN = 'return ',
13039 WITHVALUES = 'with(values){ ';
13041 s = ['<tpl>', s, '</tpl>'].join('');
13043 while((m = s.match(re))){
13044 var m2 = m[0].match(nameRe),
13045 m3 = m[0].match(ifRe),
13046 m4 = m[0].match(execRe),
13050 name = m2 && m2[1] ? m2[1] : '';
13053 exp = m3 && m3[1] ? m3[1] : null;
13055 fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
13059 exp = m4 && m4[1] ? m4[1] : null;
13061 exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
13066 case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
13067 case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
13068 default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
13078 s = s.replace(m[0], '{xtpl'+ id + '}');
13081 Ext.each(tpls, function(t) {
13084 me.master = tpls[tpls.length-1];
13087 Ext.extend(Ext.XTemplate, Ext.Template, {
13089 re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
13091 codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
13094 applySubTemplate : function(id, values, parent, xindex, xcount){
13100 if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
13101 (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
13104 vs = t.target ? t.target.call(me, values, parent) : values;
13106 parent = t.target ? values : parent;
13107 if(t.target && Ext.isArray(vs)){
13108 Ext.each(vs, function(v, i) {
13109 buf[buf.length] = t.compiled.call(me, v, parent, i+1, len);
13111 return buf.join('');
13113 return t.compiled.call(me, vs, parent, xindex, xcount);
13117 compileTpl : function(tpl){
13118 var fm = Ext.util.Format,
13119 useF = this.disableFormats !== true,
13120 sep = Ext.isGecko ? "+" : ",",
13123 function fn(m, name, format, args, math){
13124 if(name.substr(0, 4) == 'xtpl'){
13125 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
13130 }else if(name === '#'){
13132 }else if(name.indexOf('.') != -1){
13135 v = "values['" + name + "']";
13138 v = '(' + v + math + ')';
13140 if (format && useF) {
13141 args = args ? ',' + args : "";
13142 if(format.substr(0, 5) != "this."){
13143 format = "fm." + format + '(';
13145 format = 'this.call("'+ format.substr(5) + '", ';
13149 args= ''; format = "("+v+" === undefined ? '' : ";
13151 return "'"+ sep + format + v + args + ")"+sep+"'";
13154 function codeFn(m, code){
13155 // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
13156 return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
13159 // branched to use + in gecko and [].join() in others
13161 body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
13162 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
13165 body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
13166 body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
13167 body.push("'].join('');};");
13168 body = body.join('');
13175 * Returns an HTML fragment of this template with the specified values applied.
13176 * @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'})
13177 * @return {String} The HTML fragment
13179 applyTemplate : function(values){
13180 return this.master.compiled.call(this, values, {}, 1, 1);
13184 * Compile the template to a function for optimized performance. Recommended if the template will be used frequently.
13185 * @return {Function} The compiled function
13187 compile : function(){return this;}
13194 * @property disableFormats
13204 * Alias for {@link #applyTemplate}
13205 * Returns an HTML fragment of this template with the specified values applied.
13206 * @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'})
13207 * @return {String} The HTML fragment
13208 * @member Ext.XTemplate
13211 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
13214 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
13215 * @param {String/HTMLElement} el A DOM element or its id
13216 * @return {Ext.Template} The created template
13219 Ext.XTemplate.from = function(el){
13220 el = Ext.getDom(el);
13221 return new Ext.XTemplate(el.value || el.innerHTML);
13223 * @class Ext.util.CSS
\r
13224 * Utility class for manipulating CSS rules
\r
13227 Ext.util.CSS = function(){
\r
13228 var rules = null;
\r
13229 var doc = document;
\r
13231 var camelRe = /(-[a-z])/gi;
\r
13232 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
\r
13236 * Creates a stylesheet from a text blob of rules.
\r
13237 * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
\r
13238 * @param {String} cssText The text containing the css rules
\r
13239 * @param {String} id An id to add to the stylesheet for later removal
\r
13240 * @return {StyleSheet}
\r
13242 createStyleSheet : function(cssText, id){
\r
13244 var head = doc.getElementsByTagName("head")[0];
\r
13245 var rules = doc.createElement("style");
\r
13246 rules.setAttribute("type", "text/css");
\r
13248 rules.setAttribute("id", id);
\r
13251 head.appendChild(rules);
\r
13252 ss = rules.styleSheet;
\r
13253 ss.cssText = cssText;
\r
13256 rules.appendChild(doc.createTextNode(cssText));
\r
13258 rules.cssText = cssText;
\r
13260 head.appendChild(rules);
\r
13261 ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
\r
13263 this.cacheStyleSheet(ss);
\r
13268 * Removes a style or link tag by id
\r
13269 * @param {String} id The id of the tag
\r
13271 removeStyleSheet : function(id){
\r
13272 var existing = doc.getElementById(id);
\r
13274 existing.parentNode.removeChild(existing);
\r
13279 * Dynamically swaps an existing stylesheet reference for a new one
\r
13280 * @param {String} id The id of an existing link tag to remove
\r
13281 * @param {String} url The href of the new stylesheet to include
\r
13283 swapStyleSheet : function(id, url){
\r
13284 this.removeStyleSheet(id);
\r
13285 var ss = doc.createElement("link");
\r
13286 ss.setAttribute("rel", "stylesheet");
\r
13287 ss.setAttribute("type", "text/css");
\r
13288 ss.setAttribute("id", id);
\r
13289 ss.setAttribute("href", url);
\r
13290 doc.getElementsByTagName("head")[0].appendChild(ss);
\r
13294 * Refresh the rule cache if you have dynamically added stylesheets
\r
13295 * @return {Object} An object (hash) of rules indexed by selector
\r
13297 refreshCache : function(){
\r
13298 return this.getRules(true);
\r
13302 cacheStyleSheet : function(ss){
\r
13306 try{// try catch for cross domain access issue
\r
13307 var ssRules = ss.cssRules || ss.rules;
\r
13308 for(var j = ssRules.length-1; j >= 0; --j){
\r
13309 rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
\r
13315 * Gets all css rules for the document
\r
13316 * @param {Boolean} refreshCache true to refresh the internal cache
\r
13317 * @return {Object} An object (hash) of rules indexed by selector
\r
13319 getRules : function(refreshCache){
\r
13320 if(rules === null || refreshCache){
\r
13322 var ds = doc.styleSheets;
\r
13323 for(var i =0, len = ds.length; i < len; i++){
\r
13325 this.cacheStyleSheet(ds[i]);
\r
13333 * Gets an an individual CSS rule by selector(s)
\r
13334 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
\r
13335 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
\r
13336 * @return {CSSRule} The CSS rule or null if one is not found
\r
13338 getRule : function(selector, refreshCache){
\r
13339 var rs = this.getRules(refreshCache);
\r
13340 if(!Ext.isArray(selector)){
\r
13341 return rs[selector.toLowerCase()];
\r
13343 for(var i = 0; i < selector.length; i++){
\r
13344 if(rs[selector[i]]){
\r
13345 return rs[selector[i].toLowerCase()];
\r
13353 * Updates a rule property
\r
13354 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
\r
13355 * @param {String} property The css property
\r
13356 * @param {String} value The new value for the property
\r
13357 * @return {Boolean} true If a rule was found and updated
\r
13359 updateRule : function(selector, property, value){
\r
13360 if(!Ext.isArray(selector)){
\r
13361 var rule = this.getRule(selector);
\r
13363 rule.style[property.replace(camelRe, camelFn)] = value;
\r
13367 for(var i = 0; i < selector.length; i++){
\r
13368 if(this.updateRule(selector[i], property, value)){
\r
13377 @class Ext.util.ClickRepeater
13378 @extends Ext.util.Observable
13380 A wrapper class which can be applied to any element. Fires a "click" event while the
13381 mouse is pressed. The interval between firings may be specified in the config but
13382 defaults to 20 milliseconds.
13384 Optionally, a CSS class may be applied to the element during the time it is pressed.
13386 @cfg {Mixed} el The element to act as a button.
13387 @cfg {Number} delay The initial delay before the repeating event begins firing.
13388 Similar to an autorepeat key delay.
13389 @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
13390 @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13391 @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13392 "interval" and "delay" are ignored.
13393 @cfg {Boolean} preventDefault True to prevent the default click event
13394 @cfg {Boolean} stopDefault True to stop the default click event
13397 2007-02-02 jvs Original code contributed by Nige "Animal" White
13398 2007-02-02 jvs Renamed to ClickRepeater
13399 2007-02-03 jvs Modifications for FF Mac and Safari
13402 @param {Mixed} el The element to listen on
13403 @param {Object} config
13405 Ext.util.ClickRepeater = function(el, config)
13407 this.el = Ext.get(el);
13408 this.el.unselectable();
13410 Ext.apply(this, config);
13415 * Fires when the mouse button is depressed.
13416 * @param {Ext.util.ClickRepeater} this
13421 * Fires on a specified interval during the time the element is pressed.
13422 * @param {Ext.util.ClickRepeater} this
13427 * Fires when the mouse key is released.
13428 * @param {Ext.util.ClickRepeater} this
13433 if(!this.disabled){
13434 this.disabled = true;
13438 // allow inline handler
13440 this.on("click", this.handler, this.scope || this);
13443 Ext.util.ClickRepeater.superclass.constructor.call(this);
13446 Ext.extend(Ext.util.ClickRepeater, Ext.util.Observable, {
13449 preventDefault : true,
13450 stopDefault : false,
13454 * Enables the repeater and allows events to fire.
13456 enable: function(){
13458 this.el.on('mousedown', this.handleMouseDown, this);
13459 if(this.preventDefault || this.stopDefault){
13460 this.el.on('click', this.eventOptions, this);
13463 this.disabled = false;
13467 * Disables the repeater and stops events from firing.
13469 disable: function(/* private */ force){
13470 if(force || !this.disabled){
13471 clearTimeout(this.timer);
13472 if(this.pressClass){
13473 this.el.removeClass(this.pressClass);
13475 Ext.getDoc().un('mouseup', this.handleMouseUp, this);
13476 this.el.removeAllListeners();
13478 this.disabled = true;
13482 * Convenience function for setting disabled/enabled by boolean.
13483 * @param {Boolean} disabled
13485 setDisabled: function(disabled){
13486 this[disabled ? 'disable' : 'enable']();
13489 eventOptions: function(e){
13490 if(this.preventDefault){
13491 e.preventDefault();
13493 if(this.stopDefault){
13499 destroy : function() {
13500 this.disable(true);
13501 Ext.destroy(this.el);
13502 this.purgeListeners();
13506 handleMouseDown : function(){
13507 clearTimeout(this.timer);
13509 if(this.pressClass){
13510 this.el.addClass(this.pressClass);
13512 this.mousedownTime = new Date();
13514 Ext.getDoc().on("mouseup", this.handleMouseUp, this);
13515 this.el.on("mouseout", this.handleMouseOut, this);
13517 this.fireEvent("mousedown", this);
13518 this.fireEvent("click", this);
13520 // Do not honor delay or interval if acceleration wanted.
13521 if (this.accelerate) {
13524 this.timer = this.click.defer(this.delay || this.interval, this);
13528 click : function(){
13529 this.fireEvent("click", this);
13530 this.timer = this.click.defer(this.accelerate ?
13531 this.easeOutExpo(this.mousedownTime.getElapsed(),
13535 this.interval, this);
13538 easeOutExpo : function (t, b, c, d) {
13539 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
13543 handleMouseOut : function(){
13544 clearTimeout(this.timer);
13545 if(this.pressClass){
13546 this.el.removeClass(this.pressClass);
13548 this.el.on("mouseover", this.handleMouseReturn, this);
13552 handleMouseReturn : function(){
13553 this.el.un("mouseover", this.handleMouseReturn, this);
13554 if(this.pressClass){
13555 this.el.addClass(this.pressClass);
13561 handleMouseUp : function(){
13562 clearTimeout(this.timer);
13563 this.el.un("mouseover", this.handleMouseReturn, this);
13564 this.el.un("mouseout", this.handleMouseOut, this);
13565 Ext.getDoc().un("mouseup", this.handleMouseUp, this);
13566 this.el.removeClass(this.pressClass);
13567 this.fireEvent("mouseup", this);
13570 * @class Ext.KeyNav
13571 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
13572 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13573 * way to implement custom navigation schemes for any UI component.</p>
13574 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13575 * pageUp, pageDown, del, home, end. Usage:</p>
13577 var nav = new Ext.KeyNav("my-element", {
13578 "left" : function(e){
13579 this.moveLeft(e.ctrlKey);
13581 "right" : function(e){
13582 this.moveRight(e.ctrlKey);
13584 "enter" : function(e){
13591 * @param {Mixed} el The element to bind to
13592 * @param {Object} config The config
13594 Ext.KeyNav = function(el, config){
13595 this.el = Ext.get(el);
13596 Ext.apply(this, config);
13597 if(!this.disabled){
13598 this.disabled = true;
13603 Ext.KeyNav.prototype = {
13605 * @cfg {Boolean} disabled
13606 * True to disable this KeyNav instance (defaults to false)
13610 * @cfg {String} defaultEventAction
13611 * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key. Valid values are
13612 * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
13613 * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
13615 defaultEventAction: "stopEvent",
13617 * @cfg {Boolean} forceKeyDown
13618 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
13619 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13620 * handle keydown instead of keypress.
13622 forceKeyDown : false,
13625 relay : function(e){
13626 var k = e.getKey();
13627 var h = this.keyToHandler[k];
13629 if(this.doRelay(e, this[h], h) !== true){
13630 e[this.defaultEventAction]();
13636 doRelay : function(e, h, hname){
13637 return h.call(this.scope || this, e);
13640 // possible handlers
13654 // quick lookup hash
13670 stopKeyUp: function(e) {
13671 var k = e.getKey();
13673 if (k >= 37 && k <= 40) {
13674 // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
13675 // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
13681 * Destroy this KeyNav (this is the same as calling disable).
13683 destroy: function(){
13688 * Enable this KeyNav
13690 enable: function() {
13691 if (this.disabled) {
13692 if (Ext.isSafari2) {
13693 // call stopKeyUp() on "keyup" event
13694 this.el.on('keyup', this.stopKeyUp, this);
13697 this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13698 this.disabled = false;
13703 * Disable this KeyNav
13705 disable: function() {
13706 if (!this.disabled) {
13707 if (Ext.isSafari2) {
13708 // remove "keyup" event handler
13709 this.el.un('keyup', this.stopKeyUp, this);
13712 this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
13713 this.disabled = true;
13718 * Convenience function for setting disabled/enabled by boolean.
13719 * @param {Boolean} disabled
13721 setDisabled : function(disabled){
13722 this[disabled ? "disable" : "enable"]();
13726 isKeydown: function(){
13727 return this.forceKeyDown || Ext.EventManager.useKeydown;
13731 * @class Ext.KeyMap
\r
13732 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
\r
13733 * The constructor accepts the same config object as defined by {@link #addBinding}.
\r
13734 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
\r
13735 * combination it will call the function with this signature (if the match is a multi-key
\r
13736 * combination the callback will still be called only once): (String key, Ext.EventObject e)
\r
13737 * A KeyMap can also handle a string representation of keys.<br />
\r
13740 // map one key by key code
\r
13741 var map = new Ext.KeyMap("my-element", {
\r
13742 key: 13, // or Ext.EventObject.ENTER
\r
13747 // map multiple keys to one action by string
\r
13748 var map = new Ext.KeyMap("my-element", {
\r
13754 // map multiple keys to multiple actions by strings and array of codes
\r
13755 var map = new Ext.KeyMap("my-element", [
\r
13758 fn: function(){ alert("Return was pressed"); }
\r
13761 fn: function(){ alert('a, b or c was pressed'); }
\r
13766 fn: function(){ alert('Control + shift + tab was pressed.'); }
\r
13770 * <b>Note: A KeyMap starts enabled</b>
\r
13772 * @param {Mixed} el The element to bind to
\r
13773 * @param {Object} config The config (see {@link #addBinding})
\r
13774 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
\r
13776 Ext.KeyMap = function(el, config, eventName){
\r
13777 this.el = Ext.get(el);
\r
13778 this.eventName = eventName || "keydown";
\r
13779 this.bindings = [];
\r
13781 this.addBinding(config);
\r
13786 Ext.KeyMap.prototype = {
\r
13788 * True to stop the event from bubbling and prevent the default browser action if the
\r
13789 * key was handled by the KeyMap (defaults to false)
\r
13792 stopEvent : false,
\r
13795 * Add a new binding to this KeyMap. The following config object properties are supported:
\r
13797 Property Type Description
\r
13798 ---------- --------------- ----------------------------------------------------------------------
\r
13799 key String/Array A single keycode or an array of keycodes to handle
\r
13800 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)
\r
13801 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)
\r
13802 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)
\r
13803 handler Function The function to call when KeyMap finds the expected key combination
\r
13804 fn Function Alias of handler (for backwards-compatibility)
\r
13805 scope Object The scope of the callback function
\r
13806 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)
\r
13811 // Create a KeyMap
\r
13812 var map = new Ext.KeyMap(document, {
\r
13813 key: Ext.EventObject.ENTER,
\r
13818 //Add a new binding to the existing KeyMap later
\r
13826 * @param {Object/Array} config A single KeyMap config or an array of configs
\r
13828 addBinding : function(config){
\r
13829 if(Ext.isArray(config)){
\r
13830 Ext.each(config, function(c){
\r
13831 this.addBinding(c);
\r
13835 var keyCode = config.key,
\r
13836 fn = config.fn || config.handler,
\r
13837 scope = config.scope;
\r
13839 if (config.stopEvent) {
\r
13840 this.stopEvent = config.stopEvent;
\r
13843 if(typeof keyCode == "string"){
\r
13845 var keyString = keyCode.toUpperCase();
\r
13846 for(var j = 0, len = keyString.length; j < len; j++){
\r
13847 ks.push(keyString.charCodeAt(j));
\r
13851 var keyArray = Ext.isArray(keyCode);
\r
13853 var handler = function(e){
\r
13854 if(this.checkModifiers(config, e)){
\r
13855 var k = e.getKey();
\r
13857 for(var i = 0, len = keyCode.length; i < len; i++){
\r
13858 if(keyCode[i] == k){
\r
13859 if(this.stopEvent){
\r
13862 fn.call(scope || window, k, e);
\r
13867 if(k == keyCode){
\r
13868 if(this.stopEvent){
\r
13871 fn.call(scope || window, k, e);
\r
13876 this.bindings.push(handler);
\r
13880 checkModifiers: function(config, e){
\r
13881 var val, key, keys = ['shift', 'ctrl', 'alt'];
\r
13882 for (var i = 0, len = keys.length; i < len; ++i){
\r
13884 val = config[key];
\r
13885 if(!(val === undefined || (val === e[key + 'Key']))){
\r
13893 * Shorthand for adding a single key listener
\r
13894 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
\r
13895 * following options:
\r
13896 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
\r
13897 * @param {Function} fn The function to call
\r
13898 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
\r
13900 on : function(key, fn, scope){
\r
13901 var keyCode, shift, ctrl, alt;
\r
13902 if(typeof key == "object" && !Ext.isArray(key)){
\r
13903 keyCode = key.key;
\r
13904 shift = key.shift;
\r
13910 this.addBinding({
\r
13921 handleKeyDown : function(e){
\r
13922 if(this.enabled){ //just in case
\r
13923 var b = this.bindings;
\r
13924 for(var i = 0, len = b.length; i < len; i++){
\r
13925 b[i].call(this, e);
\r
13931 * Returns true if this KeyMap is enabled
\r
13932 * @return {Boolean}
\r
13934 isEnabled : function(){
\r
13935 return this.enabled;
\r
13939 * Enables this KeyMap
\r
13941 enable: function(){
\r
13942 if(!this.enabled){
\r
13943 this.el.on(this.eventName, this.handleKeyDown, this);
\r
13944 this.enabled = true;
\r
13949 * Disable this KeyMap
\r
13951 disable: function(){
\r
13952 if(this.enabled){
\r
13953 this.el.removeListener(this.eventName, this.handleKeyDown, this);
\r
13954 this.enabled = false;
\r
13959 * Convenience function for setting disabled/enabled by boolean.
\r
13960 * @param {Boolean} disabled
\r
13962 setDisabled : function(disabled){
\r
13963 this[disabled ? "disable" : "enable"]();
\r
13966 * @class Ext.util.TextMetrics
13967 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13968 * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
13969 * should not contain any HTML, otherwise it may not be measured correctly.
13972 Ext.util.TextMetrics = function(){
13976 * Measures the size of the specified text
13977 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13978 * that can affect the size of the rendered text
13979 * @param {String} text The text to measure
13980 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13981 * in order to accurately measure the text height
13982 * @return {Object} An object containing the text's size {width: (width), height: (height)}
13984 measure : function(el, text, fixedWidth){
13986 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
13989 shared.setFixedWidth(fixedWidth || 'auto');
13990 return shared.getSize(text);
13994 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
13995 * the overhead of multiple calls to initialize the style properties on each measurement.
13996 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13997 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13998 * in order to accurately measure the text height
13999 * @return {Ext.util.TextMetrics.Instance} instance The new instance
14001 createInstance : function(el, fixedWidth){
14002 return Ext.util.TextMetrics.Instance(el, fixedWidth);
14007 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14008 var ml = new Ext.Element(document.createElement('div'));
14009 document.body.appendChild(ml.dom);
14010 ml.position('absolute');
14011 ml.setLeftTop(-1000, -1000);
14015 ml.setWidth(fixedWidth);
14020 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14021 * Returns the size of the specified text based on the internal element's style and width properties
14022 * @param {String} text The text to measure
14023 * @return {Object} An object containing the text's size {width: (width), height: (height)}
14025 getSize : function(text){
14027 var s = ml.getSize();
14033 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14034 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14035 * that can affect the size of the rendered text
14036 * @param {String/HTMLElement} el The element, dom node or id
14038 bind : function(el){
14040 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
14045 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14046 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
14047 * to set a fixed width in order to accurately measure the text height.
14048 * @param {Number} width The width to set on the element
14050 setFixedWidth : function(width){
14051 ml.setWidth(width);
14055 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14056 * Returns the measured width of the specified text
14057 * @param {String} text The text to measure
14058 * @return {Number} width The width in pixels
14060 getWidth : function(text){
14061 ml.dom.style.width = 'auto';
14062 return this.getSize(text).width;
14066 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
14067 * Returns the measured height of the specified text. For multiline text, be sure to call
14068 * {@link #setFixedWidth} if necessary.
14069 * @param {String} text The text to measure
14070 * @return {Number} height The height in pixels
14072 getHeight : function(text){
14073 return this.getSize(text).height;
14077 instance.bind(bindTo);
14082 Ext.Element.addMethods({
14084 * Returns the width in pixels of the passed text, or the width of the text in this Element.
14085 * @param {String} text The text to measure. Defaults to the innerHTML of the element.
14086 * @param {Number} min (Optional) The minumum value to return.
14087 * @param {Number} max (Optional) The maximum value to return.
14088 * @return {Number} The text width in pixels.
14089 * @member Ext.Element getTextWidth
14091 getTextWidth : function(text, min, max){
14092 return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
14096 * @class Ext.util.Cookies
\r
14097 * Utility class for managing and interacting with cookies.
\r
14100 Ext.util.Cookies = {
\r
14102 * Create a cookie with the specified name and value. Additional settings
\r
14103 * for the cookie may be optionally specified (for example: expiration,
\r
14104 * access restriction, SSL).
\r
14105 * @param {String} name The name of the cookie to set.
\r
14106 * @param {Mixed} value The value to set for the cookie.
\r
14107 * @param {Object} expires (Optional) Specify an expiration date the
\r
14108 * cookie is to persist until. Note that the specified Date object will
\r
14109 * be converted to Greenwich Mean Time (GMT).
\r
14110 * @param {String} path (Optional) Setting a path on the cookie restricts
\r
14111 * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>).
\r
14112 * @param {String} domain (Optional) Setting a domain restricts access to
\r
14113 * pages on a given domain (typically used to allow cookie access across
\r
14114 * subdomains). For example, "extjs.com" will create a cookie that can be
\r
14115 * accessed from any subdomain of extjs.com, including www.extjs.com,
\r
14116 * support.extjs.com, etc.
\r
14117 * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
\r
14118 * should only be accessible via SSL on a page using the HTTPS protocol.
\r
14119 * Defaults to <tt>false</tt>. Note that this will only work if the page
\r
14120 * calling this code uses the HTTPS protocol, otherwise the cookie will be
\r
14121 * created with default options.
\r
14123 set : function(name, value){
\r
14124 var argv = arguments;
\r
14125 var argc = arguments.length;
\r
14126 var expires = (argc > 2) ? argv[2] : null;
\r
14127 var path = (argc > 3) ? argv[3] : '/';
\r
14128 var domain = (argc > 4) ? argv[4] : null;
\r
14129 var secure = (argc > 5) ? argv[5] : false;
\r
14130 document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
\r
14134 * Retrieves cookies that are accessible by the current page. If a cookie
\r
14135 * does not exist, <code>get()</code> returns <tt>null</tt>. The following
\r
14136 * example retrieves the cookie called "valid" and stores the String value
\r
14137 * in the variable <tt>validStatus</tt>.
\r
14139 * var validStatus = Ext.util.Cookies.get("valid");
\r
14141 * @param {String} name The name of the cookie to get
\r
14142 * @return {Mixed} Returns the cookie value for the specified name;
\r
14143 * null if the cookie name does not exist.
\r
14145 get : function(name){
\r
14146 var arg = name + "=";
\r
14147 var alen = arg.length;
\r
14148 var clen = document.cookie.length;
\r
14153 if(document.cookie.substring(i, j) == arg){
\r
14154 return Ext.util.Cookies.getCookieVal(j);
\r
14156 i = document.cookie.indexOf(" ", i) + 1;
\r
14165 * Removes a cookie with the provided name from the browser
\r
14166 * if found by setting its expiration date to sometime in the past.
\r
14167 * @param {String} name The name of the cookie to remove
\r
14169 clear : function(name){
\r
14170 if(Ext.util.Cookies.get(name)){
\r
14171 document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
\r
14177 getCookieVal : function(offset){
\r
14178 var endstr = document.cookie.indexOf(";", offset);
\r
14179 if(endstr == -1){
\r
14180 endstr = document.cookie.length;
\r
14182 return unescape(document.cookie.substring(offset, endstr));
\r
14185 * Framework-wide error-handler. Developers can override this method to provide
14186 * custom exception-handling. Framework errors will often extend from the base
14188 * @param {Object/Error} e The thrown exception object.
14190 Ext.handleError = function(e) {
14197 * <p>A base error class. Future implementations are intended to provide more
14198 * robust error handling throughout the framework (<b>in the debug build only</b>)
14199 * to check for common errors and problems. The messages issued by this class
14200 * will aid error checking. Error checks will be automatically removed in the
14201 * production build so that performance is not negatively impacted.</p>
14202 * <p>Some sample messages currently implemented:</p><pre>
14203 "DataProxy attempted to execute an API-action but found an undefined
14204 url / function. Please review your Proxy url/api-configuration."
14206 "Could not locate your "root" property in your server response.
14207 Please review your JsonReader config to ensure the config-property
14208 "root" matches the property your server-response. See the JsonReader
14209 docs for additional assistance."
14211 * <p>An example of the code used for generating error messages:</p><pre><code>
14220 function generateError(data) {
14221 throw new Ext.Error('foo-error', data);
14224 * @param {String} message
14226 Ext.Error = function(message) {
14227 // Try to read the message from Ext.Error.lang
14228 this.message = (this.lang[message]) ? this.lang[message] : message;
14230 Ext.Error.prototype = new Error();
14231 Ext.apply(Ext.Error.prototype, {
14232 // protected. Extensions place their error-strings here.
14240 getName : function() {
14247 getMessage : function() {
14248 return this.message;
14254 toJson : function() {
14255 return Ext.encode(this);