3 * Copyright(c) 2006-2009 Ext JS, LLC
5 * http://www.extjs.com/license
9 * <p>The DomHelper class provides a layer of abstraction from DOM and transparently supports creating
10 * elements via DOM or using HTML fragments. It also has the ability to create HTML fragment templates
11 * from your DOM building code.</p>
13 * <p><b><u>DomHelper element specification object</u></b></p>
14 * <p>A specification object is used when creating elements. Attributes of this object
15 * are assumed to be element attributes, except for 4 special attributes:
16 * <div class="mdetail-params"><ul>
17 * <li><b><tt>tag</tt></b> : <div class="sub-desc">The tag name of the element</div></li>
18 * <li><b><tt>children</tt></b> : or <tt>cn</tt><div class="sub-desc">An array of the
19 * same kind of element definition objects to be created and appended. These can be nested
20 * as deep as you want.</div></li>
21 * <li><b><tt>cls</tt></b> : <div class="sub-desc">The class attribute of the element.
22 * This will end up being either the "class" attribute on a HTML fragment or className
23 * for a DOM node, depending on whether DomHelper is using fragments or DOM.</div></li>
24 * <li><b><tt>html</tt></b> : <div class="sub-desc">The innerHTML for the element</div></li>
27 * <p><b><u>Insertion methods</u></b></p>
28 * <p>Commonly used insertion methods:
29 * <div class="mdetail-params"><ul>
30 * <li><b><tt>{@link #append}</tt></b> : <div class="sub-desc"></div></li>
31 * <li><b><tt>{@link #insertBefore}</tt></b> : <div class="sub-desc"></div></li>
32 * <li><b><tt>{@link #insertAfter}</tt></b> : <div class="sub-desc"></div></li>
33 * <li><b><tt>{@link #overwrite}</tt></b> : <div class="sub-desc"></div></li>
34 * <li><b><tt>{@link #createTemplate}</tt></b> : <div class="sub-desc"></div></li>
35 * <li><b><tt>{@link #insertHtml}</tt></b> : <div class="sub-desc"></div></li>
38 * <p><b><u>Example</u></b></p>
39 * <p>This is an example, where an unordered list with 3 children items is appended to an existing
40 * element with id <tt>'my-div'</tt>:<br>
42 var dh = Ext.DomHelper; // create shorthand alias
43 // specification object
48 // append children after creating
49 children: [ // may also specify 'cn' instead of 'children'
50 {tag: 'li', id: 'item0', html: 'List Item 0'},
51 {tag: 'li', id: 'item1', html: 'List Item 1'},
52 {tag: 'li', id: 'item2', html: 'List Item 2'}
56 'my-div', // the context element 'my-div' can either be the id or the actual node
57 spec // the specification object
60 * <p>Element creation specification parameters in this class may also be passed as an Array of
61 * specification objects. This can be used to insert multiple sibling nodes into an existing
62 * container very efficiently. For example, to add more list items to the example above:<pre><code>
64 {tag: 'li', id: 'item3', html: 'List Item 3'},
65 {tag: 'li', id: 'item4', html: 'List Item 4'}
69 * <p><b><u>Templating</u></b></p>
70 * <p>The real power is in the built-in templating. Instead of creating or appending any elements,
71 * <tt>{@link #createTemplate}</tt> returns a Template object which can be used over and over to
72 * insert new elements. Revisiting the example above, we could utilize templating this time:
75 var list = dh.append('my-div', {tag: 'ul', cls: 'my-list'});
77 var tpl = dh.createTemplate({tag: 'li', id: 'item{0}', html: 'List Item {0}'});
79 for(var i = 0; i < 5, i++){
80 tpl.append(list, [i]); // use template to append to the actual node
83 * <p>An example using a template:<pre><code>
84 var html = '<a id="{0}" href="{1}" class="nav">{2}</a>';
86 var tpl = new Ext.DomHelper.createTemplate(html);
87 tpl.append('blog-roll', ['link1', 'http://www.jackslocum.com/', "Jack's Site"]);
88 tpl.append('blog-roll', ['link2', 'http://www.dustindiaz.com/', "Dustin's Site"]);
91 * <p>The same example using named parameters:<pre><code>
92 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
94 var tpl = new Ext.DomHelper.createTemplate(html);
95 tpl.append('blog-roll', {
97 url: 'http://www.jackslocum.com/',
98 text: "Jack's Site"
100 tpl.append('blog-roll', {
102 url: 'http://www.dustindiaz.com/',
103 text: "Dustin's Site"
107 * <p><b><u>Compiling Templates</u></b></p>
108 * <p>Templates are applied using regular expressions. The performance is great, but if
109 * you are adding a bunch of DOM elements using the same template, you can increase
110 * performance even further by {@link Ext.Template#compile "compiling"} the template.
111 * The way "{@link Ext.Template#compile compile()}" works is the template is parsed and
112 * broken up at the different variable points and a dynamic function is created and eval'ed.
113 * The generated function performs string concatenation of these parts and the passed
114 * variables instead of using regular expressions.
116 var html = '<a id="{id}" href="{url}" class="nav">{text}</a>';
118 var tpl = new Ext.DomHelper.createTemplate(html);
121 //... use template like normal
124 * <p><b><u>Performance Boost</u></b></p>
125 * <p>DomHelper will transparently create HTML fragments when it can. Using HTML fragments instead
126 * of DOM can significantly boost performance.</p>
127 * <p>Element creation specification parameters may also be strings. If {@link #useDom} is <tt>false</tt>,
128 * then the string is used as innerHTML. If {@link #useDom} is <tt>true</tt>, a string specification
129 * results in the creation of a text node. Usage:</p>
131 Ext.DomHelper.useDom = true; // force it to use DOM; reduces performance
135 Ext.DomHelper = function(){
136 var tempTableEl = null,
137 emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
138 tableRe = /^table|tbody|tr|td$/i,
140 // kill repeat to save bytes
141 afterbegin = "afterbegin",
142 afterend = "afterend",
143 beforebegin = "beforebegin",
144 beforeend = "beforeend",
153 function doInsert(el, o, returnElement, pos, sibling, append){
154 var newNode = pub.insertHtml(pos, Ext.getDom(el), createHtml(o));
155 return returnElement ? Ext.get(newNode, true) : newNode;
158 // build as innerHTML where available
159 function createHtml(o){
167 if(typeof o == 'string'){
169 } else if (Ext.isArray(o)) {
170 Ext.each(o, function(v) {
174 b += "<" + (o.tag = o.tag || "div");
175 Ext.iterate(o, function(attr, val){
176 if(!/tag|children|cn|html$/i.test(attr)){
177 if (Ext.isObject(val)) {
178 b += " " + attr + "='";
179 Ext.iterate(val, function(key, keyVal){
180 b += key + ":" + keyVal + ";";
184 b += " " + ({cls : "class", htmlFor : "for"}[attr] || attr) + "='" + val + "'";
188 // Now either just close the tag or try to add children and close the tag.
189 if (emptyTags.test(o.tag)) {
193 if ((cn = o.children || o.cn)) {
198 b += "</" + o.tag + ">";
204 function ieTable(depth, s, h, e){
205 tempTableEl.innerHTML = [s, h, e].join('');
216 * Nasty code for IE's broken table implementation
218 function insertIntoTable(tag, where, el, html) {
222 tempTableEl = tempTableEl || document.createElement('div');
224 if(tag == 'td' && (where == afterbegin || where == beforeend) ||
225 !/td|tr|tbody/i.test(tag) && (where == beforebegin || where == afterend)) {
228 before = where == beforebegin ? el :
229 where == afterend ? el.nextSibling :
230 where == afterbegin ? el.firstChild : null;
232 if (where == beforebegin || where == afterend) {
236 if (tag == 'td' || (tag == "tr" && (where == beforeend || where == afterbegin))) {
237 node = ieTable(4, trs, html, tre);
238 } else if ((tag == "tbody" && (where == beforeend || where == afterbegin)) ||
239 (tag == "tr" && (where == beforebegin || where == afterend))) {
240 node = ieTable(3, tbs, html, tbe);
242 node = ieTable(2, ts, html, te);
244 el.insertBefore(node, before);
251 * Returns the markup for the passed Element(s) config.
252 * @param {Object} o The DOM object spec (and children)
255 markup : function(o){
256 return createHtml(o);
260 * Inserts an HTML fragment into the DOM.
261 * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
262 * @param {HTMLElement} el The context element
263 * @param {String} html The HTML fragmenet
264 * @return {HTMLElement} The new node
266 insertHtml : function(where, el, html){
275 where = where.toLowerCase();
276 // add these here because they are used in both branches of the condition.
277 hash[beforebegin] = ['BeforeBegin', 'previousSibling'];
278 hash[afterend] = ['AfterEnd', 'nextSibling'];
280 if (el.insertAdjacentHTML) {
281 if(tableRe.test(el.tagName) && (rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html))){
284 // add these two to the hash.
285 hash[afterbegin] = ['AfterBegin', 'firstChild'];
286 hash[beforeend] = ['BeforeEnd', 'lastChild'];
287 if ((hashVal = hash[where])) {
288 el.insertAdjacentHTML(hashVal[0], html);
289 return el[hashVal[1]];
292 range = el.ownerDocument.createRange();
293 setStart = "setStart" + (/end/i.test(where) ? "After" : "Before");
296 frag = range.createContextualFragment(html);
297 el.parentNode.insertBefore(frag, where == beforebegin ? el : el.nextSibling);
298 return el[(where == beforebegin ? "previous" : "next") + "Sibling"];
300 rangeEl = (where == afterbegin ? "first" : "last") + "Child";
302 range[setStart](el[rangeEl]);
303 frag = range.createContextualFragment(html);
304 if(where == afterbegin){
305 el.insertBefore(frag, el.firstChild);
307 el.appendChild(frag);
315 throw 'Illegal insertion point -> "' + where + '"';
319 * Creates new DOM element(s) and inserts them before el.
320 * @param {Mixed} el The context element
321 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
322 * @param {Boolean} returnElement (optional) true to return a Ext.Element
323 * @return {HTMLElement/Ext.Element} The new node
325 insertBefore : function(el, o, returnElement){
326 return doInsert(el, o, returnElement, beforebegin);
330 * Creates new DOM element(s) and inserts them after el.
331 * @param {Mixed} el The context element
332 * @param {Object} o The DOM object spec (and children)
333 * @param {Boolean} returnElement (optional) true to return a Ext.Element
334 * @return {HTMLElement/Ext.Element} The new node
336 insertAfter : function(el, o, returnElement){
337 return doInsert(el, o, returnElement, afterend, "nextSibling");
341 * Creates new DOM element(s) and inserts them as the first child of el.
342 * @param {Mixed} el The context element
343 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
344 * @param {Boolean} returnElement (optional) true to return a Ext.Element
345 * @return {HTMLElement/Ext.Element} The new node
347 insertFirst : function(el, o, returnElement){
348 return doInsert(el, o, returnElement, afterbegin, "firstChild");
352 * Creates new DOM element(s) and appends them to el.
353 * @param {Mixed} el The context element
354 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
355 * @param {Boolean} returnElement (optional) true to return a Ext.Element
356 * @return {HTMLElement/Ext.Element} The new node
358 append : function(el, o, returnElement){
359 return doInsert(el, o, returnElement, beforeend, "", true);
363 * Creates new DOM element(s) and overwrites the contents of el with them.
364 * @param {Mixed} el The context element
365 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
366 * @param {Boolean} returnElement (optional) true to return a Ext.Element
367 * @return {HTMLElement/Ext.Element} The new node
369 overwrite : function(el, o, returnElement){
371 el.innerHTML = createHtml(o);
372 return returnElement ? Ext.get(el.firstChild) : el.firstChild;
375 createHtml : createHtml
379 * @class Ext.DomHelper
\r
381 Ext.apply(Ext.DomHelper,
\r
384 afterbegin = 'afterbegin',
\r
385 afterend = 'afterend',
\r
386 beforebegin = 'beforebegin',
\r
387 beforeend = 'beforeend';
\r
390 function doInsert(el, o, returnElement, pos, sibling, append){
\r
391 el = Ext.getDom(el);
\r
394 newNode = createDom(o, null);
\r
396 el.appendChild(newNode);
\r
398 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
\r
401 newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
\r
403 return returnElement ? Ext.get(newNode, true) : newNode;
\r
408 function createDom(o, parentNode){
\r
416 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
\r
417 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
\r
418 Ext.each(o, function(v) {
\r
421 } else if (Ext.isString(o)) { // Allow a string as a child spec.
\r
422 el = doc.createTextNode(o);
\r
424 el = doc.createElement( o.tag || 'div' );
\r
425 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
\r
426 Ext.iterate(o, function(attr, val){
\r
427 if(!/tag|children|cn|html|style/.test(attr)){
\r
429 el.className = val;
\r
432 el.setAttribute(attr, val);
\r
439 pub.applyStyles(el, o.style);
\r
441 if ((cn = o.children || o.cn)) {
\r
443 } else if (o.html) {
\r
444 el.innerHTML = o.html;
\r
448 parentNode.appendChild(el);
\r
455 * Creates a new Ext.Template from the DOM object spec.
\r
456 * @param {Object} o The DOM object spec (and children)
\r
457 * @return {Ext.Template} The new template
\r
459 createTemplate : function(o){
\r
460 var html = Ext.DomHelper.createHtml(o);
\r
461 return new Ext.Template(html);
\r
464 /** True to force the use of DOM instead of html fragments @type Boolean */
\r
468 * Applies a style specification to an element.
\r
469 * @param {String/HTMLElement} el The element to apply styles to
\r
470 * @param {String/Object/Function} styles A style specification string e.g. 'width:100px', or object in the form {width:'100px'}, or
\r
471 * a function which returns such a specification.
\r
473 applyStyles : function(el, styles){
\r
480 if(Ext.isFunction(styles)){
\r
481 styles = styles.call();
\r
483 if(Ext.isString(styles)){
\r
484 styles = styles.trim().split(/\s*(?::|;)\s*/);
\r
485 for(len = styles.length; i < len;){
\r
486 el.setStyle(styles[i++], styles[i++]);
\r
488 }else if (Ext.isObject(styles)){
\r
489 el.setStyle(styles);
\r
495 * Creates new DOM element(s) and inserts them before el.
\r
496 * @param {Mixed} el The context element
\r
497 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
498 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
499 * @return {HTMLElement/Ext.Element} The new node
\r
502 insertBefore : function(el, o, returnElement){
\r
503 return doInsert(el, o, returnElement, beforebegin);
\r
507 * Creates new DOM element(s) and inserts them after el.
\r
508 * @param {Mixed} el The context element
\r
509 * @param {Object} o The DOM object spec (and children)
\r
510 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
511 * @return {HTMLElement/Ext.Element} The new node
\r
514 insertAfter : function(el, o, returnElement){
\r
515 return doInsert(el, o, returnElement, afterend, 'nextSibling');
\r
519 * Creates new DOM element(s) and inserts them as the first child of el.
\r
520 * @param {Mixed} el The context element
\r
521 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
522 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
523 * @return {HTMLElement/Ext.Element} The new node
\r
526 insertFirst : function(el, o, returnElement){
\r
527 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
\r
531 * Creates new DOM element(s) and appends them to el.
\r
532 * @param {Mixed} el The context element
\r
533 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
534 * @param {Boolean} returnElement (optional) true to return a Ext.Element
\r
535 * @return {HTMLElement/Ext.Element} The new node
\r
538 append: function(el, o, returnElement){
\r
539 return doInsert(el, o, returnElement, beforeend, '', true);
\r
543 * Creates new DOM element(s) without inserting them to the document.
\r
544 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
\r
545 * @return {HTMLElement} The new uninserted node
\r
547 createDom: createDom
\r
551 * @class Ext.Template
552 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
553 * For a list of available format functions, see {@link Ext.util.Format}.<br />
556 var t = new Ext.Template(
557 '<div name="{id}">',
558 '<span class="{cls}">{name:trim} {value:ellipsis(10)}</span>',
561 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
564 * @param {String/Array} html The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
566 Ext.Template = function(html){
571 if (Ext.isArray(html)) {
572 html = html.join("");
573 } else if (a.length > 1) {
574 Ext.each(a, function(v) {
575 if (Ext.isObject(v)) {
590 Ext.Template.prototype = {
592 * Returns an HTML fragment of this template with the specified values applied.
593 * @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'})
594 * @return {String} The HTML fragment
596 applyTemplate : function(values){
600 me.compiled(values) :
601 me.html.replace(me.re, function(m, name){
602 return values[name] !== undefined ? values[name] : "";
607 * Sets the HTML used as the template and optionally compiles it.
608 * @param {String} html
609 * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
610 * @return {Ext.Template} this
612 set : function(html, compile){
616 return compile ? me.compile() : me;
620 * The regular expression used to match template variables
624 re : /\{([\w-]+)\}/g,
627 * Compiles the template into an internal function, eliminating the RegEx overhead.
628 * @return {Ext.Template} this
630 compile : function(){
632 sep = Ext.isGecko ? "+" : ",";
634 function fn(m, name){
635 name = "values['" + name + "']";
636 return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
639 eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
640 me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
641 (Ext.isGecko ? "';};" : "'].join('');};"));
646 * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
647 * @param {Mixed} el The context element
648 * @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'})
649 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
650 * @return {HTMLElement/Ext.Element} The new node or Element
652 insertFirst: function(el, values, returnElement){
653 return this.doInsert('afterBegin', el, values, returnElement);
657 * Applies the supplied values to the template and inserts the new node(s) before el.
658 * @param {Mixed} el The context element
659 * @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'})
660 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
661 * @return {HTMLElement/Ext.Element} The new node or Element
663 insertBefore: function(el, values, returnElement){
664 return this.doInsert('beforeBegin', el, values, returnElement);
668 * Applies the supplied values to the template and inserts the new node(s) after el.
669 * @param {Mixed} el The context element
670 * @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'})
671 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
672 * @return {HTMLElement/Ext.Element} The new node or Element
674 insertAfter : function(el, values, returnElement){
675 return this.doInsert('afterEnd', el, values, returnElement);
679 * Applies the supplied values to the template and appends the new node(s) to el.
680 * @param {Mixed} el The context element
681 * @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'})
682 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
683 * @return {HTMLElement/Ext.Element} The new node or Element
685 append : function(el, values, returnElement){
686 return this.doInsert('beforeEnd', el, values, returnElement);
689 doInsert : function(where, el, values, returnEl){
691 var newNode = Ext.DomHelper.insertHtml(where, el, this.applyTemplate(values));
692 return returnEl ? Ext.get(newNode, true) : newNode;
696 * Applies the supplied values to the template and overwrites the content of el with the new node(s).
697 * @param {Mixed} el The context element
698 * @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'})
699 * @param {Boolean} returnElement (optional) true to return a Ext.Element (defaults to undefined)
700 * @return {HTMLElement/Ext.Element} The new node or Element
702 overwrite : function(el, values, returnElement){
704 el.innerHTML = this.applyTemplate(values);
705 return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
709 * Alias for {@link #applyTemplate}
710 * Returns an HTML fragment of this template with the specified values applied.
711 * @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'})
712 * @return {String} The HTML fragment
713 * @member Ext.Template
716 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
719 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
720 * @param {String/HTMLElement} el A DOM element or its id
721 * @param {Object} config A configuration object
722 * @return {Ext.Template} The created template
725 Ext.Template.from = function(el, config){
727 return new Ext.Template(el.value || el.innerHTML, config || '');
729 * @class Ext.Template
\r
731 Ext.apply(Ext.Template.prototype, {
\r
733 * Returns an HTML fragment of this template with the specified values applied.
\r
734 * @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
735 * @return {String} The HTML fragment
\r
738 applyTemplate : function(values){
\r
740 useF = me.disableFormats !== true,
\r
741 fm = Ext.util.Format,
\r
745 return me.compiled(values);
\r
747 function fn(m, name, format, args){
\r
748 if (format && useF) {
\r
749 if (format.substr(0, 5) == "this.") {
\r
750 return tpl.call(format.substr(5), values[name], values);
\r
753 // quoted values are required for strings in compiled templates,
\r
754 // but for non compiled we need to strip them
\r
755 // quoted reversed for jsmin
\r
756 var re = /^\s*['"](.*)["']\s*$/;
\r
757 args = args.split(',');
\r
758 for(var i = 0, len = args.length; i < len; i++){
\r
759 args[i] = args[i].replace(re, "$1");
\r
761 args = [values[name]].concat(args);
\r
763 args = [values[name]];
\r
765 return fm[format].apply(fm, args);
\r
768 return values[name] !== undefined ? values[name] : "";
\r
771 return me.html.replace(me.re, fn);
\r
775 * <tt>true</tt> to disable format functions (defaults to <tt>false</tt>)
\r
779 disableFormats : false,
\r
782 * The regular expression used to match template variables
\r
787 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
\r
790 * Compiles the template into an internal function, eliminating the RegEx overhead.
\r
791 * @return {Ext.Template} this
\r
794 compile : function(){
\r
796 fm = Ext.util.Format,
\r
797 useF = me.disableFormats !== true,
\r
798 sep = Ext.isGecko ? "+" : ",",
\r
801 function fn(m, name, format, args){
\r
802 if(format && useF){
\r
803 args = args ? ',' + args : "";
\r
804 if(format.substr(0, 5) != "this."){
\r
805 format = "fm." + format + '(';
\r
807 format = 'this.call("'+ format.substr(5) + '", ';
\r
811 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
\r
813 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
\r
816 // branched to use + in gecko and [].join() in others
\r
818 body = "this.compiled = function(values){ return '" +
\r
819 me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
\r
822 body = ["this.compiled = function(values){ return ['"];
\r
823 body.push(me.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
\r
824 body.push("'].join('');};");
\r
825 body = body.join('');
\r
831 // private function used to call members
\r
832 call : function(fnName, value, allValues){
\r
833 return this[fnName](value, allValues);
\r
836 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate; /*
\r
837 * This is code is also distributed under MIT license for use
\r
838 * with jQuery and prototype JavaScript libraries.
\r
841 * @class Ext.DomQuery
\r
842 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
844 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
847 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
849 <h4>Element Selectors:</h4>
\r
851 <li> <b>*</b> any element</li>
\r
852 <li> <b>E</b> an element with the tag E</li>
\r
853 <li> <b>E F</b> All descendent elements of E that have the tag F</li>
\r
854 <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
\r
855 <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
\r
856 <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
\r
858 <h4>Attribute Selectors:</h4>
\r
859 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
\r
861 <li> <b>E[foo]</b> has an attribute "foo"</li>
\r
862 <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
\r
863 <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
\r
864 <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
\r
865 <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
\r
866 <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
\r
867 <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
\r
869 <h4>Pseudo Classes:</h4>
\r
871 <li> <b>E:first-child</b> E is the first child of its parent</li>
\r
872 <li> <b>E:last-child</b> E is the last child of its parent</li>
\r
873 <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
874 <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
\r
875 <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
\r
876 <li> <b>E:only-child</b> E is the only child of its parent</li>
\r
877 <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
878 <li> <b>E:first</b> the first E in the resultset</li>
\r
879 <li> <b>E:last</b> the last E in the resultset</li>
\r
880 <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
\r
881 <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
\r
882 <li> <b>E:even</b> shortcut for :nth-child(even)</li>
\r
883 <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
\r
884 <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
\r
885 <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
\r
886 <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
\r
887 <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
\r
888 <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
\r
890 <h4>CSS Value Selectors:</h4>
\r
892 <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
\r
893 <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
\r
894 <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
\r
895 <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
\r
896 <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
\r
897 <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
\r
901 Ext.DomQuery = function(){
\r
906 trimRe = /^\s+|\s+$/g,
\r
907 tplRe = /\{(\d+)\}/g,
\r
908 modeRe = /^(\s?[\/>+~]\s?|\s|$)/,
\r
909 tagTokenRe = /^(#)?([\w-\*]+)/,
\r
910 nthRe = /(\d*)n\+?(\d*)/,
\r
912 // This is for IE MSXML which does not support expandos.
\r
913 // IE runs the same speed using setAttribute, however FF slows way down
\r
914 // and Safari completely fails so they need to continue to use expandos.
\r
915 isIE = window.ActiveXObject ? true : false,
\r
916 isOpera = Ext.isOpera,
\r
919 // this eval is stop the compressor from
\r
920 // renaming the variable to something shorter
\r
921 eval("var batch = 30803;");
\r
923 function child(p, index){
\r
927 if(n.nodeType == 1){
\r
938 while((n = n.nextSibling) && n.nodeType != 1);
\r
943 while((n = n.previousSibling) && n.nodeType != 1);
\r
947 function children(d){
\r
948 var n = d.firstChild, ni = -1,
\r
951 nx = n.nextSibling;
\r
952 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
\r
955 n.nodeIndex = ++ni;
\r
962 function byClassName(c, a, v){
\r
966 var r = [], ri = -1, cn;
\r
967 for(var i = 0, ci; ci = c[i]; i++){
\r
968 if((' '+ci.className+' ').indexOf(v) != -1){
\r
975 function attrValue(n, attr){
\r
976 if(!n.tagName && typeof n.length != "undefined"){
\r
985 if(attr == "class" || attr == "className"){
\r
986 return n.className;
\r
988 return n.getAttribute(attr) || n[attr];
\r
992 function getNodes(ns, mode, tagName){
\r
993 var result = [], ri = -1, cs;
\r
997 tagName = tagName || "*";
\r
998 if(typeof ns.getElementsByTagName != "undefined"){
\r
1002 for(var i = 0, ni; ni = ns[i]; i++){
\r
1003 cs = ni.getElementsByTagName(tagName);
\r
1004 for(var j = 0, ci; ci = cs[j]; j++){
\r
1005 result[++ri] = ci;
\r
1008 }else if(mode == "/" || mode == ">"){
\r
1009 var utag = tagName.toUpperCase();
\r
1010 for(var i = 0, ni, cn; ni = ns[i]; i++){
\r
1011 cn = isOpera ? ni.childNodes : (ni.children || ni.childNodes);
\r
1012 for(var j = 0, cj; cj = cn[j]; j++){
\r
1013 if(cj.nodeName == utag || cj.nodeName == tagName || tagName == '*'){
\r
1014 result[++ri] = cj;
\r
1018 }else if(mode == "+"){
\r
1019 var utag = tagName.toUpperCase();
\r
1020 for(var i = 0, n; n = ns[i]; i++){
\r
1021 while((n = n.nextSibling) && n.nodeType != 1);
\r
1022 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
\r
1026 }else if(mode == "~"){
\r
1027 var utag = tagName.toUpperCase();
\r
1028 for(var i = 0, n; n = ns[i]; i++){
\r
1029 while((n = n.nextSibling)){
\r
1030 if (n.nodeName == utag || n.nodeName == tagName || tagName == '*'){
\r
1039 function concat(a, b){
\r
1041 return a.concat(b);
\r
1043 for(var i = 0, l = b.length; i < l; i++){
\r
1044 a[a.length] = b[i];
\r
1049 function byTag(cs, tagName){
\r
1050 if(cs.tagName || cs == document){
\r
1056 var r = [], ri = -1;
\r
1057 tagName = tagName.toLowerCase();
\r
1058 for(var i = 0, ci; ci = cs[i]; i++){
\r
1059 if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
\r
1066 function byId(cs, attr, id){
\r
1067 if(cs.tagName || cs == document){
\r
1073 var r = [], ri = -1;
\r
1074 for(var i = 0,ci; ci = cs[i]; i++){
\r
1075 if(ci && ci.id == id){
\r
1083 function byAttribute(cs, attr, value, op, custom){
\r
1087 f = Ext.DomQuery.operators[op];
\r
1088 for(var i = 0, ci; ci = cs[i]; i++){
\r
1089 if(ci.nodeType != 1){
\r
1094 a = Ext.DomQuery.getStyle(ci, attr);
\r
1096 else if(attr == "class" || attr == "className"){
\r
1098 }else if(attr == "for"){
\r
1100 }else if(attr == "href"){
\r
1101 a = ci.getAttribute("href", 2);
\r
1103 a = ci.getAttribute(attr);
\r
1105 if((f && f(a, value)) || (!f && a)){
\r
1112 function byPseudo(cs, name, value){
\r
1113 return Ext.DomQuery.pseudos[name](cs, value);
\r
1116 function nodupIEXml(cs){
\r
1119 cs[0].setAttribute("_nodup", d);
\r
1121 for(var i = 1, len = cs.length; i < len; i++){
\r
1123 if(!c.getAttribute("_nodup") != d){
\r
1124 c.setAttribute("_nodup", d);
\r
1128 for(var i = 0, len = cs.length; i < len; i++){
\r
1129 cs[i].removeAttribute("_nodup");
\r
1134 function nodup(cs){
\r
1138 var len = cs.length, c, i, r = cs, cj, ri = -1;
\r
1139 if(!len || typeof cs.nodeType != "undefined" || len == 1){
\r
1142 if(isIE && typeof cs[0].selectSingleNode != "undefined"){
\r
1143 return nodupIEXml(cs);
\r
1147 for(i = 1; c = cs[i]; i++){
\r
1148 if(c._nodup != d){
\r
1152 for(var j = 0; j < i; j++){
\r
1155 for(j = i+1; cj = cs[j]; j++){
\r
1156 if(cj._nodup != d){
\r
1167 function quickDiffIEXml(c1, c2){
\r
1170 for(var i = 0, len = c1.length; i < len; i++){
\r
1171 c1[i].setAttribute("_qdiff", d);
\r
1173 for(var i = 0, len = c2.length; i < len; i++){
\r
1174 if(c2[i].getAttribute("_qdiff") != d){
\r
1175 r[r.length] = c2[i];
\r
1178 for(var i = 0, len = c1.length; i < len; i++){
\r
1179 c1[i].removeAttribute("_qdiff");
\r
1184 function quickDiff(c1, c2){
\r
1185 var len1 = c1.length,
\r
1191 if(isIE && c1[0].selectSingleNode){
\r
1192 return quickDiffIEXml(c1, c2);
\r
1194 for(var i = 0; i < len1; i++){
\r
1197 for(var i = 0, len = c2.length; i < len; i++){
\r
1198 if(c2[i]._qdiff != d){
\r
1199 r[r.length] = c2[i];
\r
1205 function quickId(ns, mode, root, id){
\r
1207 var d = root.ownerDocument || root;
\r
1208 return d.getElementById(id);
\r
1210 ns = getNodes(ns, mode, "*");
\r
1211 return byId(ns, null, id);
\r
1215 getStyle : function(el, name){
\r
1216 return Ext.fly(el).getStyle(name);
\r
1219 * Compiles a selector/xpath query into a reusable function. The returned function
\r
1220 * takes one parameter "root" (optional), which is the context node from where the query should start.
\r
1221 * @param {String} selector The selector/xpath query
\r
1222 * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
\r
1223 * @return {Function}
\r
1225 compile : function(path, type){
\r
1226 type = type || "select";
\r
1228 var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"],
\r
1229 q = path, mode, lq,
\r
1230 tk = Ext.DomQuery.matchers,
\r
1231 tklen = tk.length,
\r
1233 // accept leading mode switch
\r
1234 lmode = q.match(modeRe);
\r
1236 if(lmode && lmode[1]){
\r
1237 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
\r
1238 q = q.replace(lmode[1], "");
\r
1240 // strip leading slashes
\r
1241 while(path.substr(0, 1)=="/"){
\r
1242 path = path.substr(1);
\r
1245 while(q && lq != q){
\r
1247 var tm = q.match(tagTokenRe);
\r
1248 if(type == "select"){
\r
1251 fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
\r
1253 fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
\r
1255 q = q.replace(tm[0], "");
\r
1256 }else if(q.substr(0, 1) != '@'){
\r
1257 fn[fn.length] = 'n = getNodes(n, mode, "*");';
\r
1262 fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
\r
1264 fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
\r
1266 q = q.replace(tm[0], "");
\r
1269 while(!(mm = q.match(modeRe))){
\r
1270 var matched = false;
\r
1271 for(var j = 0; j < tklen; j++){
\r
1273 var m = q.match(t.re);
\r
1275 fn[fn.length] = t.select.replace(tplRe, function(x, i){
\r
1278 q = q.replace(m[0], "");
\r
1283 // prevent infinite loop on bad selector
\r
1285 throw 'Error parsing selector, parsing failed at "' + q + '"';
\r
1289 fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
\r
1290 q = q.replace(mm[1], "");
\r
1293 fn[fn.length] = "return nodup(n);\n}";
\r
1294 eval(fn.join(""));
\r
1299 * Selects a group of elements.
\r
1300 * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
\r
1301 * @param {Node} root (optional) The start of the query (defaults to document).
\r
1302 * @return {Array} An Array of DOM elements which match the selector. If there are
\r
1303 * no matches, and empty Array is returned.
\r
1305 select : function(path, root, type){
\r
1306 if(!root || root == document){
\r
1309 if(typeof root == "string"){
\r
1310 root = document.getElementById(root);
\r
1312 var paths = path.split(","),
\r
1314 for(var i = 0, len = paths.length; i < len; i++){
\r
1315 var p = paths[i].replace(trimRe, "");
\r
1317 cache[p] = Ext.DomQuery.compile(p);
\r
1319 throw p + " is not a valid selector";
\r
1322 var result = cache[p](root);
\r
1323 if(result && result != document){
\r
1324 results = results.concat(result);
\r
1327 if(paths.length > 1){
\r
1328 return nodup(results);
\r
1334 * Selects a single element.
\r
1335 * @param {String} selector The selector/xpath query
\r
1336 * @param {Node} root (optional) The start of the query (defaults to document).
\r
1337 * @return {Element} The DOM element which matched the selector.
\r
1339 selectNode : function(path, root){
\r
1340 return Ext.DomQuery.select(path, root)[0];
\r
1344 * Selects the value of a node, optionally replacing null with the defaultValue.
\r
1345 * @param {String} selector The selector/xpath query
\r
1346 * @param {Node} root (optional) The start of the query (defaults to document).
\r
1347 * @param {String} defaultValue
\r
1348 * @return {String}
\r
1350 selectValue : function(path, root, defaultValue){
\r
1351 path = path.replace(trimRe, "");
\r
1352 if(!valueCache[path]){
\r
1353 valueCache[path] = Ext.DomQuery.compile(path, "select");
\r
1355 var n = valueCache[path](root),
\r
1357 n = n[0] ? n[0] : n;
\r
1358 v = (n && n.firstChild ? n.firstChild.nodeValue : null);
\r
1359 return ((v === null||v === undefined||v==='') ? defaultValue : v);
\r
1363 * Selects the value of a node, parsing integers and floats. Returns the defaultValue, or 0 if none is specified.
\r
1364 * @param {String} selector The selector/xpath query
\r
1365 * @param {Node} root (optional) The start of the query (defaults to document).
\r
1366 * @param {Number} defaultValue
\r
1367 * @return {Number}
\r
1369 selectNumber : function(path, root, defaultValue){
\r
1370 var v = Ext.DomQuery.selectValue(path, root, defaultValue || 0);
\r
1371 return parseFloat(v);
\r
1375 * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
\r
1376 * @param {String/HTMLElement/Array} el An element id, element or array of elements
\r
1377 * @param {String} selector The simple selector to test
\r
1378 * @return {Boolean}
\r
1380 is : function(el, ss){
\r
1381 if(typeof el == "string"){
\r
1382 el = document.getElementById(el);
\r
1384 var isArray = Ext.isArray(el),
\r
1385 result = Ext.DomQuery.filter(isArray ? el : [el], ss);
\r
1386 return isArray ? (result.length == el.length) : (result.length > 0);
\r
1390 * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
\r
1391 * @param {Array} el An array of elements to filter
\r
1392 * @param {String} selector The simple selector to test
\r
1393 * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
\r
1394 * the selector instead of the ones that match
\r
1395 * @return {Array} An Array of DOM elements which match the selector. If there are
\r
1396 * no matches, and empty Array is returned.
\r
1398 filter : function(els, ss, nonMatches){
\r
1399 ss = ss.replace(trimRe, "");
\r
1400 if(!simpleCache[ss]){
\r
1401 simpleCache[ss] = Ext.DomQuery.compile(ss, "simple");
\r
1403 var result = simpleCache[ss](els);
\r
1404 return nonMatches ? quickDiff(result, els) : result;
\r
1408 * Collection of matching regular expressions and code snippets.
\r
1411 re: /^\.([\w-]+)/,
\r
1412 select: 'n = byClassName(n, null, " {1} ");'
\r
1414 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
\r
1415 select: 'n = byPseudo(n, "{1}", "{2}");'
\r
1417 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
\r
1418 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
\r
1421 select: 'n = byId(n, null, "{1}");'
\r
1424 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
\r
1429 * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
\r
1430 * 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
1433 "=" : function(a, v){
\r
1436 "!=" : function(a, v){
\r
1439 "^=" : function(a, v){
\r
1440 return a && a.substr(0, v.length) == v;
\r
1442 "$=" : function(a, v){
\r
1443 return a && a.substr(a.length-v.length) == v;
\r
1445 "*=" : function(a, v){
\r
1446 return a && a.indexOf(v) !== -1;
\r
1448 "%=" : function(a, v){
\r
1449 return (a % v) == 0;
\r
1451 "|=" : function(a, v){
\r
1452 return a && (a == v || a.substr(0, v.length+1) == v+'-');
\r
1454 "~=" : function(a, v){
\r
1455 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
\r
1460 * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
\r
1461 * and the argument (if any) supplied in the selector.
\r
1464 "first-child" : function(c){
\r
1465 var r = [], ri = -1, n;
\r
1466 for(var i = 0, ci; ci = n = c[i]; i++){
\r
1467 while((n = n.previousSibling) && n.nodeType != 1);
\r
1475 "last-child" : function(c){
\r
1476 var r = [], ri = -1, n;
\r
1477 for(var i = 0, ci; ci = n = c[i]; i++){
\r
1478 while((n = n.nextSibling) && n.nodeType != 1);
\r
1486 "nth-child" : function(c, a) {
\r
1487 var r = [], ri = -1,
\r
1488 m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a),
\r
1489 f = (m[1] || 1) - 0, l = m[2] - 0;
\r
1490 for(var i = 0, n; n = c[i]; i++){
\r
1491 var pn = n.parentNode;
\r
1492 if (batch != pn._batch) {
\r
1494 for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
\r
1495 if(cn.nodeType == 1){
\r
1496 cn.nodeIndex = ++j;
\r
1499 pn._batch = batch;
\r
1502 if (l == 0 || n.nodeIndex == l){
\r
1505 } else if ((n.nodeIndex + l) % f == 0){
\r
1513 "only-child" : function(c){
\r
1514 var r = [], ri = -1;;
\r
1515 for(var i = 0, ci; ci = c[i]; i++){
\r
1516 if(!prev(ci) && !next(ci)){
\r
1523 "empty" : function(c){
\r
1524 var r = [], ri = -1;
\r
1525 for(var i = 0, ci; ci = c[i]; i++){
\r
1526 var cns = ci.childNodes, j = 0, cn, empty = true;
\r
1527 while(cn = cns[j]){
\r
1529 if(cn.nodeType == 1 || cn.nodeType == 3){
\r
1541 "contains" : function(c, v){
\r
1542 var r = [], ri = -1;
\r
1543 for(var i = 0, ci; ci = c[i]; i++){
\r
1544 if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
\r
1551 "nodeValue" : function(c, v){
\r
1552 var r = [], ri = -1;
\r
1553 for(var i = 0, ci; ci = c[i]; i++){
\r
1554 if(ci.firstChild && ci.firstChild.nodeValue == v){
\r
1561 "checked" : function(c){
\r
1562 var r = [], ri = -1;
\r
1563 for(var i = 0, ci; ci = c[i]; i++){
\r
1564 if(ci.checked == true){
\r
1571 "not" : function(c, ss){
\r
1572 return Ext.DomQuery.filter(c, ss, true);
\r
1575 "any" : function(c, selectors){
\r
1576 var ss = selectors.split('|'),
\r
1577 r = [], ri = -1, s;
\r
1578 for(var i = 0, ci; ci = c[i]; i++){
\r
1579 for(var j = 0; s = ss[j]; j++){
\r
1580 if(Ext.DomQuery.is(ci, s)){
\r
1589 "odd" : function(c){
\r
1590 return this["nth-child"](c, "odd");
\r
1593 "even" : function(c){
\r
1594 return this["nth-child"](c, "even");
\r
1597 "nth" : function(c, a){
\r
1598 return c[a-1] || [];
\r
1601 "first" : function(c){
\r
1602 return c[0] || [];
\r
1605 "last" : function(c){
\r
1606 return c[c.length-1] || [];
\r
1609 "has" : function(c, ss){
\r
1610 var s = Ext.DomQuery.select,
\r
1612 for(var i = 0, ci; ci = c[i]; i++){
\r
1613 if(s(ss, ci).length > 0){
\r
1620 "next" : function(c, ss){
\r
1621 var is = Ext.DomQuery.is,
\r
1623 for(var i = 0, ci; ci = c[i]; i++){
\r
1625 if(n && is(n, ss)){
\r
1632 "prev" : function(c, ss){
\r
1633 var is = Ext.DomQuery.is,
\r
1635 for(var i = 0, ci; ci = c[i]; i++){
\r
1637 if(n && is(n, ss)){
\r
1648 * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Ext.DomQuery#select}
\r
1649 * @param {String} path The selector/xpath query
\r
1650 * @param {Node} root (optional) The start of the query (defaults to document).
\r
1655 Ext.query = Ext.DomQuery.select;
\r
1658 var EXTUTIL = Ext.util,
1659 TOARRAY = Ext.toArray,
1661 ISOBJECT = Ext.isObject,
1665 * @class Ext.util.Observable
1666 * Base class that provides a common interface for publishing events. Subclasses are expected to
1667 * to have a property "events" with all the events defined, and, optionally, a property "listeners"
1668 * with configured listeners defined.<br>
1671 Employee = Ext.extend(Ext.util.Observable, {
1672 constructor: function(config){
1673 this.name = config.name;
1679 // Copy configured listeners into *this* object so that the base class's
1680 // constructor will add them.
1681 this.listeners = config.listeners;
1683 // Call our superclass constructor to complete construction process.
1684 Employee.superclass.constructor.call(config)
1688 * This could then be used like this:<pre><code>
1689 var newEmployee = new Employee({
1693 // By default, "this" will be the object that fired the event.
1694 alert(this.name + " has quit!");
1700 EXTUTIL.Observable = function(){
1702 * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
1703 * object during initialization. This should be a valid listeners config object as specified in the
1704 * {@link #addListener} example for attaching multiple handlers at once.</p>
1705 * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
1706 * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
1707 * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
1708 * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
1709 * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
1710 * has been rendered. A plugin can simplify this step:<pre><code>
1711 // Plugin is configured with a listeners config object.
1712 // The Component is appended to the argument list of all handler functions.
1713 Ext.DomObserver = Ext.extend(Object, {
1714 constructor: function(config) {
1715 this.listeners = config.listeners ? config.listeners : config;
1718 // Component passes itself into plugin's init method
1720 var p, l = this.listeners;
1722 if (Ext.isFunction(l[p])) {
1723 l[p] = this.createHandler(l[p], c);
1725 l[p].fn = this.createHandler(l[p].fn, c);
1729 // Add the listeners to the Element immediately following the render call
1730 c.render = c.render.{@link Function#createSequence createSequence}(function() {
1738 createHandler: function(fn, c) {
1739 return function(e) {
1740 fn.call(this, e, c);
1745 var combo = new Ext.form.ComboBox({
1747 // Collapse combo when its element is clicked on
1748 plugins: [ new Ext.DomObserver({
1749 click: function(evt, comp) {
1756 triggerAction: 'all'
1760 var me = this, e = me.events;
1762 me.on(me.listeners);
1763 delete me.listeners;
1765 me.events = e || {};
1768 EXTUTIL.Observable.prototype = function(){
1769 var filterOptRe = /^(?:scope|delay|buffer|single)$/, toLower = function(s){
1770 return s.toLowerCase();
1775 * <p>Fires the specified event with the passed parameters (minus the event name).</p>
1776 * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
1777 * by calling {@link #enableBubble}.</p>
1778 * @param {String} eventName The name of the event to fire.
1779 * @param {Object...} args Variable number of parameters are passed to handlers.
1780 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
1783 fireEvent : function(){
1784 var a = TOARRAY(arguments),
1785 ename = toLower(a[0]),
1788 ce = me.events[ename],
1791 if (me.eventsSuspended === TRUE) {
1792 if (q = me.suspendedEventsQueue) {
1796 else if(ISOBJECT(ce) && ce.bubble){
1797 if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
1800 c = me.getBubbleTarget && me.getBubbleTarget();
1801 if(c && c.enableBubble) {
1802 c.enableBubble(ename);
1803 return c.fireEvent.apply(c, a);
1809 ret = ce.fire.apply(ce, a);
1816 * Appends an event handler to this object.
1817 * @param {String} eventName The name of the event to listen for.
1818 * @param {Function} handler The method the event invokes.
1819 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1820 * <b>If omitted, defaults to the object which fired the event.</b>
1821 * @param {Object} options (optional) An object containing handler configuration.
1822 * properties. This may contain any of the following properties:<ul>
1823 * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1824 * <b>If omitted, defaults to the object which fired the event.</b></div></li>
1825 * <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>
1826 * <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>
1827 * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
1828 * by the specified number of milliseconds. If the event fires again within that time, the original
1829 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
1830 * <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>
1831 * if the event was bubbled up from a child Observable.</div></li>
1834 * <b>Combining Options</b><br>
1835 * Using the options argument, it is possible to combine different types of listeners:<br>
1837 * A delayed, one-time listener.
1839 myDataView.on('click', this.onClick, this, {
1844 * <b>Attaching multiple handlers in 1 call</b><br>
1845 * The method also allows for a single argument to be passed which is a config object containing properties
1846 * which specify multiple handlers.
1856 fn: this.onMouseOver,
1860 fn: this.onMouseOut,
1865 * Or a shorthand syntax:<br>
1868 'click' : this.onClick,
1869 'mouseover' : this.onMouseOver,
1870 'mouseout' : this.onMouseOut,
1874 addListener : function(eventName, fn, scope, o){
1880 if (ISOBJECT(eventName)) {
1884 if (!filterOptRe.test(e)) {
1885 me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
1889 eventName = toLower(eventName);
1890 ce = me.events[eventName] || TRUE;
1891 if (typeof ce == "boolean") {
1892 me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
1894 ce.addListener(fn, scope, ISOBJECT(o) ? o : {});
1899 * Removes an event handler.
1900 * @param {String} eventName The type of event the handler was associated with.
1901 * @param {Function} handler The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
1902 * @param {Object} scope (optional) The scope originally specified for the handler.
1904 removeListener : function(eventName, fn, scope){
1905 var ce = this.events[toLower(eventName)];
1907 ce.removeListener(fn, scope);
1912 * Removes all listeners for this object
1914 purgeListeners : function(){
1915 var events = this.events,
1921 evt.clearListeners();
1927 * Used to define events on this Observable
1928 * @param {Object} object The object with the events defined
1930 addEvents : function(o){
1932 me.events = me.events || {};
1933 if (typeof o == 'string') {
1934 EACH(arguments, function(a) {
1935 me.events[a] = me.events[a] || TRUE;
1938 Ext.applyIf(me.events, o);
1943 * Checks to see if this object has any listeners for a specified event
1944 * @param {String} eventName The name of the event to check for
1945 * @return {Boolean} True if the event is being listened for, else false
1947 hasListener : function(eventName){
1948 var e = this.events[eventName];
1949 return ISOBJECT(e) && e.listeners.length > 0;
1953 * Suspend the firing of all events. (see {@link #resumeEvents})
1954 * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
1955 * after the {@link #resumeEvents} call instead of discarding all suspended events;
1957 suspendEvents : function(queueSuspended){
1958 this.eventsSuspended = TRUE;
1959 if (queueSuspended){
1960 this.suspendedEventsQueue = [];
1965 * Resume firing events. (see {@link #suspendEvents})
1966 * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
1967 * events fired during event suspension will be sent to any listeners now.
1969 resumeEvents : function(){
1971 me.eventsSuspended = !delete me.suspendedEventQueue;
1972 EACH(me.suspendedEventsQueue, function(e) {
1973 me.fireEvent.apply(me, e);
1979 var OBSERVABLE = EXTUTIL.Observable.prototype;
1981 * Appends an event handler to this object (shorthand for {@link #addListener}.)
1982 * @param {String} eventName The type of event to listen for
1983 * @param {Function} handler The method the event invokes
1984 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
1985 * <b>If omitted, defaults to the object which fired the event.</b>
1986 * @param {Object} options (optional) An object containing handler configuration.
1989 OBSERVABLE.on = OBSERVABLE.addListener;
1991 * Removes an event handler (shorthand for {@link #removeListener}.)
1992 * @param {String} eventName The type of event the handler was associated with.
1993 * @param {Function} handler The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
1994 * @param {Object} scope (optional) The scope originally specified for the handler.
1997 OBSERVABLE.un = OBSERVABLE.removeListener;
2000 * Removes <b>all</b> added captures from the Observable.
2001 * @param {Observable} o The Observable to release
2004 EXTUTIL.Observable.releaseCapture = function(o){
2005 o.fireEvent = OBSERVABLE.fireEvent;
2008 function createTargeted(h, o, scope){
2010 if(o.target == arguments[0]){
2011 h.apply(scope, TOARRAY(arguments));
2016 function createBuffered(h, o, scope){
2017 var task = new EXTUTIL.DelayedTask();
2019 task.delay(o.buffer, h, scope, TOARRAY(arguments));
2023 function createSingle(h, e, fn, scope){
2025 e.removeListener(fn, scope);
2026 return h.apply(scope, arguments);
2030 function createDelayed(h, o, scope){
2032 var args = TOARRAY(arguments);
2034 h.apply(scope, args);
2035 }).defer(o.delay || 10);
2039 EXTUTIL.Event = function(obj, name){
2042 this.listeners = [];
2045 EXTUTIL.Event.prototype = {
2046 addListener : function(fn, scope, options){
2049 scope = scope || me.obj;
2050 if(!me.isListening(fn, scope)){
2051 l = me.createListener(fn, scope, options);
2052 if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
2053 me.listeners = me.listeners.slice(0);
2055 me.listeners.push(l);
2059 createListener: function(fn, scope, o){
2060 o = o || {}, scope = scope || this.obj;
2067 h = createTargeted(h, o, scope);
2070 h = createDelayed(h, o, scope);
2073 h = createSingle(h, this, fn, scope);
2076 h = createBuffered(h, o, scope);
2082 findListener : function(fn, scope){
2084 EACH(this.listeners, function(l, i) {
2086 if(l.fn == fn && (s == scope || s == this.obj)){
2095 isListening : function(fn, scope){
2096 return this.findListener(fn, scope) != -1;
2099 removeListener : function(fn, scope){
2103 if((index = me.findListener(fn, scope)) != -1){
2105 me.listeners = me.listeners.slice(0);
2107 me.listeners.splice(index, 1);
2113 clearListeners : function(){
2114 this.listeners = [];
2119 args = TOARRAY(arguments),
2122 EACH(me.listeners, function(l) {
2124 if (l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
2125 return ret = me.firing = FALSE;
2133 * @class Ext.util.Observable
\r
2135 Ext.apply(Ext.util.Observable.prototype, function(){
\r
2136 // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
\r
2137 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
\r
2139 function getMethodEvent(method){
\r
2140 var e = (this.methodEvents = this.methodEvents ||
\r
2141 {})[method], returnValue, v, cancel, obj = this;
\r
2144 this.methodEvents[method] = e = {};
\r
2145 e.originalFn = this[method];
\r
2146 e.methodName = method;
\r
2150 var makeCall = function(fn, scope, args){
\r
2151 if (!Ext.isEmpty(v = fn.apply(scope || obj, args))) {
\r
2152 if (Ext.isObject(v)) {
\r
2153 returnValue = !Ext.isEmpty(v.returnValue) ? v.returnValue : v;
\r
2154 cancel = !!v.cancel;
\r
2157 if (v === false) {
\r
2166 this[method] = function(){
\r
2167 var args = Ext.toArray(arguments);
\r
2168 returnValue = v = undefined;
\r
2171 Ext.each(e.before, function(b){
\r
2172 makeCall(b.fn, b.scope, args);
\r
2174 return returnValue;
\r
2178 if (!Ext.isEmpty(v = e.originalFn.apply(obj, args))) {
\r
2181 Ext.each(e.after, function(a){
\r
2182 makeCall(a.fn, a.scope, args);
\r
2184 return returnValue;
\r
2187 return returnValue;
\r
2194 // these are considered experimental
\r
2195 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
\r
2196 // adds an "interceptor" called before the original method
\r
2197 beforeMethod: function(method, fn, scope){
\r
2198 getMethodEvent.call(this, method).before.push({
\r
2204 // adds a "sequence" called after the original method
\r
2205 afterMethod: function(method, fn, scope){
\r
2206 getMethodEvent.call(this, method).after.push({
\r
2212 removeMethodListener: function(method, fn, scope){
\r
2213 var e = getMethodEvent.call(this, method), found = false;
\r
2214 Ext.each(e.before, function(b, i, arr){
\r
2215 if (b.fn == fn && b.scope == scope) {
\r
2222 Ext.each(e.after, function(a, i, arr){
\r
2223 if (a.fn == fn && a.scope == scope) {
\r
2232 * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
\r
2233 * @param {Object} o The Observable whose events this object is to relay.
\r
2234 * @param {Array} events Array of event names to relay.
\r
2236 relayEvents: function(o, events){
\r
2238 function createHandler(ename){
\r
2239 return function(){
\r
2240 return me.fireEvent.apply(me, [ename].concat(Ext.toArray(arguments)));
\r
2243 Ext.each(events, function(ename){
\r
2244 me.events[ename] = me.events[ename] || true;
\r
2245 o.on(ename, createHandler(ename), me);
\r
2250 * Used to enable bubbling of events
\r
2251 * @param {Object} events
\r
2253 enableBubble: function(events){
\r
2255 events = Ext.isArray(events) ? events : Ext.toArray(arguments);
\r
2256 Ext.each(events, function(ename){
\r
2257 ename = ename.toLowerCase();
\r
2258 var ce = me.events[ename] || true;
\r
2259 if (typeof ce == "boolean") {
\r
2260 ce = new Ext.util.Event(me, ename);
\r
2261 me.events[ename] = ce;
\r
2271 * Starts capture on the specified Observable. All events will be passed
\r
2272 * to the supplied function with the event name + standard signature of the event
\r
2273 * <b>before</b> the event is fired. If the supplied function returns false,
\r
2274 * the event will not fire.
\r
2275 * @param {Observable} o The Observable to capture
\r
2276 * @param {Function} fn The function to call
\r
2277 * @param {Object} scope (optional) The scope (this object) for the fn
\r
2280 Ext.util.Observable.capture = function(o, fn, scope){
\r
2281 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
\r
2286 * Sets observability on the passed class constructor.<p>
\r
2287 * <p>This makes any event fired on any instance of the passed class also fire a single event through
\r
2288 * the <i>class</i> allowing for central handling of events on many instances at once.</p>
\r
2289 * <p>Usage:</p><pre><code>
\r
2290 Ext.util.Observable.observeClass(Ext.data.Connection);
\r
2291 Ext.data.Connection.on('beforerequest', function(con, options) {
\r
2292 console.log("Ajax request made to " + options.url);
\r
2294 * @param {Function} c The class constructor to make observable.
\r
2297 Ext.util.Observable.observeClass = function(c){
\r
2298 Ext.apply(c, new Ext.util.Observable());
\r
2299 c.prototype.fireEvent = function(){
\r
2300 return (c.fireEvent.apply(c, arguments) !== false) &&
\r
2301 (Ext.util.Observable.prototype.fireEvent.apply(this, arguments) !== false);
\r
2304 * @class Ext.EventManager
2305 * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
2306 * several useful events directly.
2307 * See {@link Ext.EventObject} for more details on normalized event objects.
2310 Ext.EventManager = function(){
2313 docReadyState = false,
2318 IEDEFERED = "ie-deferred-loader",
2319 DOMCONTENTLOADED = "DOMContentLoaded",
2321 propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
2323 /// There is some jquery work around stuff here that isn't needed in Ext Core.
2324 function addListener(el, ename, fn, wrap, scope){
2325 var id = Ext.id(el),
2326 es = elHash[id] = elHash[id] || {};
2328 (es[ename] = es[ename] || []).push([fn, wrap, scope]);
2329 E.on(el, ename, wrap);
2331 // this is a workaround for jQuery and should somehow be removed from Ext Core in the future
2332 // without breaking ExtJS.
2333 if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
2334 var args = ["DOMMouseScroll", wrap, false];
2335 el.addEventListener.apply(el, args);
2336 E.on(window, 'unload', function(){
2337 el.removeEventListener.apply(el, args);
2340 if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
2341 Ext.EventManager.stoppedMouseDownEvent.addListener(wrap);
2345 function fireDocReady(){
2347 Ext.isReady = docReadyState = true;
2349 clearInterval(docReadyProcId);
2351 if(Ext.isGecko || Ext.isOpera) {
2352 DOC.removeEventListener(DOMCONTENTLOADED, fireDocReady, false);
2355 var defer = DOC.getElementById(IEDEFERED);
2357 defer.onreadystatechange = null;
2358 defer.parentNode.removeChild(defer);
2362 docReadyEvent.fire();
2363 docReadyEvent.clearListeners();
2368 function initDocReady(){
2369 var COMPLETE = "complete";
2371 docReadyEvent = new Ext.util.Event();
2372 if (Ext.isGecko || Ext.isOpera) {
2373 DOC.addEventListener(DOMCONTENTLOADED, fireDocReady, false);
2374 } else if (Ext.isIE){
2375 DOC.write("<s"+'cript id=' + IEDEFERED + ' defer="defer" src="/'+'/:"></s'+"cript>");
2376 DOC.getElementById(IEDEFERED).onreadystatechange = function(){
2377 if(this.readyState == COMPLETE){
2381 } else if (Ext.isWebKit){
2382 docReadyProcId = setInterval(function(){
2383 if(DOC.readyState == COMPLETE) {
2388 // no matter what, make sure it fires on load
2389 E.on(WINDOW, "load", fireDocReady);
2392 function createTargeted(h, o){
2394 var args = Ext.toArray(arguments);
2395 if(o.target == Ext.EventObject.setEvent(args[0]).target){
2396 h.apply(this, args);
2401 function createBuffered(h, o){
2402 var task = new Ext.util.DelayedTask(h);
2404 // create new event object impl so new events don't wipe out properties
2405 task.delay(o.buffer, h, null, [new Ext.EventObjectImpl(e)]);
2409 function createSingle(h, el, ename, fn, scope){
2411 Ext.EventManager.removeListener(el, ename, fn, scope);
2416 function createDelayed(h, o){
2418 // create new event object impl so new events don't wipe out properties
2419 e = new Ext.EventObjectImpl(e);
2420 setTimeout(function(){
2426 function listen(element, ename, opt, fn, scope){
2427 var o = !Ext.isObject(opt) ? {} : opt,
2428 el = Ext.getDom(element);
2431 scope = scope || o.scope;
2434 throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
2437 // prevent errors while unload occurring
2438 if(!Ext){// !window[xname]){ ==> can't we do this?
2441 e = Ext.EventObject.setEvent(e);
2444 if(!(t = e.getTarget(o.delegate, el))){
2453 if (o.preventDefault) {
2456 if (o.stopPropagation) {
2457 e.stopPropagation();
2463 fn.call(scope || el, e, t, o);
2466 h = createTargeted(h, o);
2469 h = createDelayed(h, o);
2472 h = createSingle(h, el, ename, fn, scope);
2475 h = createBuffered(h, o);
2478 addListener(el, ename, fn, h, scope);
2484 * Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
2485 * use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
2486 * @param {String/HTMLElement} el The html element or id to assign the event handler to
2487 * @param {String} eventName The type of event to listen for
2488 * @param {Function} handler The handler function the event invokes This function is passed
2489 * the following parameters:<ul>
2490 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
2491 * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
2492 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
2493 * <li>o : Object<div class="sub-desc">The options object from the addListener call.</div></li>
2495 * @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>.
2496 * @param {Object} options (optional) An object containing handler configuration properties.
2497 * This may contain any of the following properties:<ul>
2498 * <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>
2499 * <li>delegate : String<div class="sub-desc">A simple selector to filter the target or look for a descendant of the target</div></li>
2500 * <li>stopEvent : Boolean<div class="sub-desc">True to stop the event. That is stop propagation, and prevent the default action.</div></li>
2501 * <li>preventDefault : Boolean<div class="sub-desc">True to prevent the default action</div></li>
2502 * <li>stopPropagation : Boolean<div class="sub-desc">True to prevent event propagation</div></li>
2503 * <li>normalized : Boolean<div class="sub-desc">False to pass a browser event to the handler function instead of an Ext.EventObject</div></li>
2504 * <li>delay : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after te event fires.</div></li>
2505 * <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>
2506 * <li>buffer : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
2507 * by the specified number of milliseconds. If the event fires again within that time, the original
2508 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
2509 * <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>
2511 * <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
2513 addListener : function(element, eventName, fn, scope, options){
2514 if(Ext.isObject(eventName)){
2515 var o = eventName, e, val;
2518 if(!propRe.test(e)){
2519 if(Ext.isFunction(val)){
2521 listen(element, e, o, val, o.scope);
2523 // individual options
2524 listen(element, e, val);
2529 listen(element, eventName, options, fn, scope);
2534 * Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
2535 * you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
2536 * @param {String/HTMLElement} el The id or html element from which to remove the event
2537 * @param {String} eventName The type of event
2538 * @param {Function} fn The handler function to remove
2540 removeListener : function(element, eventName, fn, scope){
2541 var el = Ext.getDom(element),
2545 Ext.each((elHash[id] || {})[eventName], function (v,i,a) {
2546 if (Ext.isArray(v) && v[0] == fn && (!scope || v[2] == scope)) {
2547 E.un(el, eventName, wrap = v[1]);
2553 // jQuery workaround that should be removed from Ext Core
2554 if(eventName == "mousewheel" && el.addEventListener && wrap){
2555 el.removeEventListener("DOMMouseScroll", wrap, false);
2558 if(eventName == "mousedown" && el == DOC && wrap){ // fix stopped mousedowns on the document
2559 Ext.EventManager.stoppedMouseDownEvent.removeListener(wrap);
2564 * Removes all event handers from an element. Typically you will use {@link Ext.Element#removeAllListeners}
2565 * directly on an Element in favor of calling this version.
2566 * @param {String/HTMLElement} el The id or html element from which to remove the event
2568 removeAll : function(el){
2569 var id = Ext.id(el = Ext.getDom(el)),
2574 if(es.hasOwnProperty(ename)){
2575 Ext.each(es[ename], function(v) {
2576 E.un(el, ename, v.wrap);
2584 * Fires when the document is ready (before onload and before images are loaded). Can be
2585 * accessed shorthanded as Ext.onReady().
2586 * @param {Function} fn The method the event invokes
2587 * @param {Object} scope (optional) An object that becomes the scope of the handler
2588 * @param {boolean} options (optional) An object containing standard {@link #addListener} options
2590 onDocumentReady : function(fn, scope, options){
2591 if(docReadyState){ // if it already fired
2592 docReadyEvent.addListener(fn, scope, options);
2593 docReadyEvent.fire();
2594 docReadyEvent.clearListeners();
2596 if(!docReadyEvent) initDocReady();
2597 options = options || {};
2598 options.delay = options.delay || 1;
2599 docReadyEvent.addListener(fn, scope, options);
2606 * Appends an event handler to an element. Shorthand for {@link #addListener}.
2607 * @param {String/HTMLElement} el The html element or id to assign the event handler to
2608 * @param {String} eventName The type of event to listen for
2609 * @param {Function} handler The handler function the event invokes
2610 * @param {Object} scope (optional) The scope in which to execute the handler
2611 * function (the handler function's "this" context)
2612 * @param {Object} options (optional) An object containing standard {@link #addListener} options
2613 * @member Ext.EventManager
2616 pub.on = pub.addListener;
2618 * Removes an event handler from an element. Shorthand for {@link #removeListener}.
2619 * @param {String/HTMLElement} el The id or html element from which to remove the event
2620 * @param {String} eventName The type of event
2621 * @param {Function} fn The handler function to remove
2622 * @return {Boolean} True if a listener was actually removed, else false
2623 * @member Ext.EventManager
2626 pub.un = pub.removeListener;
2628 pub.stoppedMouseDownEvent = new Ext.util.Event();
2632 * Fires when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
2633 * @param {Function} fn The method the event invokes
2634 * @param {Object} scope An object that becomes the scope of the handler
2635 * @param {boolean} options (optional) An object containing standard {@link #addListener} options
2639 Ext.onReady = Ext.EventManager.onDocumentReady;
2642 //Initialize doc classes
2645 var initExtCss = function(){
2646 // find the body element
2647 var bd = document.body || document.getElementsByTagName('body')[0];
2648 if(!bd){ return false; }
2650 Ext.isIE ? "ext-ie " + (Ext.isIE6 ? 'ext-ie6' : (Ext.isIE7 ? 'ext-ie7' : 'ext-ie8'))
2651 : Ext.isGecko ? "ext-gecko " + (Ext.isGecko2 ? 'ext-gecko2' : 'ext-gecko3')
2652 : Ext.isOpera ? "ext-opera"
2653 : Ext.isWebKit ? "ext-webkit" : ""];
2656 cls.push("ext-safari " + (Ext.isSafari2 ? 'ext-safari2' : (Ext.isSafari3 ? 'ext-safari3' : 'ext-safari4')));
2657 }else if(Ext.isChrome){
2658 cls.push("ext-chrome");
2662 cls.push("ext-mac");
2665 cls.push("ext-linux");
2668 if(Ext.isStrict || Ext.isBorderBox){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
2669 var p = bd.parentNode;
2671 p.className += Ext.isStrict ? ' ext-strict' : ' ext-border-box';
2674 bd.className += cls.join(' ');
2679 Ext.onReady(initExtCss);
2685 * @class Ext.EventObject
2686 * Just as {@link Ext.Element} wraps around a native DOM node, Ext.EventObject
2687 * wraps the browser's native event-object normalizing cross-browser differences,
2688 * such as which mouse button is clicked, keys pressed, mechanisms to stop
2689 * event-propagation along with a method to prevent default actions from taking place.
2690 * <p>For example:</p>
2692 function handleClick(e, t){ // e is not a standard event object, it is a Ext.EventObject
2694 var target = e.getTarget(); // same as t (the target HTMLElement)
2697 var myDiv = {@link Ext#get Ext.get}("myDiv"); // get reference to an {@link Ext.Element}
2698 myDiv.on( // 'on' is shorthand for addListener
2699 "click", // perform an action on click of myDiv
2700 handleClick // reference to the action handler
2702 // other methods to do the same:
2703 Ext.EventManager.on("myDiv", 'click', handleClick);
2704 Ext.EventManager.addListener("myDiv", 'click', handleClick);
2708 Ext.EventObject = function(){
2709 var E = Ext.lib.Event,
2710 // safari keypress events for special keys return bad keycodes
2714 63235 : 39, // right
2717 63276 : 33, // page up
2718 63277 : 34, // page down
2719 63272 : 46, // delete
2723 // normalize button clicks
2724 btnMap = Ext.isIE ? {1:0,4:1,2:2} :
2725 (Ext.isWebKit ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
2727 Ext.EventObjectImpl = function(e){
2729 this.setEvent(e.browserEvent || e);
2733 Ext.EventObjectImpl.prototype = {
2735 setEvent : function(e){
2737 if(e == me || (e && e.browserEvent)){ // already wrapped
2740 me.browserEvent = e;
2742 // normalize buttons
2743 me.button = e.button ? btnMap[e.button] : (e.which ? e.which - 1 : -1);
2744 if(e.type == 'click' && me.button == -1){
2748 me.shiftKey = e.shiftKey;
2749 // mac metaKey behaves like ctrlKey
2750 me.ctrlKey = e.ctrlKey || e.metaKey || false;
2751 me.altKey = e.altKey;
2752 // in getKey these will be normalized for the mac
2753 me.keyCode = e.keyCode;
2754 me.charCode = e.charCode;
2755 // cache the target for the delayed and or buffered events
2756 me.target = E.getTarget(e);
2761 me.shiftKey = false;
2773 * Stop the event (preventDefault and stopPropagation)
2775 stopEvent : function(){
2777 if(me.browserEvent){
2778 if(me.browserEvent.type == 'mousedown'){
2779 Ext.EventManager.stoppedMouseDownEvent.fire(me);
2781 E.stopEvent(me.browserEvent);
2786 * Prevents the browsers default handling of the event.
2788 preventDefault : function(){
2789 if(this.browserEvent){
2790 E.preventDefault(this.browserEvent);
2795 * Cancels bubbling of the event.
2797 stopPropagation : function(){
2799 if(me.browserEvent){
2800 if(me.browserEvent.type == 'mousedown'){
2801 Ext.EventManager.stoppedMouseDownEvent.fire(me);
2803 E.stopPropagation(me.browserEvent);
2808 * Gets the character code for the event.
2811 getCharCode : function(){
2812 return this.charCode || this.keyCode;
2816 * Returns a normalized keyCode for the event.
2817 * @return {Number} The key code
2819 getKey : function(){
2820 return this.normalizeKey(this.keyCode || this.charCode)
2824 normalizeKey: function(k){
2825 return Ext.isSafari ? (safariKeys[k] || k) : k;
2829 * Gets the x coordinate of the event.
2832 getPageX : function(){
2837 * Gets the y coordinate of the event.
2840 getPageY : function(){
2845 * Gets the page coordinates of the event.
2846 * @return {Array} The xy values like [x, y]
2853 * Gets the target for the event.
2854 * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
2855 * @param {Number/Mixed} maxDepth (optional) The max depth to
2856 search as a number or element (defaults to 10 || document.body)
2857 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
2858 * @return {HTMLelement}
2860 getTarget : function(selector, maxDepth, returnEl){
2861 return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
2865 * Gets the related target.
2866 * @return {HTMLElement}
2868 getRelatedTarget : function(){
2869 return this.browserEvent ? E.getRelatedTarget(this.browserEvent) : null;
2873 * Normalizes mouse wheel delta across browsers
2874 * @return {Number} The delta
2876 getWheelDelta : function(){
2877 var e = this.browserEvent;
2879 if(e.wheelDelta){ /* IE/Opera. */
2880 delta = e.wheelDelta/120;
2881 }else if(e.detail){ /* Mozilla case. */
2882 delta = -e.detail/3;
2888 * 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.
2889 * Example usage:<pre><code>
2890 // Handle click on any child of an element
2891 Ext.getBody().on('click', function(e){
2892 if(e.within('some-el')){
2893 alert('Clicked on a child of some-el!');
2897 // Handle click directly on an element, ignoring clicks on child nodes
2898 Ext.getBody().on('click', function(e,t){
2899 if((t.id == 'some-el') && !e.within(t, true)){
2900 alert('Clicked directly on some-el!');
2904 * @param {Mixed} el The id, DOM element or Ext.Element to check
2905 * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
2906 * @param {Boolean} allowEl {optional} true to also check if the passed element is the target or related target
2909 within : function(el, related, allowEl){
2911 var t = this[related ? "getRelatedTarget" : "getTarget"]();
2912 return t && ((allowEl ? (t == Ext.getDom(el)) : false) || Ext.fly(el).contains(t));
2918 return new Ext.EventObjectImpl();
2920 * @class Ext.EventManager
\r
2922 Ext.apply(Ext.EventManager, function(){
\r
2928 E = Ext.lib.Event,
\r
2929 propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
\r
2932 // note 1: IE fires ONLY the keydown event on specialkey autorepeat
\r
2933 // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
\r
2934 // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
\r
2935 useKeydown = Ext.isSafari ?
\r
2936 Ext.num(navigator.userAgent.toLowerCase().match(/version\/(\d+\.\d)/)[1] || 2) >= 3.1 :
\r
2937 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
\r
2941 doResizeEvent: function(){
\r
2942 var h = D.getViewHeight(),
\r
2943 w = D.getViewWidth();
\r
2945 //whacky problem in IE where the resize event will fire even though the w/h are the same.
\r
2946 if(curHeight != h || curWidth != w){
\r
2947 resizeEvent.fire(curWidth = w, curHeight = h);
\r
2952 * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
\r
2953 * @param {Function} fn The method the event invokes
\r
2954 * @param {Object} scope An object that becomes the scope of the handler
\r
2955 * @param {boolean} options
\r
2957 onWindowResize : function(fn, scope, options){
\r
2959 resizeEvent = new Ext.util.Event();
\r
2960 resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
\r
2961 E.on(window, "resize", this.fireWindowResize, this);
\r
2963 resizeEvent.addListener(fn, scope, options);
\r
2966 // exposed only to allow manual firing
\r
2967 fireWindowResize : function(){
\r
2969 if((Ext.isIE||Ext.isAir) && resizeTask){
\r
2970 resizeTask.delay(50);
\r
2972 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
\r
2978 * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
\r
2979 * @param {Function} fn The method the event invokes
\r
2980 * @param {Object} scope An object that becomes the scope of the handler
\r
2981 * @param {boolean} options
\r
2983 onTextResize : function(fn, scope, options){
\r
2985 textEvent = new Ext.util.Event();
\r
2986 var textEl = new Ext.Element(document.createElement('div'));
\r
2987 textEl.dom.className = 'x-text-resize';
\r
2988 textEl.dom.innerHTML = 'X';
\r
2989 textEl.appendTo(document.body);
\r
2990 textSize = textEl.dom.offsetHeight;
\r
2991 setInterval(function(){
\r
2992 if(textEl.dom.offsetHeight != textSize){
\r
2993 textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
\r
2995 }, this.textResizeInterval);
\r
2997 textEvent.addListener(fn, scope, options);
\r
3001 * Removes the passed window resize listener.
\r
3002 * @param {Function} fn The method the event invokes
\r
3003 * @param {Object} scope The scope of handler
\r
3005 removeResizeListener : function(fn, scope){
\r
3007 resizeEvent.removeListener(fn, scope);
\r
3012 fireResize : function(){
\r
3014 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
\r
3019 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
\r
3021 textResizeInterval : 50,
\r
3024 * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
\r
3026 ieDeferSrc : false,
\r
3028 // protected for use inside the framework
\r
3029 // detects whether we should use keydown or keypress based on the browser.
\r
3030 useKeydown: useKeydown
\r
3034 Ext.EventManager.on = Ext.EventManager.addListener;
\r
3037 Ext.apply(Ext.EventObjectImpl.prototype, {
\r
3038 /** Key constant @type Number */
\r
3040 /** Key constant @type Number */
\r
3042 /** Key constant @type Number */
\r
3044 /** Key constant @type Number */
\r
3046 /** Key constant @type Number */
\r
3048 /** Key constant @type Number */
\r
3050 /** Key constant @type Number */
\r
3052 CONTROL : 17, // legacy
\r
3053 /** Key constant @type Number */
\r
3055 /** Key constant @type Number */
\r
3057 /** Key constant @type Number */
\r
3059 /** Key constant @type Number */
\r
3061 /** Key constant @type Number */
\r
3063 /** Key constant @type Number */
\r
3065 PAGEUP : 33, // legacy
\r
3066 /** Key constant @type Number */
\r
3068 PAGEDOWN : 34, // legacy
\r
3069 /** Key constant @type Number */
\r
3071 /** Key constant @type Number */
\r
3073 /** Key constant @type Number */
\r
3075 /** Key constant @type Number */
\r
3077 /** Key constant @type Number */
\r
3079 /** Key constant @type Number */
\r
3081 /** Key constant @type Number */
\r
3083 /** Key constant @type Number */
\r
3085 /** Key constant @type Number */
\r
3087 /** Key constant @type Number */
\r
3089 /** Key constant @type Number */
\r
3091 /** Key constant @type Number */
\r
3093 /** Key constant @type Number */
\r
3095 /** Key constant @type Number */
\r
3097 /** Key constant @type Number */
\r
3099 /** Key constant @type Number */
\r
3101 /** Key constant @type Number */
\r
3103 /** Key constant @type Number */
\r
3105 /** Key constant @type Number */
\r
3107 /** Key constant @type Number */
\r
3109 /** Key constant @type Number */
\r
3111 /** Key constant @type Number */
\r
3113 /** Key constant @type Number */
\r
3115 /** Key constant @type Number */
\r
3117 /** Key constant @type Number */
\r
3119 /** Key constant @type Number */
\r
3121 /** Key constant @type Number */
\r
3123 /** Key constant @type Number */
\r
3125 /** Key constant @type Number */
\r
3127 /** Key constant @type Number */
\r
3129 /** Key constant @type Number */
\r
3131 /** Key constant @type Number */
\r
3133 /** Key constant @type Number */
\r
3135 /** Key constant @type Number */
\r
3137 /** Key constant @type Number */
\r
3139 /** Key constant @type Number */
\r
3141 /** Key constant @type Number */
\r
3143 /** Key constant @type Number */
\r
3145 /** Key constant @type Number */
\r
3147 /** Key constant @type Number */
\r
3149 /** Key constant @type Number */
\r
3151 /** Key constant @type Number */
\r
3153 /** Key constant @type Number */
\r
3155 /** Key constant @type Number */
\r
3157 /** Key constant @type Number */
\r
3159 /** Key constant @type Number */
\r
3161 /** Key constant @type Number */
\r
3163 /** Key constant @type Number */
\r
3165 /** Key constant @type Number */
\r
3167 /** Key constant @type Number */
\r
3169 /** Key constant @type Number */
\r
3171 /** Key constant @type Number */
\r
3173 /** Key constant @type Number */
\r
3175 /** Key constant @type Number */
\r
3177 /** Key constant @type Number */
\r
3179 /** Key constant @type Number */
\r
3181 /** Key constant @type Number */
\r
3182 NUM_MULTIPLY: 106,
\r
3183 /** Key constant @type Number */
\r
3185 /** Key constant @type Number */
\r
3187 /** Key constant @type Number */
\r
3189 /** Key constant @type Number */
\r
3190 NUM_DIVISION: 111,
\r
3191 /** Key constant @type Number */
\r
3193 /** Key constant @type Number */
\r
3195 /** Key constant @type Number */
\r
3197 /** Key constant @type Number */
\r
3199 /** Key constant @type Number */
\r
3201 /** Key constant @type Number */
\r
3203 /** Key constant @type Number */
\r
3205 /** Key constant @type Number */
\r
3207 /** Key constant @type Number */
\r
3209 /** Key constant @type Number */
\r
3211 /** Key constant @type Number */
\r
3213 /** Key constant @type Number */
\r
3217 isNavKeyPress : function(){
\r
3219 k = this.normalizeKey(me.keyCode);
\r
3220 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
\r
3226 isSpecialKey : function(){
\r
3227 var k = this.normalizeKey(this.keyCode);
\r
3228 return (this.type == 'keypress' && this.ctrlKey) ||
\r
3229 this.isNavKeyPress() ||
\r
3230 (k == this.BACKSPACE) || // Backspace
\r
3231 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
\r
3232 (k >= 44 && k <= 45); // Print Screen, Insert
\r
3235 getPoint : function(){
\r
3236 return new Ext.lib.Point(this.xy[0], this.xy[1]);
\r
3240 * Returns true if the control, meta, shift or alt key was pressed during this event.
\r
3241 * @return {Boolean}
\r
3243 hasModifier : function(){
\r
3244 return ((this.ctrlKey || this.altKey) || this.shiftKey);
\r
3247 * @class Ext.Element
\r
3248 * <p>Encapsulates a DOM element, adding simple DOM manipulation facilities, normalizing for browser differences.</p>
\r
3249 * <p>All instances of this class inherit the methods of {@link Ext.Fx} making visual effects easily available to all DOM elements.</p>
\r
3250 * <p>Note that the events documented in this class are not Ext events, they encapsulate browser events. To
\r
3251 * access the underlying browser event, see {@link Ext.EventObject#browserEvent}. Some older
\r
3252 * browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.</p>
\r
3256 var el = Ext.get("my-div");
\r
3258 // by DOM element reference
\r
3259 var el = Ext.get(myDivElement);
\r
3261 * <b>Animations</b><br />
\r
3262 * <p>When an element is manipulated, by default there is no animation.</p>
\r
3264 var el = Ext.get("my-div");
\r
3269 * <p>Many of the functions for manipulating an element have an optional "animate" parameter. This
\r
3270 * parameter can be specified as boolean (<tt>true</tt>) for default animation effects.</p>
\r
3272 // default animation
\r
3273 el.setWidth(100, true);
\r
3276 * <p>To configure the effects, an object literal with animation options to use as the Element animation
\r
3277 * configuration object can also be specified. Note that the supported Element animation configuration
\r
3278 * options are a subset of the {@link Ext.Fx} animation options specific to Fx effects. The supported
\r
3279 * Element animation configuration options are:</p>
\r
3281 Option Default Description
\r
3282 --------- -------- ---------------------------------------------
\r
3283 {@link Ext.Fx#duration duration} .35 The duration of the animation in seconds
\r
3284 {@link Ext.Fx#easing easing} easeOut The easing method
\r
3285 {@link Ext.Fx#callback callback} none A function to execute when the anim completes
\r
3286 {@link Ext.Fx#scope scope} this The scope (this) of the callback function
\r
3290 // Element animation options object
\r
3292 {@link Ext.Fx#duration duration}: 1,
\r
3293 {@link Ext.Fx#easing easing}: 'elasticIn',
\r
3294 {@link Ext.Fx#callback callback}: this.foo,
\r
3295 {@link Ext.Fx#scope scope}: this
\r
3297 // animation with some options set
\r
3298 el.setWidth(100, opt);
\r
3300 * <p>The Element animation object being used for the animation will be set on the options
\r
3301 * object as "anim", which allows you to stop or manipulate the animation. Here is an example:</p>
\r
3303 // using the "anim" property to get the Anim object
\r
3304 if(opt.anim.isAnimated()){
\r
3308 * <p>Also see the <tt>{@link #animate}</tt> method for another animation technique.</p>
\r
3309 * <p><b> Composite (Collections of) Elements</b></p>
\r
3310 * <p>For working with collections of Elements, see {@link Ext.CompositeElement}</p>
\r
3311 * @constructor Create a new Element directly.
\r
3312 * @param {String/HTMLElement} element
\r
3313 * @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).
\r
3316 var DOC = document;
\r
3318 Ext.Element = function(element, forceNew){
\r
3319 var dom = typeof element == "string" ?
\r
3320 DOC.getElementById(element) : element,
\r
3323 if(!dom) return null;
\r
3327 if(!forceNew && id && Ext.Element.cache[id]){ // element object already exists
\r
3328 return Ext.Element.cache[id];
\r
3333 * @type HTMLElement
\r
3338 * The DOM element ID
\r
3341 this.id = id || Ext.id(dom);
\r
3344 var D = Ext.lib.Dom,
\r
3345 DH = Ext.DomHelper,
\r
3346 E = Ext.lib.Event,
\r
3352 * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
\r
3353 * @param {Object} o The object with the attributes
\r
3354 * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
\r
3355 * @return {Ext.Element} this
\r
3357 set : function(o, useSet){
\r
3358 var el = this.dom,
\r
3364 if (attr != "style" && !Ext.isFunction(val)) {
\r
3365 if (attr == "cls" ) {
\r
3366 el.className = val;
\r
3367 } else if (o.hasOwnProperty(attr)) {
\r
3368 if (useSet || !!el.setAttribute) el.setAttribute(attr, val);
\r
3369 else el[attr] = val;
\r
3374 Ext.DomHelper.applyStyles(el, o.style);
\r
3382 * Fires when a mouse click is detected within the element.
\r
3383 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3384 * @param {HtmlElement} t The target of the event.
\r
3385 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3389 * Fires when a mouse double click is detected within the element.
\r
3390 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3391 * @param {HtmlElement} t The target of the event.
\r
3392 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3395 * @event mousedown
\r
3396 * Fires when a mousedown is detected within the element.
\r
3397 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3398 * @param {HtmlElement} t The target of the event.
\r
3399 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3403 * Fires when a mouseup is detected within the element.
\r
3404 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3405 * @param {HtmlElement} t The target of the event.
\r
3406 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3409 * @event mouseover
\r
3410 * Fires when a mouseover is detected within the element.
\r
3411 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3412 * @param {HtmlElement} t The target of the event.
\r
3413 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3416 * @event mousemove
\r
3417 * Fires when a mousemove is detected with the element.
\r
3418 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3419 * @param {HtmlElement} t The target of the event.
\r
3420 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3424 * Fires when a mouseout is detected with the element.
\r
3425 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3426 * @param {HtmlElement} t The target of the event.
\r
3427 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3430 * @event mouseenter
\r
3431 * Fires when the mouse enters the element.
\r
3432 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3433 * @param {HtmlElement} t The target of the event.
\r
3434 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3437 * @event mouseleave
\r
3438 * Fires when the mouse leaves the element.
\r
3439 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3440 * @param {HtmlElement} t The target of the event.
\r
3441 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3444 // Keyboard events
\r
3447 * Fires when a keypress is detected within the element.
\r
3448 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3449 * @param {HtmlElement} t The target of the event.
\r
3450 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3454 * Fires when a keydown is detected within the element.
\r
3455 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3456 * @param {HtmlElement} t The target of the event.
\r
3457 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3461 * Fires when a keyup is detected within the element.
\r
3462 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3463 * @param {HtmlElement} t The target of the event.
\r
3464 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3468 // HTML frame/object events
\r
3471 * Fires when the user agent finishes loading all content within the element. Only supported by window, frames, objects and images.
\r
3472 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3473 * @param {HtmlElement} t The target of the event.
\r
3474 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3478 * 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.
\r
3479 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3480 * @param {HtmlElement} t The target of the event.
\r
3481 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3485 * Fires when an object/image is stopped from loading before completely loaded.
\r
3486 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3487 * @param {HtmlElement} t The target of the event.
\r
3488 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3492 * Fires when an object/image/frame cannot be loaded properly.
\r
3493 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3494 * @param {HtmlElement} t The target of the event.
\r
3495 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3499 * Fires when a document view is resized.
\r
3500 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3501 * @param {HtmlElement} t The target of the event.
\r
3502 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3506 * Fires when a document view is scrolled.
\r
3507 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3508 * @param {HtmlElement} t The target of the event.
\r
3509 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3515 * Fires when a user selects some text in a text field, including input and textarea.
\r
3516 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3517 * @param {HtmlElement} t The target of the event.
\r
3518 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3522 * Fires when a control loses the input focus and its value has been modified since gaining focus.
\r
3523 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3524 * @param {HtmlElement} t The target of the event.
\r
3525 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3529 * Fires when a form is submitted.
\r
3530 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3531 * @param {HtmlElement} t The target of the event.
\r
3532 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3536 * Fires when a form is reset.
\r
3537 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3538 * @param {HtmlElement} t The target of the event.
\r
3539 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3543 * Fires when an element receives focus either via the pointing device or by tab navigation.
\r
3544 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3545 * @param {HtmlElement} t The target of the event.
\r
3546 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3550 * Fires when an element loses focus either via the pointing device or by tabbing navigation.
\r
3551 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3552 * @param {HtmlElement} t The target of the event.
\r
3553 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3556 // User Interface events
\r
3558 * @event DOMFocusIn
\r
3559 * Where supported. Similar to HTML focus event, but can be applied to any focusable element.
\r
3560 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3561 * @param {HtmlElement} t The target of the event.
\r
3562 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3565 * @event DOMFocusOut
\r
3566 * Where supported. Similar to HTML blur event, but can be applied to any focusable element.
\r
3567 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3568 * @param {HtmlElement} t The target of the event.
\r
3569 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3572 * @event DOMActivate
\r
3573 * Where supported. Fires when an element is activated, for instance, through a mouse click or a keypress.
\r
3574 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3575 * @param {HtmlElement} t The target of the event.
\r
3576 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3579 // DOM Mutation events
\r
3581 * @event DOMSubtreeModified
\r
3582 * Where supported. Fires when the subtree is modified.
\r
3583 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3584 * @param {HtmlElement} t The target of the event.
\r
3585 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3588 * @event DOMNodeInserted
\r
3589 * Where supported. Fires when a node has been added as a child of another node.
\r
3590 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3591 * @param {HtmlElement} t The target of the event.
\r
3592 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3595 * @event DOMNodeRemoved
\r
3596 * Where supported. Fires when a descendant node of the element is removed.
\r
3597 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3598 * @param {HtmlElement} t The target of the event.
\r
3599 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3602 * @event DOMNodeRemovedFromDocument
\r
3603 * Where supported. Fires when a node is being removed from a document.
\r
3604 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3605 * @param {HtmlElement} t The target of the event.
\r
3606 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3609 * @event DOMNodeInsertedIntoDocument
\r
3610 * Where supported. Fires when a node is being inserted into a document.
\r
3611 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3612 * @param {HtmlElement} t The target of the event.
\r
3613 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3616 * @event DOMAttrModified
\r
3617 * Where supported. Fires when an attribute has been modified.
\r
3618 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3619 * @param {HtmlElement} t The target of the event.
\r
3620 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3623 * @event DOMCharacterDataModified
\r
3624 * Where supported. Fires when the character data has been modified.
\r
3625 * @param {Ext.EventObject} e The {@link Ext.EventObject} encapsulating the DOM event.
\r
3626 * @param {HtmlElement} t The target of the event.
\r
3627 * @param {Object} o The options configuration passed to the {@link #addListener} call.
\r
3631 * The default unit to append to CSS values where a unit isn't provided (defaults to px).
\r
3634 defaultUnit : "px",
\r
3637 * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
\r
3638 * @param {String} selector The simple selector to test
\r
3639 * @return {Boolean} True if this element matches the selector, else false
\r
3641 is : function(simpleSelector){
\r
3642 return Ext.DomQuery.is(this.dom, simpleSelector);
\r
3646 * Tries to focus the element. Any exceptions are caught and ignored.
\r
3647 * @param {Number} defer (optional) Milliseconds to defer the focus
\r
3648 * @return {Ext.Element} this
\r
3650 focus : function(defer, /* private */ dom) {
\r
3652 dom = dom || me.dom;
\r
3654 if(Number(defer)){
\r
3655 me.focus.defer(defer, null, [null, dom]);
\r
3664 * Tries to blur the element. Any exceptions are caught and ignored.
\r
3665 * @return {Ext.Element} this
\r
3667 blur : function() {
\r
3675 * Returns the value of the "value" attribute
\r
3676 * @param {Boolean} asNumber true to parse the value as a number
\r
3677 * @return {String/Number}
\r
3679 getValue : function(asNumber){
\r
3680 var val = this.dom.value;
\r
3681 return asNumber ? parseInt(val, 10) : val;
\r
3685 * Appends an event handler to this element. The shorthand version {@link #on} is equivalent.
\r
3686 * @param {String} eventName The type of event to handle
\r
3687 * @param {Function} fn The handler function the event invokes. This function is passed
\r
3688 * the following parameters:<ul>
\r
3689 * <li><b>evt</b> : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
\r
3690 * <li><b>el</b> : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
\r
3691 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
\r
3692 * <li><b>o</b> : Object<div class="sub-desc">The options object from the addListener call.</div></li>
\r
3694 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
\r
3695 * <b>If omitted, defaults to this Element.</b>.
\r
3696 * @param {Object} options (optional) An object containing handler configuration properties.
\r
3697 * This may contain any of the following properties:<ul>
\r
3698 * <li><b>scope</b> Object : <div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
\r
3699 * <b>If omitted, defaults to this Element.</b></div></li>
\r
3700 * <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>
\r
3701 * <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>
\r
3702 * <li><b>preventDefault</b> Boolean: <div class="sub-desc">True to prevent the default action</div></li>
\r
3703 * <li><b>stopPropagation</b> Boolean: <div class="sub-desc">True to prevent event propagation</div></li>
\r
3704 * <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>
\r
3705 * <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>
\r
3706 * <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>
\r
3707 * <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>
\r
3708 * <li><b>buffer</b> Number: <div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
\r
3709 * by the specified number of milliseconds. If the event fires again within that time, the original
\r
3710 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
\r
3713 * <b>Combining Options</b><br>
\r
3714 * In the following examples, the shorthand form {@link #on} is used rather than the more verbose
\r
3715 * addListener. The two are equivalent. Using the options argument, it is possible to combine different
\r
3716 * types of listeners:<br>
\r
3718 * A delayed, one-time listener that auto stops the event and adds a custom argument (forumId) to the
\r
3719 * options object. The options object is available as the third parameter in the handler function.<div style="margin: 5px 20px 20px;">
\r
3720 * Code:<pre><code>
\r
3721 el.on('click', this.onClick, this, {
\r
3726 });</code></pre></p>
\r
3728 * <b>Attaching multiple handlers in 1 call</b><br>
\r
3729 * The method also allows for a single argument to be passed which is a config object containing properties
\r
3730 * which specify multiple handlers.</p>
\r
3732 * Code:<pre><code>
\r
3740 fn: this.onMouseOver,
\r
3744 fn: this.onMouseOut,
\r
3749 * Or a shorthand syntax:<br>
\r
3750 * Code:<pre><code></p>
\r
3752 'click' : this.onClick,
\r
3753 'mouseover' : this.onMouseOver,
\r
3754 'mouseout' : this.onMouseOut,
\r
3757 * </code></pre></p>
\r
3758 * <p><b>delegate</b></p>
\r
3759 * <p>This is a configuration option that you can pass along when registering a handler for
\r
3760 * an event to assist with event delegation. Event delegation is a technique that is used to
\r
3761 * reduce memory consumption and prevent exposure to memory-leaks. By registering an event
\r
3762 * for a container element as opposed to each element within a container. By setting this
\r
3763 * configuration option to a simple selector, the target element will be filtered to look for
\r
3764 * a descendant of the target.
\r
3765 * For example:<pre><code>
\r
3766 // using this markup:
\r
3767 <div id='elId'>
\r
3768 <p id='p1'>paragraph one</p>
\r
3769 <p id='p2' class='clickable'>paragraph two</p>
\r
3770 <p id='p3'>paragraph three</p>
\r
3772 // utilize event delegation to registering just one handler on the container element:
\r
3773 el = Ext.get('elId');
\r
3778 console.info(t.id); // 'p2'
\r
3782 // filter the target element to be a descendant with the class 'clickable'
\r
3783 delegate: '.clickable'
\r
3786 * </code></pre></p>
\r
3787 * @return {Ext.Element} this
\r
3789 addListener : function(eventName, fn, scope, options){
\r
3790 Ext.EventManager.on(this.dom, eventName, fn, scope || this, options);
\r
3795 * Removes an event handler from this element. The shorthand version {@link #un} is equivalent.
\r
3796 * <b>Note</b>: if a <i>scope</i> was explicitly specified when {@link #addListener adding} the
\r
3797 * listener, the same scope must be specified here.
\r
3800 el.removeListener('click', this.handlerFn);
\r
3802 el.un('click', this.handlerFn);
\r
3804 * @param {String} eventName the type of event to remove
\r
3805 * @param {Function} fn the method the event invokes
\r
3806 * @param {Object} scope (optional) The scope (The <tt>this</tt> reference) of the handler function. Defaults
\r
3807 * to this Element.
\r
3808 * @return {Ext.Element} this
\r
3810 removeListener : function(eventName, fn, scope){
\r
3811 Ext.EventManager.removeListener(this.dom, eventName, fn, scope || this);
\r
3816 * Removes all previous added listeners from this element
\r
3817 * @return {Ext.Element} this
\r
3819 removeAllListeners : function(){
\r
3820 Ext.EventManager.removeAll(this.dom);
\r
3825 * @private Test if size has a unit, otherwise appends the default
\r
3827 addUnits : function(size){
\r
3828 if(size === "" || size == "auto" || size === undefined){
\r
3829 size = size || '';
\r
3830 } else if(!isNaN(size) || !unitPattern.test(size)){
\r
3831 size = size + (this.defaultUnit || 'px');
\r
3837 * <p>Updates the <a href="http://developer.mozilla.org/en/DOM/element.innerHTML">innerHTML</a> of this Element
\r
3838 * 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>
\r
3839 * <p>Updating innerHTML of an element will <b>not</b> execute embedded <tt><script></tt> elements. This is a browser restriction.</p>
\r
3840 * @param {Mixed} options. Either a sring containing the URL from which to load the HTML, or an {@link Ext.Ajax#request} options object specifying
\r
3841 * exactly how to request the HTML.
\r
3842 * @return {Ext.Element} this
\r
3844 load : function(url, params, cb){
\r
3845 Ext.Ajax.request(Ext.apply({
\r
3847 url: url.url || url,
\r
3850 indicatorText: url.indicatorText || ''
\r
3851 }, Ext.isObject(url) ? url : {}));
\r
3856 * Tests various css rules/browsers to determine if this element uses a border box
\r
3857 * @return {Boolean}
\r
3859 isBorderBox : function(){
\r
3860 return noBoxAdjust[(this.dom.tagName || "").toLowerCase()] || Ext.isBorderBox;
\r
3864 * Removes this element from the DOM and deletes it from the cache
\r
3866 remove : function(){
\r
3870 me.removeAllListeners();
\r
3871 delete El.cache[dom.id];
\r
3872 delete El.dataCache[dom.id]
\r
3873 Ext.removeNode(dom);
\r
3877 * Sets up event handlers to call the passed functions when the mouse is moved into and out of the Element.
\r
3878 * @param {Function} overFn The function to call when the mouse enters the Element.
\r
3879 * @param {Function} outFn The function to call when the mouse leaves the Element.
\r
3880 * @param {Object} scope (optional) The scope (<tt>this</tt> reference) in which the functions are executed. Defaults to the Element's DOM element.
\r
3881 * @param {Object} options (optional) Options for the listener. See {@link Ext.util.Observable#addListener the <tt>options</tt> parameter}.
\r
3882 * @return {Ext.Element} this
\r
3884 hover : function(overFn, outFn, scope, options){
\r
3886 me.on('mouseenter', overFn, scope || me.dom, options);
\r
3887 me.on('mouseleave', outFn, scope || me.dom, options);
\r
3892 * Returns true if this element is an ancestor of the passed element
\r
3893 * @param {HTMLElement/String} el The element to check
\r
3894 * @return {Boolean} True if this element is an ancestor of el, else false
\r
3896 contains : function(el){
\r
3897 return !el ? false : Ext.lib.Dom.isAncestor(this.dom, el.dom ? el.dom : el);
\r
3901 * Returns the value of a namespaced attribute from the element's underlying DOM node.
\r
3902 * @param {String} namespace The namespace in which to look for the attribute
\r
3903 * @param {String} name The attribute name
\r
3904 * @return {String} The attribute value
\r
3907 getAttributeNS : function(ns, name){
\r
3908 return this.getAttribute(name, ns);
\r
3912 * Returns the value of an attribute from the element's underlying DOM node.
\r
3913 * @param {String} name The attribute name
\r
3914 * @param {String} namespace (optional) The namespace in which to look for the attribute
\r
3915 * @return {String} The attribute value
\r
3917 getAttribute : Ext.isIE ? function(name, ns){
\r
3919 type = typeof d[ns + ":" + name];
\r
3921 if(['undefined', 'unknown'].indexOf(type) == -1){
\r
3922 return d[ns + ":" + name];
\r
3925 } : function(name, ns){
\r
3927 return d.getAttributeNS(ns, name) || d.getAttribute(ns + ":" + name) || d.getAttribute(name) || d[name];
\r
3931 * Update the innerHTML of this element
\r
3932 * @param {String} html The new HTML
\r
3933 * @return {Ext.Element} this
\r
3935 update : function(html) {
\r
3936 this.dom.innerHTML = html;
\r
3941 var ep = El.prototype;
\r
3943 El.addMethods = function(o){
\r
3948 * Appends an event handler (shorthand for {@link #addListener}).
\r
3949 * @param {String} eventName The type of event to handle
\r
3950 * @param {Function} fn The handler function the event invokes
\r
3951 * @param {Object} scope (optional) The scope (this element) of the handler function
\r
3952 * @param {Object} options (optional) An object containing standard {@link #addListener} options
\r
3953 * @member Ext.Element
\r
3956 ep.on = ep.addListener;
\r
3959 * Removes an event handler from this element (see {@link #removeListener} for additional notes).
\r
3960 * @param {String} eventName the type of event to remove
\r
3961 * @param {Function} fn the method the event invokes
\r
3962 * @param {Object} scope (optional) The scope (The <tt>this</tt> reference) of the handler function. Defaults
\r
3963 * to this Element.
\r
3964 * @return {Ext.Element} this
\r
3965 * @member Ext.Element
\r
3968 ep.un = ep.removeListener;
\r
3971 * true to automatically adjust width and height settings for box-model issues (default to true)
\r
3973 ep.autoBoxAdjust = true;
\r
3976 var unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
\r
3983 El.dataCache = {};
\r
3986 * Retrieves Ext.Element objects.
\r
3987 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
\r
3988 * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
\r
3989 * its ID, use {@link Ext.ComponentMgr#get}.</p>
\r
3990 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
\r
3991 * object was recreated with the same id via AJAX or DOM.</p>
\r
3992 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
\r
3993 * @return {Element} The Element object (or null if no matching element was found)
\r
3995 * @member Ext.Element
\r
3998 El.get = function(el){
\r
4002 if(!el){ return null; }
\r
4003 if (typeof el == "string") { // element id
\r
4004 if (!(elm = DOC.getElementById(el))) {
\r
4007 if (ex = El.cache[el]) {
\r
4010 ex = El.cache[el] = new El(elm);
\r
4013 } else if (el.tagName) { // dom element
\r
4014 if(!(id = el.id)){
\r
4017 if(ex = El.cache[id]){
\r
4020 ex = El.cache[id] = new El(el);
\r
4023 } else if (el instanceof El) {
\r
4025 el.dom = DOC.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
\r
4026 // catch case where it hasn't been appended
\r
4027 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
\r
4030 } else if(el.isComposite) {
\r
4032 } else if(Ext.isArray(el)) {
\r
4033 return El.select(el);
\r
4034 } else if(el == DOC) {
\r
4035 // create a bogus element object representing the document object
\r
4037 var f = function(){};
\r
4038 f.prototype = El.prototype;
\r
4047 // private method for getting and setting element data
\r
4048 El.data = function(el, key, value){
\r
4049 var c = El.dataCache[el.id];
\r
4051 c = El.dataCache[el.id] = {};
\r
4053 if(arguments.length == 2){
\r
4061 // Garbage collection - uncache elements/purge listeners on orphaned elements
\r
4062 // so we don't hold a reference and cause the browser to retain them
\r
4063 function garbageCollect(){
\r
4064 if(!Ext.enableGarbageCollector){
\r
4065 clearInterval(El.collectorThread);
\r
4071 for(eid in El.cache){
\r
4072 el = El.cache[eid];
\r
4074 // -------------------------------------------------------
\r
4075 // Determining what is garbage:
\r
4076 // -------------------------------------------------------
\r
4078 // dom node is null, definitely garbage
\r
4079 // -------------------------------------------------------
\r
4081 // no parentNode == direct orphan, definitely garbage
\r
4082 // -------------------------------------------------------
\r
4083 // !d.offsetParent && !document.getElementById(eid)
\r
4084 // display none elements have no offsetParent so we will
\r
4085 // also try to look it up by it's id. However, check
\r
4086 // offsetParent first so we don't do unneeded lookups.
\r
4087 // This enables collection of elements that are not orphans
\r
4088 // directly, but somewhere up the line they have an orphan
\r
4090 // -------------------------------------------------------
\r
4091 if(!d || !d.parentNode || (!d.offsetParent && !DOC.getElementById(eid))){
\r
4092 delete El.cache[eid];
\r
4093 if(d && Ext.enableListenerCollection){
\r
4094 Ext.EventManager.removeAll(d);
\r
4100 El.collectorThreadId = setInterval(garbageCollect, 30000);
\r
4102 var flyFn = function(){};
\r
4103 flyFn.prototype = El.prototype;
\r
4105 // dom is optional
\r
4106 El.Flyweight = function(dom){
\r
4110 El.Flyweight.prototype = new flyFn();
\r
4111 El.Flyweight.prototype.isFlyweight = true;
\r
4112 El._flyweights = {};
\r
4115 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
\r
4116 * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
\r
4117 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
\r
4118 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
\r
4119 * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
\r
4120 * @param {String/HTMLElement} el The dom node or id
\r
4121 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
\r
4122 * (e.g. internally Ext uses "_global")
\r
4123 * @return {Element} The shared Element object (or null if no matching element was found)
\r
4124 * @member Ext.Element
\r
4127 El.fly = function(el, named){
\r
4129 named = named || '_global';
\r
4131 if (el = Ext.getDom(el)) {
\r
4132 (El._flyweights[named] = El._flyweights[named] || new El.Flyweight()).dom = el;
\r
4133 ret = El._flyweights[named];
\r
4139 * Retrieves Ext.Element objects.
\r
4140 * <p><b>This method does not retrieve {@link Ext.Component Component}s.</b> This method
\r
4141 * retrieves Ext.Element objects which encapsulate DOM elements. To retrieve a Component by
\r
4142 * its ID, use {@link Ext.ComponentMgr#get}.</p>
\r
4143 * <p>Uses simple caching to consistently return the same object. Automatically fixes if an
\r
4144 * object was recreated with the same id via AJAX or DOM.</p>
\r
4145 * Shorthand of {@link Ext.Element#get}
\r
4146 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
\r
4147 * @return {Element} The Element object (or null if no matching element was found)
\r
4154 * <p>Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
\r
4155 * the dom node can be overwritten by other code. Shorthand of {@link Ext.Element#fly}</p>
\r
4156 * <p>Use this to make one-time references to DOM elements which are not going to be accessed again either by
\r
4157 * application code, or by Ext's classes. If accessing an element which will be processed regularly, then {@link Ext#get}
\r
4158 * will be more appropriate to take advantage of the caching provided by the Ext.Element class.</p>
\r
4159 * @param {String/HTMLElement} el The dom node or id
\r
4160 * @param {String} named (optional) Allows for creation of named reusable flyweights to prevent conflicts
\r
4161 * (e.g. internally Ext uses "_global")
\r
4162 * @return {Element} The shared Element object (or null if no matching element was found)
\r
4168 // speedy lookup for elements never to box adjust
\r
4169 var noBoxAdjust = Ext.isStrict ? {
\r
4172 input:1, select:1, textarea:1
\r
4174 if(Ext.isIE || Ext.isGecko){
\r
4175 noBoxAdjust['button'] = 1;
\r
4179 Ext.EventManager.on(window, 'unload', function(){
\r
4181 delete El.dataCache;
\r
4182 delete El._flyweights;
\r
4186 * @class Ext.Element
\r
4188 Ext.Element.addMethods({
\r
4190 * Stops the specified event(s) from bubbling and optionally prevents the default action
\r
4191 * @param {String/Array} eventName an event / array of events to stop from bubbling
\r
4192 * @param {Boolean} preventDefault (optional) true to prevent the default action too
\r
4193 * @return {Ext.Element} this
\r
4195 swallowEvent : function(eventName, preventDefault){
\r
4198 e.stopPropagation();
\r
4199 if(preventDefault){
\r
4200 e.preventDefault();
\r
4203 if(Ext.isArray(eventName)){
\r
4204 Ext.each(eventName, function(e) {
\r
4209 me.on(eventName, fn);
\r
4214 * Create an event handler on this element such that when the event fires and is handled by this element,
\r
4215 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
\r
4216 * @param {String} eventName The type of event to relay
\r
4217 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
\r
4218 * for firing the relayed event
\r
4220 relayEvent : function(eventName, observable){
\r
4221 this.on(eventName, function(e){
\r
4222 observable.fireEvent(eventName, e);
\r
4227 * Removes worthless text nodes
\r
4228 * @param {Boolean} forceReclean (optional) By default the element
\r
4229 * keeps track if it has been cleaned already so
\r
4230 * you can call this over and over. However, if you update the element and
\r
4231 * need to force a reclean, you can pass true.
\r
4233 clean : function(forceReclean){
\r
4236 n = dom.firstChild,
\r
4239 if(Ext.Element.data(dom, 'isCleaned') && forceReclean !== true){
\r
4244 var nx = n.nextSibling;
\r
4245 if(n.nodeType == 3 && !/\S/.test(n.nodeValue)){
\r
4246 dom.removeChild(n);
\r
4248 n.nodeIndex = ++ni;
\r
4252 Ext.Element.data(dom, 'isCleaned', true);
\r
4257 * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
\r
4258 * parameter as {@link Ext.Updater#update}
\r
4259 * @return {Ext.Element} this
\r
4261 load : function(){
\r
4262 var um = this.getUpdater();
\r
4263 um.update.apply(um, arguments);
\r
4268 * Gets this element's {@link Ext.Updater Updater}
\r
4269 * @return {Ext.Updater} The Updater
\r
4271 getUpdater : function(){
\r
4272 return this.updateManager || (this.updateManager = new Ext.Updater(this));
\r
4276 * Update the innerHTML of this element, optionally searching for and processing scripts
\r
4277 * @param {String} html The new HTML
\r
4278 * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
\r
4279 * @param {Function} callback (optional) For async script loading you can be notified when the update completes
\r
4280 * @return {Ext.Element} this
\r
4282 update : function(html, loadScripts, callback){
\r
4283 html = html || "";
\r
4285 if(loadScripts !== true){
\r
4286 this.dom.innerHTML = html;
\r
4287 if(Ext.isFunction(callback)){
\r
4293 var id = Ext.id(),
\r
4296 html += '<span id="' + id + '"></span>';
\r
4298 Ext.lib.Event.onAvailable(id, function(){
\r
4299 var DOC = document,
\r
4300 hd = DOC.getElementsByTagName("head")[0],
\r
4301 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
\r
4302 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
\r
4303 typeRe = /\stype=([\'\"])(.*?)\1/i,
\r
4311 while((match = re.exec(html))){
\r
4313 srcMatch = attrs ? attrs.match(srcRe) : false;
\r
4314 if(srcMatch && srcMatch[2]){
\r
4315 s = DOC.createElement("script");
\r
4316 s.src = srcMatch[2];
\r
4317 typeMatch = attrs.match(typeRe);
\r
4318 if(typeMatch && typeMatch[2]){
\r
4319 s.type = typeMatch[2];
\r
4321 hd.appendChild(s);
\r
4322 }else if(match[2] && match[2].length > 0){
\r
4323 if(window.execScript) {
\r
4324 window.execScript(match[2]);
\r
4326 window.eval(match[2]);
\r
4330 el = DOC.getElementById(id);
\r
4331 if(el){Ext.removeNode(el);}
\r
4332 if(Ext.isFunction(callback)){
\r
4336 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
\r
4341 * Creates a proxy element of this element
\r
4342 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
\r
4343 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
\r
4344 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
\r
4345 * @return {Ext.Element} The new proxy element
\r
4347 createProxy : function(config, renderTo, matchBox){
\r
4348 config = Ext.isObject(config) ? config : {tag : "div", cls: config};
\r
4351 proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
\r
4352 Ext.DomHelper.insertBefore(me.dom, config, true);
\r
4354 if(matchBox && me.setBox && me.getBox){ // check to make sure Element.position.js is loaded
\r
4355 proxy.setBox(me.getBox());
\r
4361 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
\r
4364 Ext.Element.uncache = function(el){
\r
4365 for(var i = 0, a = arguments, len = a.length; i < len; i++) {
\r
4367 delete Ext.Element.cache[a[i].id || a[i]];
\r
4371 * @class Ext.Element
\r
4373 Ext.Element.addMethods({
\r
4375 * Gets the x,y coordinates specified by the anchor position on the element.
\r
4376 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo}
\r
4377 * for details on supported anchor positions.
\r
4378 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
\r
4379 * of page coordinates
\r
4380 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
\r
4381 * {width: (target width), height: (target height)} (defaults to the element's current size)
\r
4382 * @return {Array} [x, y] An array containing the element's x and y coordinates
\r
4384 getAnchorXY : function(anchor, local, s){
\r
4385 //Passing a different size is useful for pre-calculating anchors,
\r
4386 //especially for anchored animations that change the el size.
\r
4387 anchor = (anchor || "tl").toLowerCase();
\r
4391 vp = me.dom == document.body || me.dom == document,
\r
4392 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
\r
4393 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),
\r
4397 scroll = me.getScroll(),
\r
4398 extraX = vp ? scroll.left : !local ? o[0] : 0,
\r
4399 extraY = vp ? scroll.top : !local ? o[1] : 0,
\r
4401 c : [r(w * 0.5), r(h * 0.5)],
\r
4402 t : [r(w * 0.5), 0],
\r
4403 l : [0, r(h * 0.5)],
\r
4404 r : [w, r(h * 0.5)],
\r
4405 b : [r(w * 0.5), h],
\r
4412 xy = hash[anchor];
\r
4413 return [xy[0] + extraX, xy[1] + extraY];
\r
4417 * Anchors an element to another element and realigns it when the window is resized.
\r
4418 * @param {Mixed} element The element to align to.
\r
4419 * @param {String} position The position to align to.
\r
4420 * @param {Array} offsets (optional) Offset the positioning by [x, y]
\r
4421 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
\r
4422 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
\r
4423 * is a number, it is used as the buffer delay (defaults to 50ms).
\r
4424 * @param {Function} callback The function to call after the animation finishes
\r
4425 * @return {Ext.Element} this
\r
4427 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
\r
4431 function action(){
\r
4432 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
\r
4433 Ext.callback(callback, Ext.fly(dom));
\r
4436 Ext.EventManager.onWindowResize(action, me);
\r
4438 if(!Ext.isEmpty(monitorScroll)){
\r
4439 Ext.EventManager.on(window, 'scroll', action, me,
\r
4440 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
\r
4442 action.call(me); // align immediately
\r
4447 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
\r
4448 * supported position values.
\r
4449 * @param {Mixed} element The element to align to.
\r
4450 * @param {String} position The position to align to.
\r
4451 * @param {Array} offsets (optional) Offset the positioning by [x, y]
\r
4452 * @return {Array} [x, y]
\r
4454 getAlignToXY : function(el, p, o){
\r
4457 if(!el || !el.dom){
\r
4458 throw "Element.alignToXY with an element that doesn't exist";
\r
4462 p = (p == "?" ? "tl-bl?" : (!/-/.test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
\r
4470 //constrain the aligned el to viewport if necessary
\r
4474 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
\r
4475 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
\r
4483 docElement = doc.documentElement,
\r
4484 docBody = doc.body,
\r
4485 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
\r
4486 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
\r
4487 c = false, //constrain to viewport
\r
4490 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
\r
4493 throw "Element.alignTo with an invalid alignment " + p;
\r
4500 //Subtract the aligned el's internal xy from the target's offset xy
\r
4501 //plus custom offset to get the aligned el's new offset xy
\r
4502 a1 = me.getAnchorXY(p1, true);
\r
4503 a2 = el.getAnchorXY(p2, false);
\r
4505 x = a2[0] - a1[0] + o[0];
\r
4506 y = a2[1] - a1[1] + o[1];
\r
4509 w = me.getWidth();
\r
4510 h = me.getHeight();
\r
4511 r = el.getRegion();
\r
4512 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
\r
4513 //perpendicular to the vp border, allow the aligned el to slide on that border,
\r
4514 //otherwise swap the aligned el to the opposite border of the target.
\r
4515 p1y = p1.charAt(0);
\r
4516 p1x = p1.charAt(p1.length-1);
\r
4517 p2y = p2.charAt(0);
\r
4518 p2x = p2.charAt(p2.length-1);
\r
4519 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
\r
4520 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
\r
4523 if (x + w > dw + scrollX) {
\r
4524 x = swapX ? r.left-w : dw+scrollX-w;
\r
4526 if (x < scrollX) {
\r
4527 x = swapX ? r.right : scrollX;
\r
4529 if (y + h > dh + scrollY) {
\r
4530 y = swapY ? r.top-h : dh+scrollY-h;
\r
4533 y = swapY ? r.bottom : scrollY;
\r
4540 * Aligns this element with another element relative to the specified anchor points. If the other element is the
\r
4541 * document it aligns it to the viewport.
\r
4542 * The position parameter is optional, and can be specified in any one of the following formats:
\r
4544 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
\r
4545 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
\r
4546 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
\r
4547 * deprecated in favor of the newer two anchor syntax below</i>.</li>
\r
4548 * <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
\r
4549 * element's anchor point, and the second value is used as the target's anchor point.</li>
\r
4551 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
\r
4552 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
\r
4553 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
\r
4554 * that specified in order to enforce the viewport constraints.
\r
4555 * Following are all of the supported anchor positions:
\r
4558 ----- -----------------------------
\r
4559 tl The top left corner (default)
\r
4560 t The center of the top edge
\r
4561 tr The top right corner
\r
4562 l The center of the left edge
\r
4563 c In the center of the element
\r
4564 r The center of the right edge
\r
4565 bl The bottom left corner
\r
4566 b The center of the bottom edge
\r
4567 br The bottom right corner
\r
4571 // align el to other-el using the default positioning ("tl-bl", non-constrained)
\r
4572 el.alignTo("other-el");
\r
4574 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
\r
4575 el.alignTo("other-el", "tr?");
\r
4577 // align the bottom right corner of el with the center left edge of other-el
\r
4578 el.alignTo("other-el", "br-l?");
\r
4580 // align the center of el with the bottom left corner of other-el and
\r
4581 // adjust the x position by -6 pixels (and the y position by 0)
\r
4582 el.alignTo("other-el", "c-bl", [-6, 0]);
\r
4584 * @param {Mixed} element The element to align to.
\r
4585 * @param {String} position The position to align to.
\r
4586 * @param {Array} offsets (optional) Offset the positioning by [x, y]
\r
4587 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
4588 * @return {Ext.Element} this
\r
4590 alignTo : function(element, position, offsets, animate){
\r
4592 return me.setXY(me.getAlignToXY(element, position, offsets),
\r
4593 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
\r
4596 // private ==> used outside of core
\r
4597 adjustForConstraints : function(xy, parent, offsets){
\r
4598 return this.getConstrainToXY(parent || document, false, offsets, xy) || xy;
\r
4601 // private ==> used outside of core
\r
4602 getConstrainToXY : function(el, local, offsets, proposedXY){
\r
4603 var os = {top:0, left:0, bottom:0, right: 0};
\r
4605 return function(el, local, offsets, proposedXY){
\r
4607 offsets = offsets ? Ext.applyIf(offsets, os) : os;
\r
4609 var vw, vh, vx = 0, vy = 0;
\r
4610 if(el.dom == document.body || el.dom == document){
\r
4611 vw =Ext.lib.Dom.getViewWidth();
\r
4612 vh = Ext.lib.Dom.getViewHeight();
\r
4614 vw = el.dom.clientWidth;
\r
4615 vh = el.dom.clientHeight;
\r
4617 var vxy = el.getXY();
\r
4623 var s = el.getScroll();
\r
4625 vx += offsets.left + s.left;
\r
4626 vy += offsets.top + s.top;
\r
4628 vw -= offsets.right;
\r
4629 vh -= offsets.bottom;
\r
4634 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
\r
4635 var x = xy[0], y = xy[1];
\r
4636 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
\r
4638 // only move it if it needs it
\r
4639 var moved = false;
\r
4641 // first validate right/bottom
\r
4650 // then make sure top/left isn't negative
\r
4659 return moved ? [x, y] : false;
\r
4665 // el = Ext.get(el);
\r
4666 // offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
\r
4669 // doc = document,
\r
4670 // s = el.getScroll(),
\r
4671 // vxy = el.getXY(),
\r
4672 // vx = offsets.left + s.left,
\r
4673 // vy = offsets.top + s.top,
\r
4674 // vw = -offsets.right,
\r
4675 // vh = -offsets.bottom,
\r
4678 // xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
\r
4681 // w = me.dom.offsetWidth, h = me.dom.offsetHeight,
\r
4682 // moved = false; // only move it if it needs it
\r
4685 // if(el.dom == doc.body || el.dom == doc){
\r
4686 // vw += Ext.lib.Dom.getViewWidth();
\r
4687 // vh += Ext.lib.Dom.getViewHeight();
\r
4689 // vw += el.dom.clientWidth;
\r
4690 // vh += el.dom.clientHeight;
\r
4697 // // first validate right/bottom
\r
4698 // if(x + w > vx + vw){
\r
4699 // x = vx + vw - w;
\r
4702 // if(y + h > vy + vh){
\r
4703 // y = vy + vh - h;
\r
4706 // // then make sure top/left isn't negative
\r
4715 // return moved ? [x, y] : false;
\r
4719 * Calculates the x, y to center this element on the screen
\r
4720 * @return {Array} The x, y values [x, y]
\r
4722 getCenterXY : function(){
\r
4723 return this.getAlignToXY(document, 'c-c');
\r
4727 * Centers the Element in either the viewport, or another Element.
\r
4728 * @param {Mixed} centerIn (optional) The element in which to center the element.
\r
4730 center : function(centerIn){
\r
4731 return this.alignTo(centerIn || document, 'c-c');
\r
4735 * @class Ext.Element
\r
4737 Ext.Element.addMethods(function(){
\r
4738 var PARENTNODE = 'parentNode',
\r
4739 NEXTSIBLING = 'nextSibling',
\r
4740 PREVIOUSSIBLING = 'previousSibling',
\r
4741 DQ = Ext.DomQuery,
\r
4746 * 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
4747 * @param {String} selector The simple selector to test
\r
4748 * @param {Number/Mixed} maxDepth (optional) The max depth to search as a number or element (defaults to 50 || document.body)
\r
4749 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
\r
4750 * @return {HTMLElement} The matching DOM node (or null if no match was found)
\r
4752 findParent : function(simpleSelector, maxDepth, returnEl){
\r
4754 b = document.body,
\r
4757 if(Ext.isGecko && Object.prototype.toString.call(p) == '[object XULElement]') {
\r
4760 maxDepth = maxDepth || 50;
\r
4761 if (isNaN(maxDepth)) {
\r
4762 stopEl = Ext.getDom(maxDepth);
\r
4763 maxDepth = Number.MAX_VALUE;
\r
4765 while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
\r
4766 if(DQ.is(p, simpleSelector)){
\r
4767 return returnEl ? GET(p) : p;
\r
4776 * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
\r
4777 * @param {String} selector The simple selector to test
\r
4778 * @param {Number/Mixed} maxDepth (optional) The max depth to
\r
4779 search as a number or element (defaults to 10 || document.body)
\r
4780 * @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
\r
4781 * @return {HTMLElement} The matching DOM node (or null if no match was found)
\r
4783 findParentNode : function(simpleSelector, maxDepth, returnEl){
\r
4784 var p = Ext.fly(this.dom.parentNode, '_internal');
\r
4785 return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
\r
4789 * 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
4790 * This is a shortcut for findParentNode() that always returns an Ext.Element.
\r
4791 * @param {String} selector The simple selector to test
\r
4792 * @param {Number/Mixed} maxDepth (optional) The max depth to
\r
4793 search as a number or element (defaults to 10 || document.body)
\r
4794 * @return {Ext.Element} The matching DOM node (or null if no match was found)
\r
4796 up : function(simpleSelector, maxDepth){
\r
4797 return this.findParentNode(simpleSelector, maxDepth, true);
\r
4801 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
\r
4802 * @param {String} selector The CSS selector
\r
4803 * @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
4804 * @return {CompositeElement/CompositeElementLite} The composite element
\r
4806 select : function(selector, unique){
\r
4807 return Ext.Element.select(selector, unique, this.dom);
\r
4811 * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
\r
4812 * @param {String} selector The CSS selector
\r
4813 * @return {Array} An array of the matched nodes
\r
4815 query : function(selector, unique){
\r
4816 return DQ.select(selector, this.dom);
\r
4820 * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
\r
4821 * @param {String} selector The CSS selector
\r
4822 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
\r
4823 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
\r
4825 child : function(selector, returnDom){
\r
4826 var n = DQ.selectNode(selector, this.dom);
\r
4827 return returnDom ? n : GET(n);
\r
4831 * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
\r
4832 * @param {String} selector The CSS selector
\r
4833 * @param {Boolean} returnDom (optional) True to return the DOM node instead of Ext.Element (defaults to false)
\r
4834 * @return {HTMLElement/Ext.Element} The child Ext.Element (or DOM node if returnDom = true)
\r
4836 down : function(selector, returnDom){
\r
4837 var n = DQ.selectNode(" > " + selector, this.dom);
\r
4838 return returnDom ? n : GET(n);
\r
4842 * Gets the parent node for this element, optionally chaining up trying to match a selector
\r
4843 * @param {String} selector (optional) Find a parent node that matches the passed simple selector
\r
4844 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
\r
4845 * @return {Ext.Element/HTMLElement} The parent node or null
\r
4847 parent : function(selector, returnDom){
\r
4848 return this.matchNode(PARENTNODE, PARENTNODE, selector, returnDom);
\r
4852 * Gets the next sibling, skipping text nodes
\r
4853 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
\r
4854 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
\r
4855 * @return {Ext.Element/HTMLElement} The next sibling or null
\r
4857 next : function(selector, returnDom){
\r
4858 return this.matchNode(NEXTSIBLING, NEXTSIBLING, selector, returnDom);
\r
4862 * Gets the previous sibling, skipping text nodes
\r
4863 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
\r
4864 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
\r
4865 * @return {Ext.Element/HTMLElement} The previous sibling or null
\r
4867 prev : function(selector, returnDom){
\r
4868 return this.matchNode(PREVIOUSSIBLING, PREVIOUSSIBLING, selector, returnDom);
\r
4873 * Gets the first child, skipping text nodes
\r
4874 * @param {String} selector (optional) Find the next sibling that matches the passed simple selector
\r
4875 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
\r
4876 * @return {Ext.Element/HTMLElement} The first child or null
\r
4878 first : function(selector, returnDom){
\r
4879 return this.matchNode(NEXTSIBLING, 'firstChild', selector, returnDom);
\r
4883 * Gets the last child, skipping text nodes
\r
4884 * @param {String} selector (optional) Find the previous sibling that matches the passed simple selector
\r
4885 * @param {Boolean} returnDom (optional) True to return a raw dom node instead of an Ext.Element
\r
4886 * @return {Ext.Element/HTMLElement} The last child or null
\r
4888 last : function(selector, returnDom){
\r
4889 return this.matchNode(PREVIOUSSIBLING, 'lastChild', selector, returnDom);
\r
4892 matchNode : function(dir, start, selector, returnDom){
\r
4893 var n = this.dom[start];
\r
4895 if(n.nodeType == 1 && (!selector || DQ.is(n, selector))){
\r
4896 return !returnDom ? GET(n) : n;
\r
4904 * @class Ext.Element
\r
4906 Ext.Element.addMethods(
\r
4908 var GETDOM = Ext.getDom,
\r
4910 DH = Ext.DomHelper,
\r
4911 isEl = function(el){
\r
4912 return (el.nodeType || el.dom || typeof el == 'string');
\r
4917 * Appends the passed element(s) to this element
\r
4918 * @param {String/HTMLElement/Array/Element/CompositeElement} el
\r
4919 * @return {Ext.Element} this
\r
4921 appendChild: function(el){
\r
4922 return GET(el).appendTo(this);
\r
4926 * Appends this element to the passed element
\r
4927 * @param {Mixed} el The new parent element
\r
4928 * @return {Ext.Element} this
\r
4930 appendTo: function(el){
\r
4931 GETDOM(el).appendChild(this.dom);
\r
4936 * Inserts this element before the passed element in the DOM
\r
4937 * @param {Mixed} el The element before which this element will be inserted
\r
4938 * @return {Ext.Element} this
\r
4940 insertBefore: function(el){
\r
4941 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el);
\r
4946 * Inserts this element after the passed element in the DOM
\r
4947 * @param {Mixed} el The element to insert after
\r
4948 * @return {Ext.Element} this
\r
4950 insertAfter: function(el){
\r
4951 (el = GETDOM(el)).parentNode.insertBefore(this.dom, el.nextSibling);
\r
4956 * Inserts (or creates) an element (or DomHelper config) as the first child of this element
\r
4957 * @param {Mixed/Object} el The id or element to insert or a DomHelper config to create and insert
\r
4958 * @return {Ext.Element} The new child
\r
4960 insertFirst: function(el, returnDom){
\r
4962 if(isEl(el)){ // element
\r
4964 this.dom.insertBefore(el, this.dom.firstChild);
\r
4965 return !returnDom ? GET(el) : el;
\r
4966 }else{ // dh config
\r
4967 return this.createChild(el, this.dom.firstChild, returnDom);
\r
4972 * Replaces the passed element with this element
\r
4973 * @param {Mixed} el The element to replace
\r
4974 * @return {Ext.Element} this
\r
4976 replace: function(el){
\r
4978 this.insertBefore(el);
\r
4984 * Replaces this element with the passed element
\r
4985 * @param {Mixed/Object} el The new element or a DomHelper config of an element to create
\r
4986 * @return {Ext.Element} this
\r
4988 replaceWith: function(el){
\r
4990 Element = Ext.Element;
\r
4993 me.dom.parentNode.insertBefore(el, me.dom);
\r
4995 el = DH.insertBefore(me.dom, el);
\r
4998 delete Element.cache[me.id];
\r
4999 Ext.removeNode(me.dom);
\r
5000 me.id = Ext.id(me.dom = el);
\r
5001 return Element.cache[me.id] = me;
\r
5005 * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
\r
5006 * @param {Object} config DomHelper element config object. If no tag is specified (e.g., {tag:'input'}) then a div will be
\r
5007 * automatically generated with the specified attributes.
\r
5008 * @param {HTMLElement} insertBefore (optional) a child element of this element
\r
5009 * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
\r
5010 * @return {Ext.Element} The new child element
\r
5012 createChild: function(config, insertBefore, returnDom){
\r
5013 config = config || {tag:'div'};
\r
5014 return insertBefore ?
\r
5015 DH.insertBefore(insertBefore, config, returnDom !== true) :
\r
5016 DH[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config, returnDom !== true);
\r
5020 * Creates and wraps this element with another element
\r
5021 * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
\r
5022 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
\r
5023 * @return {HTMLElement/Element} The newly created wrapper element
\r
5025 wrap: function(config, returnDom){
\r
5026 var newEl = DH.insertBefore(this.dom, config || {tag: "div"}, !returnDom);
\r
5027 newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
\r
5032 * Inserts an html fragment into this element
\r
5033 * @param {String} where Where to insert the html in relation to this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
\r
5034 * @param {String} html The HTML fragment
\r
5035 * @param {Boolean} returnEl (optional) True to return an Ext.Element (defaults to false)
\r
5036 * @return {HTMLElement/Ext.Element} The inserted node (or nearest related if more than 1 inserted)
\r
5038 insertHtml : function(where, html, returnEl){
\r
5039 var el = DH.insertHtml(where, this.dom, html);
\r
5040 return returnEl ? Ext.get(el) : el;
\r
5044 * @class Ext.Element
\r
5046 Ext.apply(Ext.Element.prototype, function() {
\r
5047 var GETDOM = Ext.getDom,
\r
5049 DH = Ext.DomHelper;
\r
5053 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
\r
5054 * @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
5055 * @param {String} where (optional) 'before' or 'after' defaults to before
\r
5056 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
\r
5057 * @return {Ext.Element} the inserted Element
\r
5059 insertSibling: function(el, where, returnDom){
\r
5063 if(Ext.isArray(el)){
\r
5064 Ext.each(el, function(e) {
\r
5065 rt = me.insertSibling(e, where, returnDom);
\r
5070 where = (where || 'before').toLowerCase();
\r
5073 if(el.nodeType || el.dom){
\r
5074 rt = me.dom.parentNode.insertBefore(GETDOM(el), where == 'before' ? me.dom : me.dom.nextSibling);
\r
5079 if (where == 'after' && !me.dom.nextSibling) {
\r
5080 rt = DH.append(me.dom.parentNode, el, !returnDom);
\r
5082 rt = DH[where == 'after' ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
\r
5089 * @class Ext.Element
\r
5091 Ext.Element.addMethods(function(){
\r
5092 // local style camelizing for speed
\r
5093 var propCache = {},
\r
5094 camelRe = /(-[a-z])/gi,
\r
5095 classReCache = {},
\r
5096 view = document.defaultView,
\r
5097 propFloat = Ext.isIE ? 'styleFloat' : 'cssFloat',
\r
5098 opacityRe = /alpha\(opacity=(.*)\)/i,
\r
5099 trimRe = /^\s+|\s+$/g,
\r
5100 EL = Ext.Element,
\r
5101 PADDING = "padding",
\r
5102 MARGIN = "margin",
\r
5103 BORDER = "border",
\r
5107 BOTTOM = "-bottom",
\r
5108 WIDTH = "-width",
\r
5110 HIDDEN = 'hidden',
\r
5111 ISCLIPPED = 'isClipped',
\r
5112 OVERFLOW = 'overflow',
\r
5113 OVERFLOWX = 'overflow-x',
\r
5114 OVERFLOWY = 'overflow-y',
\r
5115 ORIGINALCLIP = 'originalClip',
\r
5116 // special markup used throughout Ext when box wrapping elements
\r
5117 borders = {l: BORDER + LEFT + WIDTH, r: BORDER + RIGHT + WIDTH, t: BORDER + TOP + WIDTH, b: BORDER + BOTTOM + WIDTH},
\r
5118 paddings = {l: PADDING + LEFT, r: PADDING + RIGHT, t: PADDING + TOP, b: PADDING + BOTTOM},
\r
5119 margins = {l: MARGIN + LEFT, r: MARGIN + RIGHT, t: MARGIN + TOP, b: MARGIN + BOTTOM},
\r
5120 data = Ext.Element.data;
\r
5124 function camelFn(m, a) {
\r
5125 return a.charAt(1).toUpperCase();
\r
5128 // private (needs to be called => addStyles.call(this, sides, styles))
\r
5129 function addStyles(sides, styles){
\r
5132 Ext.each(sides.match(/\w/g), function(s) {
\r
5133 if (s = parseInt(this.getStyle(styles[s]), 10)) {
\r
5134 val += MATH.abs(s);
\r
5141 function chkCache(prop) {
\r
5142 return propCache[prop] || (propCache[prop] = prop == 'float' ? propFloat : prop.replace(camelRe, camelFn));
\r
5147 // private ==> used by Fx
\r
5148 adjustWidth : function(width) {
\r
5150 var isNum = (typeof width == "number");
\r
5151 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
\r
5152 width -= (me.getBorderWidth("lr") + me.getPadding("lr"));
\r
5154 return (isNum && width < 0) ? 0 : width;
\r
5157 // private ==> used by Fx
\r
5158 adjustHeight : function(height) {
\r
5160 var isNum = (typeof height == "number");
\r
5161 if(isNum && me.autoBoxAdjust && !me.isBorderBox()){
\r
5162 height -= (me.getBorderWidth("tb") + me.getPadding("tb"));
\r
5164 return (isNum && height < 0) ? 0 : height;
\r
5169 * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
\r
5170 * @param {String/Array} className The CSS class to add, or an array of classes
\r
5171 * @return {Ext.Element} this
\r
5173 addClass : function(className){
\r
5175 Ext.each(className, function(v) {
\r
5176 me.dom.className += (!me.hasClass(v) && v ? " " + v : "");
\r
5182 * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
\r
5183 * @param {String/Array} className The CSS class to add, or an array of classes
\r
5184 * @return {Ext.Element} this
\r
5186 radioClass : function(className){
\r
5187 Ext.each(this.dom.parentNode.childNodes, function(v) {
\r
5188 if(v.nodeType == 1) {
\r
5189 Ext.fly(v, '_internal').removeClass(className);
\r
5192 return this.addClass(className);
\r
5196 * Removes one or more CSS classes from the element.
\r
5197 * @param {String/Array} className The CSS class to remove, or an array of classes
\r
5198 * @return {Ext.Element} this
\r
5200 removeClass : function(className){
\r
5202 if (me.dom.className) {
\r
5203 Ext.each(className, function(v) {
\r
5204 me.dom.className = me.dom.className.replace(
\r
5205 classReCache[v] = classReCache[v] || new RegExp('(?:^|\\s+)' + v + '(?:\\s+|$)', "g"),
\r
5213 * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
\r
5214 * @param {String} className The CSS class to toggle
\r
5215 * @return {Ext.Element} this
\r
5217 toggleClass : function(className){
\r
5218 return this.hasClass(className) ? this.removeClass(className) : this.addClass(className);
\r
5222 * Checks if the specified CSS class exists on this element's DOM node.
\r
5223 * @param {String} className The CSS class to check for
\r
5224 * @return {Boolean} True if the class exists, else false
\r
5226 hasClass : function(className){
\r
5227 return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
\r
5231 * Replaces a CSS class on the element with another. If the old name does not exist, the new name will simply be added.
\r
5232 * @param {String} oldClassName The CSS class to replace
\r
5233 * @param {String} newClassName The replacement CSS class
\r
5234 * @return {Ext.Element} this
\r
5236 replaceClass : function(oldClassName, newClassName){
\r
5237 return this.removeClass(oldClassName).addClass(newClassName);
\r
5240 isStyle : function(style, val) {
\r
5241 return this.getStyle(style) == val;
\r
5245 * Normalizes currentStyle and computedStyle.
\r
5246 * @param {String} property The style property whose value is returned.
\r
5247 * @return {String} The current value of the style property for this element.
\r
5249 getStyle : function(){
\r
5250 return view && view.getComputedStyle ?
\r
5252 var el = this.dom,
\r
5255 if(el == document) return null;
\r
5256 prop = chkCache(prop);
\r
5257 return (v = el.style[prop]) ? v :
\r
5258 (cs = view.getComputedStyle(el, "")) ? cs[prop] : null;
\r
5261 var el = this.dom,
\r
5265 if(el == document) return null;
\r
5266 if (prop == 'opacity') {
\r
5267 if (el.style.filter.match) {
\r
5268 if(m = el.style.filter.match(opacityRe)){
\r
5269 var fv = parseFloat(m[1]);
\r
5271 return fv ? fv / 100 : 0;
\r
5277 prop = chkCache(prop);
\r
5278 return el.style[prop] || ((cs = el.currentStyle) ? cs[prop] : null);
\r
5283 * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
\r
5284 * are convert to standard 6 digit hex color.
\r
5285 * @param {String} attr The css attribute
\r
5286 * @param {String} defaultValue The default value to use when a valid color isn't found
\r
5287 * @param {String} prefix (optional) defaults to #. Use an empty string when working with
\r
5290 getColor : function(attr, defaultValue, prefix){
\r
5291 var v = this.getStyle(attr),
\r
5292 color = prefix || '#',
\r
5295 if(!v || /transparent|inherit/.test(v)){
\r
5296 return defaultValue;
\r
5299 Ext.each(v.slice(4, v.length -1).split(','), function(s){
\r
5300 h = parseInt(s, 10);
\r
5301 color += (h < 16 ? '0' : '') + h.toString(16);
\r
5304 v = v.replace('#', '');
\r
5305 color += v.length == 3 ? v.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3') : v;
\r
5307 return(color.length > 5 ? color.toLowerCase() : defaultValue);
\r
5311 * Wrapper for setting style properties, also takes single object parameter of multiple styles.
\r
5312 * @param {String/Object} property The style property to be set, or an object of multiple styles.
\r
5313 * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
\r
5314 * @return {Ext.Element} this
\r
5316 setStyle : function(prop, value){
\r
5320 if (!Ext.isObject(prop)) {
\r
5322 tmp[prop] = value;
\r
5325 for (style in prop) {
\r
5326 value = prop[style];
\r
5327 style == 'opacity' ?
\r
5328 this.setOpacity(value) :
\r
5329 this.dom.style[chkCache(style)] = value;
\r
5335 * Set the opacity of the element
\r
5336 * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
\r
5337 * @param {Boolean/Object} animate (optional) a standard Element animation config object or <tt>true</tt> for
\r
5338 * the default animation (<tt>{duration: .35, easing: 'easeIn'}</tt>)
\r
5339 * @return {Ext.Element} this
\r
5341 setOpacity : function(opacity, animate){
\r
5345 if(!animate || !me.anim){
\r
5347 var opac = opacity < 1 ? 'alpha(opacity=' + opacity * 100 + ')' : '',
\r
5348 val = s.filter.replace(opacityRe, '').replace(trimRe, '');
\r
5351 s.filter = val + (val.length > 0 ? ' ' : '') + opac;
\r
5353 s.opacity = opacity;
\r
5356 me.anim({opacity: {to: opacity}}, me.preanim(arguments, 1), null, .35, 'easeIn');
\r
5362 * Clears any opacity settings from this element. Required in some cases for IE.
\r
5363 * @return {Ext.Element} this
\r
5365 clearOpacity : function(){
\r
5366 var style = this.dom.style;
\r
5368 if(!Ext.isEmpty(style.filter)){
\r
5369 style.filter = style.filter.replace(opacityRe, '').replace(trimRe, '');
\r
5372 style.opacity = style['-moz-opacity'] = style['-khtml-opacity'] = '';
\r
5378 * Returns the offset height of the element
\r
5379 * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
\r
5380 * @return {Number} The element's height
\r
5382 getHeight : function(contentHeight){
\r
5385 h = MATH.max(dom.offsetHeight, dom.clientHeight) || 0;
\r
5386 h = !contentHeight ? h : h - me.getBorderWidth("tb") - me.getPadding("tb");
\r
5387 return h < 0 ? 0 : h;
\r
5391 * Returns the offset width of the element
\r
5392 * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
\r
5393 * @return {Number} The element's width
\r
5395 getWidth : function(contentWidth){
\r
5398 w = MATH.max(dom.offsetWidth, dom.clientWidth) || 0;
\r
5399 w = !contentWidth ? w : w - me.getBorderWidth("lr") - me.getPadding("lr");
\r
5400 return w < 0 ? 0 : w;
\r
5404 * Set the width of this Element.
\r
5405 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
\r
5406 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
\r
5407 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
\r
5409 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
5410 * @return {Ext.Element} this
\r
5412 setWidth : function(width, animate){
\r
5414 width = me.adjustWidth(width);
\r
5415 !animate || !me.anim ?
\r
5416 me.dom.style.width = me.addUnits(width) :
\r
5417 me.anim({width : {to : width}}, me.preanim(arguments, 1));
\r
5422 * Set the height of this Element.
\r
5424 // change the height to 200px and animate with default configuration
\r
5425 Ext.fly('elementId').setHeight(200, true);
\r
5427 // change the height to 150px and animate with a custom configuration
\r
5428 Ext.fly('elId').setHeight(150, {
\r
5429 duration : .5, // animation will have a duration of .5 seconds
\r
5430 // will change the content to "finished"
\r
5431 callback: function(){ this.{@link #update}("finished"); }
\r
5434 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
\r
5435 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels.)</li>
\r
5436 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
\r
5438 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
5439 * @return {Ext.Element} this
\r
5441 setHeight : function(height, animate){
\r
5443 height = me.adjustHeight(height);
\r
5444 !animate || !me.anim ?
\r
5445 me.dom.style.height = me.addUnits(height) :
\r
5446 me.anim({height : {to : height}}, me.preanim(arguments, 1));
\r
5451 * Gets the width of the border(s) for the specified side(s)
\r
5452 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
\r
5453 * passing <tt>'lr'</tt> would get the border <b><u>l</u></b>eft width + the border <b><u>r</u></b>ight width.
\r
5454 * @return {Number} The width of the sides passed added together
\r
5456 getBorderWidth : function(side){
\r
5457 return addStyles.call(this, side, borders);
\r
5461 * Gets the width of the padding(s) for the specified side(s)
\r
5462 * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
\r
5463 * passing <tt>'lr'</tt> would get the padding <b><u>l</u></b>eft + the padding <b><u>r</u></b>ight.
\r
5464 * @return {Number} The padding of the sides passed added together
\r
5466 getPadding : function(side){
\r
5467 return addStyles.call(this, side, paddings);
\r
5471 * Store the current overflow setting and clip overflow on the element - use <tt>{@link #unclip}</tt> to remove
\r
5472 * @return {Ext.Element} this
\r
5474 clip : function(){
\r
5478 if(!data(dom, ISCLIPPED)){
\r
5479 data(dom, ISCLIPPED, true);
\r
5480 data(dom, ORIGINALCLIP, {
\r
5481 o: me.getStyle(OVERFLOW),
\r
5482 x: me.getStyle(OVERFLOWX),
\r
5483 y: me.getStyle(OVERFLOWY)
\r
5485 me.setStyle(OVERFLOW, HIDDEN);
\r
5486 me.setStyle(OVERFLOWX, HIDDEN);
\r
5487 me.setStyle(OVERFLOWY, HIDDEN);
\r
5493 * Return clipping (overflow) to original clipping before <tt>{@link #clip}</tt> was called
\r
5494 * @return {Ext.Element} this
\r
5496 unclip : function(){
\r
5500 if(data(dom, ISCLIPPED)){
\r
5501 data(dom, ISCLIPPED, false);
\r
5502 var o = data(dom, ORIGINALCLIP);
\r
5504 me.setStyle(OVERFLOW, o.o);
\r
5507 me.setStyle(OVERFLOWX, o.x);
\r
5510 me.setStyle(OVERFLOWY, o.y);
\r
5516 addStyles : addStyles,
\r
5521 * @class Ext.Element
\r
5524 // special markup used throughout Ext when box wrapping elements
\r
5525 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
5527 Ext.Element.addMethods(function(){
\r
5528 var INTERNAL = "_internal";
\r
5531 * More flexible version of {@link #setStyle} for setting style properties.
\r
5532 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
\r
5533 * a function which returns such a specification.
\r
5534 * @return {Ext.Element} this
\r
5536 applyStyles : function(style){
\r
5537 Ext.DomHelper.applyStyles(this.dom, style);
\r
5542 * Returns an object with properties matching the styles requested.
\r
5543 * For example, el.getStyles('color', 'font-size', 'width') might return
\r
5544 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
\r
5545 * @param {String} style1 A style name
\r
5546 * @param {String} style2 A style name
\r
5547 * @param {String} etc.
\r
5548 * @return {Object} The style object
\r
5550 getStyles : function(){
\r
5552 Ext.each(arguments, function(v) {
\r
5553 ret[v] = this.getStyle(v);
\r
5559 getStyleSize : function(){
\r
5565 if(s.width && s.width != 'auto'){
\r
5566 w = parseInt(s.width, 10);
\r
5567 if(me.isBorderBox()){
\r
5568 w -= me.getFrameWidth('lr');
\r
5571 if(s.height && s.height != 'auto'){
\r
5572 h = parseInt(s.height, 10);
\r
5573 if(me.isBorderBox()){
\r
5574 h -= me.getFrameWidth('tb');
\r
5577 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
\r
5580 // private ==> used by ext full
\r
5581 setOverflow : function(v){
\r
5582 var dom = this.dom;
\r
5583 if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
\r
5584 dom.style.overflow = 'hidden';
\r
5585 (function(){dom.style.overflow = 'auto';}).defer(1);
\r
5587 dom.style.overflow = v;
\r
5592 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
\r
5593 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
\r
5594 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
\r
5595 * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}). The markup
\r
5596 * is of this form:</p>
\r
5598 Ext.Element.boxMarkup =
\r
5599 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
\r
5600 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
\r
5601 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
\r
5603 * <p>Example usage:</p>
\r
5606 Ext.get("foo").boxWrap();
\r
5608 // You can also add a custom class and use CSS inheritance rules to customize the box look.
\r
5609 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
\r
5610 // for how to create a custom box wrap style.
\r
5611 Ext.get("foo").boxWrap().addClass("x-box-blue");
\r
5613 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
\r
5614 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
\r
5615 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
\r
5616 * also supply all of the necessary rules.
\r
5617 * @return {Ext.Element} this
\r
5619 boxWrap : function(cls){
\r
5620 cls = cls || 'x-box';
\r
5621 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
5622 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
\r
5627 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
\r
5628 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
\r
5629 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
\r
5630 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
\r
5631 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
\r
5633 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
\r
5634 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
\r
5635 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
\r
5637 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
5638 * @return {Ext.Element} this
\r
5640 setSize : function(width, height, animate){
\r
5642 if(Ext.isObject(width)){ // in case of object from getSize()
\r
5643 height = width.height;
\r
5644 width = width.width;
\r
5646 width = me.adjustWidth(width);
\r
5647 height = me.adjustHeight(height);
\r
5648 if(!animate || !me.anim){
\r
5649 me.dom.style.width = me.addUnits(width);
\r
5650 me.dom.style.height = me.addUnits(height);
\r
5652 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
\r
5658 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
\r
5659 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
\r
5660 * if a height has not been set using CSS.
\r
5661 * @return {Number}
\r
5663 getComputedHeight : function(){
\r
5665 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
\r
5667 h = parseInt(me.getStyle('height'), 10) || 0;
\r
5668 if(!me.isBorderBox()){
\r
5669 h += me.getFrameWidth('tb');
\r
5676 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
\r
5677 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
\r
5678 * if a width has not been set using CSS.
\r
5679 * @return {Number}
\r
5681 getComputedWidth : function(){
\r
5682 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
\r
5684 w = parseInt(this.getStyle('width'), 10) || 0;
\r
5685 if(!this.isBorderBox()){
\r
5686 w += this.getFrameWidth('lr');
\r
5693 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
\r
5694 for more information about the sides.
\r
5695 * @param {String} sides
\r
5696 * @return {Number}
\r
5698 getFrameWidth : function(sides, onlyContentBox){
\r
5699 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
\r
5703 * Sets up event handlers to add and remove a css class when the mouse is over this element
\r
5704 * @param {String} className
\r
5705 * @return {Ext.Element} this
\r
5707 addClassOnOver : function(className){
\r
5710 Ext.fly(this, INTERNAL).addClass(className);
\r
5713 Ext.fly(this, INTERNAL).removeClass(className);
\r
5720 * Sets up event handlers to add and remove a css class when this element has the focus
\r
5721 * @param {String} className
\r
5722 * @return {Ext.Element} this
\r
5724 addClassOnFocus : function(className){
\r
5725 this.on("focus", function(){
\r
5726 Ext.fly(this, INTERNAL).addClass(className);
\r
5728 this.on("blur", function(){
\r
5729 Ext.fly(this, INTERNAL).removeClass(className);
\r
5735 * 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
5736 * @param {String} className
\r
5737 * @return {Ext.Element} this
\r
5739 addClassOnClick : function(className){
\r
5740 var dom = this.dom;
\r
5741 this.on("mousedown", function(){
\r
5742 Ext.fly(dom, INTERNAL).addClass(className);
\r
5743 var d = Ext.getDoc(),
\r
5745 Ext.fly(dom, INTERNAL).removeClass(className);
\r
5746 d.removeListener("mouseup", fn);
\r
5748 d.on("mouseup", fn);
\r
5754 * Returns the width and height of the viewport.
\r
5756 var vpSize = Ext.getBody().getViewSize();
\r
5758 // all Windows created afterwards will have a default value of 90% height and 95% width
\r
5759 Ext.Window.override({
\r
5760 width: vpSize.width * 0.9,
\r
5761 height: vpSize.height * 0.95
\r
5763 // To handle window resizing you would have to hook onto onWindowResize.
\r
5765 * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
\r
5767 getViewSize : function(){
\r
5768 var doc = document,
\r
5770 extdom = Ext.lib.Dom,
\r
5771 isDoc = (d == doc || d == doc.body);
\r
5772 return { width : (isDoc ? extdom.getViewWidth() : d.clientWidth),
\r
5773 height : (isDoc ? extdom.getViewHeight() : d.clientHeight) };
\r
5777 * Returns the size of the element.
\r
5778 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
\r
5779 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
\r
5781 getSize : function(contentSize){
\r
5782 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
\r
5786 * Forces the browser to repaint this element
\r
5787 * @return {Ext.Element} this
\r
5789 repaint : function(){
\r
5790 var dom = this.dom;
\r
5791 this.addClass("x-repaint");
\r
5792 setTimeout(function(){
\r
5793 Ext.fly(dom).removeClass("x-repaint");
\r
5799 * Disables text selection for this element (normalized across browsers)
\r
5800 * @return {Ext.Element} this
\r
5802 unselectable : function(){
\r
5803 this.dom.unselectable = "on";
\r
5804 return this.swallowEvent("selectstart", true).
\r
5805 applyStyles("-moz-user-select:none;-khtml-user-select:none;").
\r
5806 addClass("x-unselectable");
\r
5810 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
\r
5811 * then it returns the calculated width of the sides (see getPadding)
\r
5812 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
\r
5813 * @return {Object/Number}
\r
5815 getMargins : function(side){
\r
5818 hash = {t:"top", l:"left", r:"right", b: "bottom"},
\r
5822 for (key in me.margins){
\r
5823 o[hash[key]] = parseInt(me.getStyle(me.margins[key]), 10) || 0;
\r
5827 return me.addStyles.call(me, side, me.margins);
\r
5832 * @class Ext.Element
\r
5835 var D = Ext.lib.Dom,
\r
5839 BOTTOM = "bottom",
\r
5840 POSITION = "position",
\r
5841 STATIC = "static",
\r
5842 RELATIVE = "relative",
\r
5844 ZINDEX = "z-index";
\r
5846 function animTest(args, animate, i) {
\r
5847 return this.preanim && !!animate ? this.preanim(args, i) : false
\r
5850 Ext.Element.addMethods({
\r
5852 * 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
5853 * @return {Number} The X position of the element
\r
5855 getX : function(){
\r
5856 return D.getX(this.dom);
\r
5860 * 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
5861 * @return {Number} The Y position of the element
\r
5863 getY : function(){
\r
5864 return D.getY(this.dom);
\r
5868 * 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
5869 * @return {Array} The XY position of the element
\r
5871 getXY : function(){
\r
5872 return D.getXY(this.dom);
\r
5876 * 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
5877 * @param {Mixed} element The element to get the offsets from.
\r
5878 * @return {Array} The XY page offsets (e.g. [100, -200])
\r
5880 getOffsetsTo : function(el){
\r
5881 var o = this.getXY(),
\r
5882 e = Ext.fly(el, '_internal').getXY();
\r
5883 return [o[0]-e[0],o[1]-e[1]];
\r
5887 * 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
5888 * @param {Number} The X position of the element
\r
5889 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
5890 * @return {Ext.Element} this
\r
5892 setX : function(x, animate){
\r
5893 return this.setXY([x, this.getY()], animTest.call(this, arguments, animate, 1));
\r
5897 * 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
5898 * @param {Number} The Y position of the element
\r
5899 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
5900 * @return {Ext.Element} this
\r
5902 setY : function(y, animate){
\r
5903 return this.setXY([this.getX(), y], animTest.call(this, arguments, animate, 1));
\r
5907 * Sets the element's left position directly using CSS style (instead of {@link #setX}).
\r
5908 * @param {String} left The left CSS property value
\r
5909 * @return {Ext.Element} this
\r
5911 setLeft : function(left){
\r
5912 this.setStyle(LEFT, this.addUnits(left));
\r
5917 * Sets the element's top position directly using CSS style (instead of {@link #setY}).
\r
5918 * @param {String} top The top CSS property value
\r
5919 * @return {Ext.Element} this
\r
5921 setTop : function(top){
\r
5922 this.setStyle(TOP, this.addUnits(top));
\r
5927 * Sets the element's CSS right style.
\r
5928 * @param {String} right The right CSS property value
\r
5929 * @return {Ext.Element} this
\r
5931 setRight : function(right){
\r
5932 this.setStyle(RIGHT, this.addUnits(right));
\r
5937 * Sets the element's CSS bottom style.
\r
5938 * @param {String} bottom The bottom CSS property value
\r
5939 * @return {Ext.Element} this
\r
5941 setBottom : function(bottom){
\r
5942 this.setStyle(BOTTOM, this.addUnits(bottom));
\r
5947 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
\r
5948 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
\r
5949 * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
\r
5950 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
5951 * @return {Ext.Element} this
\r
5953 setXY : function(pos, animate){
\r
5955 if(!animate || !me.anim){
\r
5956 D.setXY(me.dom, pos);
\r
5958 me.anim({points: {to: pos}}, me.preanim(arguments, 1), 'motion');
\r
5964 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
\r
5965 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
\r
5966 * @param {Number} x X value for new position (coordinates are page-based)
\r
5967 * @param {Number} y Y value for new position (coordinates are page-based)
\r
5968 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
5969 * @return {Ext.Element} this
\r
5971 setLocation : function(x, y, animate){
\r
5972 return this.setXY([x, y], animTest.call(this, arguments, animate, 2));
\r
5976 * Sets the position of the element in page coordinates, regardless of how the element is positioned.
\r
5977 * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
\r
5978 * @param {Number} x X value for new position (coordinates are page-based)
\r
5979 * @param {Number} y Y value for new position (coordinates are page-based)
\r
5980 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
5981 * @return {Ext.Element} this
\r
5983 moveTo : function(x, y, animate){
\r
5984 return this.setXY([x, y], animTest.call(this, arguments, animate, 2));
\r
5988 * Gets the left X coordinate
\r
5989 * @param {Boolean} local True to get the local css position instead of page coordinate
\r
5990 * @return {Number}
\r
5992 getLeft : function(local){
\r
5993 return !local ? this.getX() : parseInt(this.getStyle(LEFT), 10) || 0;
\r
5997 * Gets the right X coordinate of the element (element X position + element width)
\r
5998 * @param {Boolean} local True to get the local css position instead of page coordinate
\r
5999 * @return {Number}
\r
6001 getRight : function(local){
\r
6003 return !local ? me.getX() + me.getWidth() : (me.getLeft(true) + me.getWidth()) || 0;
\r
6007 * Gets the top Y coordinate
\r
6008 * @param {Boolean} local True to get the local css position instead of page coordinate
\r
6009 * @return {Number}
\r
6011 getTop : function(local) {
\r
6012 return !local ? this.getY() : parseInt(this.getStyle(TOP), 10) || 0;
\r
6016 * Gets the bottom Y coordinate of the element (element Y position + element height)
\r
6017 * @param {Boolean} local True to get the local css position instead of page coordinate
\r
6018 * @return {Number}
\r
6020 getBottom : function(local){
\r
6022 return !local ? me.getY() + me.getHeight() : (me.getTop(true) + me.getHeight()) || 0;
\r
6026 * Initializes positioning on this element. If a desired position is not passed, it will make the
\r
6027 * the element positioned relative IF it is not already positioned.
\r
6028 * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
\r
6029 * @param {Number} zIndex (optional) The zIndex to apply
\r
6030 * @param {Number} x (optional) Set the page X position
\r
6031 * @param {Number} y (optional) Set the page Y position
\r
6033 position : function(pos, zIndex, x, y){
\r
6036 if(!pos && me.isStyle(POSITION, STATIC)){
\r
6037 me.setStyle(POSITION, RELATIVE);
\r
6039 me.setStyle(POSITION, pos);
\r
6042 me.setStyle(ZINDEX, zIndex);
\r
6044 if(x || y) me.setXY([x || false, y || false]);
\r
6048 * Clear positioning back to the default when the document was loaded
\r
6049 * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
\r
6050 * @return {Ext.Element} this
\r
6052 clearPositioning : function(value){
\r
6053 value = value || '';
\r
6066 * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
\r
6067 * snapshot before performing an update and then restoring the element.
\r
6068 * @return {Object}
\r
6070 getPositioning : function(){
\r
6071 var l = this.getStyle(LEFT);
\r
6072 var t = this.getStyle(TOP);
\r
6074 "position" : this.getStyle(POSITION),
\r
6076 "right" : l ? "" : this.getStyle(RIGHT),
\r
6078 "bottom" : t ? "" : this.getStyle(BOTTOM),
\r
6079 "z-index" : this.getStyle(ZINDEX)
\r
6084 * Set positioning with an object returned by getPositioning().
\r
6085 * @param {Object} posCfg
\r
6086 * @return {Ext.Element} this
\r
6088 setPositioning : function(pc){
\r
6090 style = me.dom.style;
\r
6094 if(pc.right == AUTO){
\r
6097 if(pc.bottom == AUTO){
\r
6098 style.bottom = "";
\r
6105 * Translates the passed page coordinates into left/top css values for this element
\r
6106 * @param {Number/Array} x The page x or an array containing [x, y]
\r
6107 * @param {Number} y (optional) The page y, required if x is not an array
\r
6108 * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
\r
6110 translatePoints : function(x, y){
\r
6111 y = isNaN(x[1]) ? y : x[1];
\r
6112 x = isNaN(x[0]) ? x : x[0];
\r
6114 relative = me.isStyle(POSITION, RELATIVE),
\r
6116 l = parseInt(me.getStyle(LEFT), 10),
\r
6117 t = parseInt(me.getStyle(TOP), 10);
\r
6119 l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
\r
6120 t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);
\r
6122 return {left: (x - o[0] + l), top: (y - o[1] + t)};
\r
6125 animTest : animTest
\r
6128 * @class Ext.Element
\r
6130 Ext.Element.addMethods({
\r
6132 * 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
6133 * @param {Object} box The box to fill {x, y, width, height}
\r
6134 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
\r
6135 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6136 * @return {Ext.Element} this
\r
6138 setBox : function(box, adjust, animate){
\r
6142 if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
\r
6143 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
\r
6144 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
\r
6146 me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
\r
6151 * Return a box {x, y, width, height} that can be used to set another elements
\r
6152 * size/location to match this element.
\r
6153 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
\r
6154 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
\r
6155 * @return {Object} box An object in the format {x, y, width, height}
\r
6157 getBox : function(contentBox, local) {
\r
6162 getBorderWidth = me.getBorderWidth,
\r
6163 getPadding = me.getPadding,
\r
6171 left = parseInt(me.getStyle("left"), 10) || 0;
\r
6172 top = parseInt(me.getStyle("top"), 10) || 0;
\r
6175 var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
\r
6177 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
\r
6179 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
\r
6180 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
\r
6181 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
\r
6182 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
\r
6183 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
6185 bx.right = bx.x + bx.width;
\r
6186 bx.bottom = bx.y + bx.height;
\r
6191 * Move this element relative to its current position.
\r
6192 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
\r
6193 * @param {Number} distance How far to move the element in pixels
\r
6194 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6195 * @return {Ext.Element} this
\r
6197 move : function(direction, distance, animate){
\r
6202 left = [x - distance, y],
\r
6203 right = [x + distance, y],
\r
6204 top = [x, y - distance],
\r
6205 bottom = [x, y + distance],
\r
6219 direction = direction.toLowerCase();
\r
6220 me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
\r
6224 * Quick set left and top adding default units
\r
6225 * @param {String} left The left CSS property value
\r
6226 * @param {String} top The top CSS property value
\r
6227 * @return {Ext.Element} this
\r
6229 setLeftTop : function(left, top){
\r
6231 style = me.dom.style;
\r
6232 style.left = me.addUnits(left);
\r
6233 style.top = me.addUnits(top);
\r
6238 * Returns the region of the given element.
\r
6239 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
\r
6240 * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
\r
6242 getRegion : function(){
\r
6243 return Ext.lib.Dom.getRegion(this.dom);
\r
6247 * 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
6248 * @param {Number} x X value for new position (coordinates are page-based)
\r
6249 * @param {Number} y Y value for new position (coordinates are page-based)
\r
6250 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
\r
6251 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
\r
6252 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
\r
6254 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
\r
6255 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
\r
6256 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
\r
6258 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6259 * @return {Ext.Element} this
\r
6261 setBounds : function(x, y, width, height, animate){
\r
6263 if (!animate || !me.anim) {
\r
6264 me.setSize(width, height);
\r
6265 me.setLocation(x, y);
\r
6267 me.anim({points: {to: [x, y]},
\r
6268 width: {to: me.adjustWidth(width)},
\r
6269 height: {to: me.adjustHeight(height)}},
\r
6270 me.preanim(arguments, 4),
\r
6277 * 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
6278 * @param {Ext.lib.Region} region The region to fill
\r
6279 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6280 * @return {Ext.Element} this
\r
6282 setRegion : function(region, animate) {
\r
6283 return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
\r
6286 * @class Ext.Element
\r
6288 Ext.Element.addMethods({
\r
6290 * Returns true if this element is scrollable.
\r
6291 * @return {Boolean}
\r
6293 isScrollable : function(){
\r
6294 var dom = this.dom;
\r
6295 return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
\r
6299 * 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
6300 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
\r
6301 * @param {Number} value The new scroll value.
\r
6302 * @return {Element} this
\r
6304 scrollTo : function(side, value){
\r
6305 this.dom["scroll" + (/top/i.test(side) ? "Top" : "Left")] = value;
\r
6310 * Returns the current scroll position of the element.
\r
6311 * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
\r
6313 getScroll : function(){
\r
6314 var d = this.dom,
\r
6317 docElement = doc.documentElement,
\r
6322 if(d == doc || d == body){
\r
6323 if(Ext.isIE && Ext.isStrict){
\r
6324 l = docElement.scrollLeft;
\r
6325 t = docElement.scrollTop;
\r
6327 l = window.pageXOffset;
\r
6328 t = window.pageYOffset;
\r
6330 ret = {left: l || (body ? body.scrollLeft : 0), top: t || (body ? body.scrollTop : 0)};
\r
6332 ret = {left: d.scrollLeft, top: d.scrollTop};
\r
6337 * @class Ext.Element
\r
6339 Ext.Element.addMethods({
\r
6341 * 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
6342 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
\r
6343 * @param {Number} value The new scroll value
\r
6344 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6345 * @return {Element} this
\r
6347 scrollTo : function(side, value, animate){
\r
6348 var tester = /top/i,
\r
6349 prop = "scroll" + (tester.test(side) ? "Top" : "Left"),
\r
6352 if (!animate || !me.anim) {
\r
6353 dom[prop] = value;
\r
6355 me.anim({scroll: {to: tester.test(prop) ? [dom[prop], value] : [value, dom[prop]]}},
\r
6356 me.preanim(arguments, 2), 'scroll');
\r
6362 * Scrolls this element into view within the passed container.
\r
6363 * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a
\r
6364 * string (id), dom node, or Ext.Element.
\r
6365 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
\r
6366 * @return {Ext.Element} this
\r
6368 scrollIntoView : function(container, hscroll){
\r
6369 var c = Ext.getDom(container) || Ext.getBody().dom,
\r
6371 o = this.getOffsetsTo(c),
\r
6372 l = o[0] + c.scrollLeft,
\r
6373 t = o[1] + c.scrollTop,
\r
6374 b = t + el.offsetHeight,
\r
6375 r = l + el.offsetWidth,
\r
6376 ch = c.clientHeight,
\r
6377 ct = parseInt(c.scrollTop, 10),
\r
6378 cl = parseInt(c.scrollLeft, 10),
\r
6380 cr = cl + c.clientWidth;
\r
6382 if (el.offsetHeight > ch || t < ct) {
\r
6384 } else if (b > cb){
\r
6385 c.scrollTop = b-ch;
\r
6387 c.scrollTop = c.scrollTop; // corrects IE, other browsers will ignore
\r
6389 if(hscroll !== false){
\r
6390 if(el.offsetWidth > c.clientWidth || l < cl){
\r
6393 c.scrollLeft = r - c.clientWidth;
\r
6395 c.scrollLeft = c.scrollLeft;
\r
6401 scrollChildIntoView : function(child, hscroll){
\r
6402 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
\r
6406 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
\r
6407 * within this element's scrollable range.
\r
6408 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
\r
6409 * @param {Number} distance How far to scroll the element in pixels
\r
6410 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6411 * @return {Boolean} Returns true if a scroll was triggered or false if the element
\r
6412 * was scrolled as far as it could go.
\r
6414 scroll : function(direction, distance, animate){
\r
6415 if(!this.isScrollable()){
\r
6418 var el = this.dom,
\r
6419 l = el.scrollLeft, t = el.scrollTop,
\r
6420 w = el.scrollWidth, h = el.scrollHeight,
\r
6421 cw = el.clientWidth, ch = el.clientHeight,
\r
6422 scrolled = false, v,
\r
6424 l: Math.min(l + distance, w-cw),
\r
6425 r: v = Math.max(l - distance, 0),
\r
6426 t: Math.max(t - distance, 0),
\r
6427 b: Math.min(t + distance, h-ch)
\r
6432 direction = direction.substr(0, 1);
\r
6433 if((v = hash[direction]) > -1){
\r
6435 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
\r
6440 * @class Ext.Element
\r
6443 * Visibility mode constant for use with {@link #setVisibilityMode}. Use visibility to hide element
\r
6447 Ext.Element.VISIBILITY = 1;
\r
6449 * Visibility mode constant for use with {@link #setVisibilityMode}. Use display to hide element
\r
6453 Ext.Element.DISPLAY = 2;
\r
6455 Ext.Element.addMethods(function(){
\r
6456 var VISIBILITY = "visibility",
\r
6457 DISPLAY = "display",
\r
6458 HIDDEN = "hidden",
\r
6460 ORIGINALDISPLAY = 'originalDisplay',
\r
6461 VISMODE = 'visibilityMode',
\r
6462 ELDISPLAY = Ext.Element.DISPLAY,
\r
6463 data = Ext.Element.data,
\r
6464 getDisplay = function(dom){
\r
6465 var d = data(dom, ORIGINALDISPLAY);
\r
6466 if(d === undefined){
\r
6467 data(dom, ORIGINALDISPLAY, d = '');
\r
6471 getVisMode = function(dom){
\r
6472 var m = data(dom, VISMODE);
\r
6473 if(m === undefined){
\r
6474 data(dom, VISMODE, m = 1)
\r
6481 * The element's default display mode (defaults to "")
\r
6484 originalDisplay : "",
\r
6485 visibilityMode : 1,
\r
6488 * Sets the element's visibility mode. When setVisible() is called it
\r
6489 * will use this to determine whether to set the visibility or the display property.
\r
6490 * @param visMode Ext.Element.VISIBILITY or Ext.Element.DISPLAY
\r
6491 * @return {Ext.Element} this
\r
6493 setVisibilityMode : function(visMode){
\r
6494 data(this.dom, VISMODE, visMode);
\r
6499 * Perform custom animation on this element.
\r
6500 * <div><ul class="mdetail-params">
\r
6501 * <li><u>Animation Properties</u></li>
\r
6503 * <p>The Animation Control Object enables gradual transitions for any member of an
\r
6504 * element's style object that takes a numeric value including but not limited to
\r
6505 * these properties:</p><div><ul class="mdetail-params">
\r
6506 * <li><tt>bottom, top, left, right</tt></li>
\r
6507 * <li><tt>height, width</tt></li>
\r
6508 * <li><tt>margin, padding</tt></li>
\r
6509 * <li><tt>borderWidth</tt></li>
\r
6510 * <li><tt>opacity</tt></li>
\r
6511 * <li><tt>fontSize</tt></li>
\r
6512 * <li><tt>lineHeight</tt></li>
\r
6516 * <li><u>Animation Property Attributes</u></li>
\r
6518 * <p>Each Animation Property is a config object with optional properties:</p>
\r
6519 * <div><ul class="mdetail-params">
\r
6520 * <li><tt>by</tt>* : relative change - start at current value, change by this value</li>
\r
6521 * <li><tt>from</tt> : ignore current value, start from this value</li>
\r
6522 * <li><tt>to</tt>* : start at current value, go to this value</li>
\r
6523 * <li><tt>unit</tt> : any allowable unit specification</li>
\r
6524 * <p>* do not specify both <tt>to</tt> and <tt>by</tt> for an animation property</p>
\r
6527 * <li><u>Animation Types</u></li>
\r
6529 * <p>The supported animation types:</p><div><ul class="mdetail-params">
\r
6530 * <li><tt>'run'</tt> : Default
\r
6532 var el = Ext.get('complexEl');
\r
6534 // animation control object
\r
6536 borderWidth: {to: 3, from: 0},
\r
6537 opacity: {to: .3, from: 1},
\r
6538 height: {to: 50, from: el.getHeight()},
\r
6539 width: {to: 300, from: el.getWidth()},
\r
6540 top : {by: - 100, unit: 'px'},
\r
6542 0.35, // animation duration
\r
6544 'easeOut', // easing method
\r
6545 'run' // animation type ('run','color','motion','scroll')
\r
6549 * <li><tt>'color'</tt>
\r
6550 * <p>Animates transition of background, text, or border colors.</p>
\r
6553 // animation control object
\r
6555 color: { to: '#06e' },
\r
6556 backgroundColor: { to: '#e06' }
\r
6558 0.35, // animation duration
\r
6560 'easeOut', // easing method
\r
6561 'color' // animation type ('run','color','motion','scroll')
\r
6566 * <li><tt>'motion'</tt>
\r
6567 * <p>Animates the motion of an element to/from specific points using optional bezier
\r
6568 * way points during transit.</p>
\r
6571 // animation control object
\r
6573 borderWidth: {to: 3, from: 0},
\r
6574 opacity: {to: .3, from: 1},
\r
6575 height: {to: 50, from: el.getHeight()},
\r
6576 width: {to: 300, from: el.getWidth()},
\r
6577 top : {by: - 100, unit: 'px'},
\r
6579 to: [50, 100], // go to this point
\r
6580 control: [ // optional bezier way points
\r
6586 3000, // animation duration (milliseconds!)
\r
6588 'easeOut', // easing method
\r
6589 'motion' // animation type ('run','color','motion','scroll')
\r
6593 * <li><tt>'scroll'</tt>
\r
6594 * <p>Animate horizontal or vertical scrolling of an overflowing page element.</p>
\r
6597 // animation control object
\r
6599 scroll: {to: [400, 300]}
\r
6601 0.35, // animation duration
\r
6603 'easeOut', // easing method
\r
6604 'scroll' // animation type ('run','color','motion','scroll')
\r
6612 * @param {Object} args The animation control args
\r
6613 * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to <tt>.35</tt>)
\r
6614 * @param {Function} onComplete (optional) Function to call when animation completes
\r
6615 * @param {String} easing (optional) {@link Ext.Fx#easing} method to use (defaults to <tt>'easeOut'</tt>)
\r
6616 * @param {String} animType (optional) <tt>'run'</tt> is the default. Can also be <tt>'color'</tt>,
\r
6617 * <tt>'motion'</tt>, or <tt>'scroll'</tt>
\r
6618 * @return {Ext.Element} this
\r
6620 animate : function(args, duration, onComplete, easing, animType){
\r
6621 this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
\r
6626 * @private Internal animation call
\r
6628 anim : function(args, opt, animType, defaultDur, defaultEase, cb){
\r
6629 animType = animType || 'run';
\r
6632 anim = Ext.lib.Anim[animType](
\r
6635 (opt.duration || defaultDur) || .35,
\r
6636 (opt.easing || defaultEase) || 'easeOut',
\r
6638 if(cb) cb.call(me);
\r
6639 if(opt.callback) opt.callback.call(opt.scope || me, me, opt);
\r
6647 // private legacy anim prep
\r
6648 preanim : function(a, i){
\r
6649 return !a[i] ? false : (Ext.isObject(a[i]) ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
\r
6653 * Checks whether the element is currently visible using both visibility and display properties.
\r
6654 * @return {Boolean} True if the element is currently visible, else false
\r
6656 isVisible : function() {
\r
6657 return !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE);
\r
6661 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
\r
6662 * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
\r
6663 * @param {Boolean} visible Whether the element is visible
\r
6664 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
6665 * @return {Ext.Element} this
\r
6667 setVisible : function(visible, animate){
\r
6670 isDisplay = getVisMode(this.dom) == ELDISPLAY;
\r
6672 if (!animate || !me.anim) {
\r
6674 me.setDisplayed(visible);
\r
6677 dom.style.visibility = visible ? "visible" : HIDDEN;
\r
6680 // closure for composites
\r
6682 me.setOpacity(.01);
\r
6683 me.setVisible(true);
\r
6685 me.anim({opacity: { to: (visible?1:0) }},
\r
6686 me.preanim(arguments, 1),
\r
6692 dom.style[isDisplay ? DISPLAY : VISIBILITY] = (isDisplay) ? NONE : HIDDEN;
\r
6693 Ext.fly(dom).setOpacity(1);
\r
6701 * Toggles the element's visibility or display, depending on visibility mode.
\r
6702 * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
\r
6703 * @return {Ext.Element} this
\r
6705 toggle : function(animate){
\r
6707 me.setVisible(!me.isVisible(), me.preanim(arguments, 0));
\r
6712 * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
\r
6713 * @param {Mixed} value Boolean value to display the element using its default display, or a string to set the display directly.
\r
6714 * @return {Ext.Element} this
\r
6716 setDisplayed : function(value) {
\r
6717 if(typeof value == "boolean"){
\r
6718 value = value ? getDisplay(this.dom) : NONE;
\r
6720 this.setStyle(DISPLAY, value);
\r
6725 fixDisplay : function(){
\r
6727 if(me.isStyle(DISPLAY, NONE)){
\r
6728 me.setStyle(VISIBILITY, HIDDEN);
\r
6729 me.setStyle(DISPLAY, getDisplay(this.dom)); // first try reverting to default
\r
6730 if(me.isStyle(DISPLAY, NONE)){ // if that fails, default to block
\r
6731 me.setStyle(DISPLAY, "block");
\r
6737 * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
\r
6738 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6739 * @return {Ext.Element} this
\r
6741 hide : function(animate){
\r
6742 this.setVisible(false, this.preanim(arguments, 0));
\r
6747 * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
\r
6748 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
\r
6749 * @return {Ext.Element} this
\r
6751 show : function(animate){
\r
6752 this.setVisible(true, this.preanim(arguments, 0));
\r
6757 * @class Ext.Element
\r
6759 Ext.Element.addMethods(
\r
6761 var VISIBILITY = "visibility",
\r
6762 DISPLAY = "display",
\r
6763 HIDDEN = "hidden",
\r
6765 XMASKED = "x-masked",
\r
6766 XMASKEDRELATIVE = "x-masked-relative",
\r
6767 data = Ext.Element.data;
\r
6771 * Checks whether the element is currently visible using both visibility and display properties.
\r
6772 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
\r
6773 * @return {Boolean} True if the element is currently visible, else false
\r
6775 isVisible : function(deep) {
\r
6776 var vis = !this.isStyle(VISIBILITY,HIDDEN) && !this.isStyle(DISPLAY,NONE),
\r
6777 p = this.dom.parentNode;
\r
6778 if(deep !== true || !vis){
\r
6781 while(p && !/body/i.test(p.tagName)){
\r
6782 if(!Ext.fly(p, '_isVisible').isVisible()){
\r
6791 * Returns true if display is not "none"
\r
6792 * @return {Boolean}
\r
6794 isDisplayed : function() {
\r
6795 return !this.isStyle(DISPLAY, NONE);
\r
6799 * Convenience method for setVisibilityMode(Element.DISPLAY)
\r
6800 * @param {String} display (optional) What to set display to when visible
\r
6801 * @return {Ext.Element} this
\r
6803 enableDisplayMode : function(display){
\r
6804 this.setVisibilityMode(Ext.Element.DISPLAY);
\r
6805 if(!Ext.isEmpty(display)){
\r
6806 data(this.dom, 'originalDisplay', display);
\r
6812 * Puts a mask over this element to disable user interaction. Requires core.css.
\r
6813 * This method can only be applied to elements which accept child nodes.
\r
6814 * @param {String} msg (optional) A message to display in the mask
\r
6815 * @param {String} msgCls (optional) A css class to apply to the msg element
\r
6816 * @return {Element} The mask element
\r
6818 mask : function(msg, msgCls){
\r
6821 dh = Ext.DomHelper,
\r
6822 EXTELMASKMSG = "ext-el-mask-msg",
\r
6826 if(me.getStyle("position") == "static"){
\r
6827 me.addClass(XMASKEDRELATIVE);
\r
6829 if((el = data(dom, 'maskMsg'))){
\r
6832 if((el = data(dom, 'mask'))){
\r
6836 mask = dh.append(dom, {cls : "ext-el-mask"}, true);
\r
6837 data(dom, 'mask', mask);
\r
6839 me.addClass(XMASKED);
\r
6840 mask.setDisplayed(true);
\r
6841 if(typeof msg == 'string'){
\r
6842 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
\r
6843 data(dom, 'maskMsg', mm);
\r
6844 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
\r
6845 mm.dom.firstChild.innerHTML = msg;
\r
6846 mm.setDisplayed(true);
\r
6849 if(Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto'){ // ie will not expand full height automatically
\r
6850 mask.setSize(undefined, me.getHeight());
\r
6856 * Removes a previously applied mask.
\r
6858 unmask : function(){
\r
6861 mask = data(dom, 'mask'),
\r
6862 maskMsg = data(dom, 'maskMsg');
\r
6866 data(dom, 'maskMsg', undefined);
\r
6869 data(dom, 'mask', undefined);
\r
6871 me.removeClass([XMASKED, XMASKEDRELATIVE]);
\r
6875 * Returns true if this element is masked
\r
6876 * @return {Boolean}
\r
6878 isMasked : function(){
\r
6879 var m = data(this.dom, 'mask');
\r
6880 return m && m.isVisible();
\r
6884 * Creates an iframe shim for this element to keep selects and other windowed objects from
\r
6885 * showing through.
\r
6886 * @return {Ext.Element} The new shim element
\r
6888 createShim : function(){
\r
6889 var el = document.createElement('iframe'),
\r
6891 el.frameBorder = '0';
\r
6892 el.className = 'ext-shim';
\r
6893 if(Ext.isIE && Ext.isSecure){
\r
6894 el.src = Ext.SSL_SECURE_URL;
\r
6896 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
\r
6897 shim.autoBoxAdjust = false;
\r
6902 * @class Ext.Element
\r
6904 Ext.Element.addMethods({
\r
6906 * Convenience method for constructing a KeyMap
\r
6907 * @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
6908 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
\r
6909 * @param {Function} fn The function to call
\r
6910 * @param {Object} scope (optional) The scope of the function
\r
6911 * @return {Ext.KeyMap} The KeyMap created
\r
6913 addKeyListener : function(key, fn, scope){
\r
6915 if(!Ext.isObject(key) || Ext.isArray(key)){
\r
6924 shift : key.shift,
\r
6931 return new Ext.KeyMap(this, config);
\r
6935 * Creates a KeyMap for this element
\r
6936 * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
\r
6937 * @return {Ext.KeyMap} The KeyMap created
\r
6939 addKeyMap : function(config){
\r
6940 return new Ext.KeyMap(this, config);
\r
6945 UNDEFINED = undefined,
\r
6952 BOTTOM = "bottom",
\r
6955 HEIGHT = "height",
\r
6957 POINTS = "points",
\r
6958 HIDDEN = "hidden",
\r
6959 ABSOLUTE = "absolute",
\r
6960 VISIBLE = "visible",
\r
6961 MOTION = "motion",
\r
6962 POSITION = "position",
\r
6963 EASEOUT = "easeOut",
\r
6965 * Use a light flyweight here since we are using so many callbacks and are always assured a DOM element
\r
6967 flyEl = new Ext.Element.Flyweight(),
\r
6969 getObject = function(o){
\r
6972 fly = function(dom){
\r
6974 flyEl.id = Ext.id(dom);
\r
6978 * Queueing now stored outside of the element due to closure issues
\r
6980 getQueue = function(id){
\r
6984 return queues[id];
\r
6986 setQueue = function(id, value){
\r
6987 queues[id] = value;
\r
6990 //Notifies Element that fx methods are available
\r
6991 Ext.enableFx = TRUE;
\r
6995 * <p>A class to provide basic animation and visual effects support. <b>Note:</b> This class is automatically applied
\r
6996 * to the {@link Ext.Element} interface when included, so all effects calls should be performed via {@link Ext.Element}.
\r
6997 * Conversely, since the effects are not actually defined in {@link Ext.Element}, Ext.Fx <b>must</b> be
\r
6998 * {@link Ext#enableFx included} in order for the Element effects to work.</p><br/>
\r
7000 * <p><b><u>Method Chaining</u></b></p>
\r
7001 * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
\r
7002 * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
\r
7003 * method chain. The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
\r
7004 * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately. For this reason,
\r
7005 * 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
7006 * expected results and should be done with care. Also see <tt>{@link #callback}</tt>.</p><br/>
\r
7008 * <p><b><u>Anchor Options for Motion Effects</u></b></p>
\r
7009 * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
\r
7010 * that will serve as either the start or end point of the animation. Following are all of the supported anchor positions:</p>
\r
7013 ----- -----------------------------
\r
7014 tl The top left corner
\r
7015 t The center of the top edge
\r
7016 tr The top right corner
\r
7017 l The center of the left edge
\r
7018 r The center of the right edge
\r
7019 bl The bottom left corner
\r
7020 b The center of the bottom edge
\r
7021 br The bottom right corner
\r
7023 * <b>Note</b>: some Fx methods accept specific custom config parameters. The options shown in the Config Options
\r
7024 * section below are common options that can be passed to any Fx method unless otherwise noted.</b>
\r
7026 * @cfg {Function} callback A function called when the effect is finished. Note that effects are queued internally by the
\r
7027 * Fx class, so a callback is not required to specify another effect -- effects can simply be chained together
\r
7028 * and called in sequence (see note for <b><u>Method Chaining</u></b> above), for example:<pre><code>
\r
7029 * el.slideIn().highlight();
\r
7031 * The callback is intended for any additional code that should run once a particular effect has completed. The Element
\r
7032 * being operated upon is passed as the first parameter.
\r
7034 * @cfg {Object} scope The scope of the <tt>{@link #callback}</tt> function
\r
7036 * @cfg {String} easing A valid Ext.lib.Easing value for the effect:</p><div class="mdetail-params"><ul>
\r
7037 * <li><b><tt>backBoth</tt></b></li>
\r
7038 * <li><b><tt>backIn</tt></b></li>
\r
7039 * <li><b><tt>backOut</tt></b></li>
\r
7040 * <li><b><tt>bounceBoth</tt></b></li>
\r
7041 * <li><b><tt>bounceIn</tt></b></li>
\r
7042 * <li><b><tt>bounceOut</tt></b></li>
\r
7043 * <li><b><tt>easeBoth</tt></b></li>
\r
7044 * <li><b><tt>easeBothStrong</tt></b></li>
\r
7045 * <li><b><tt>easeIn</tt></b></li>
\r
7046 * <li><b><tt>easeInStrong</tt></b></li>
\r
7047 * <li><b><tt>easeNone</tt></b></li>
\r
7048 * <li><b><tt>easeOut</tt></b></li>
\r
7049 * <li><b><tt>easeOutStrong</tt></b></li>
\r
7050 * <li><b><tt>elasticBoth</tt></b></li>
\r
7051 * <li><b><tt>elasticIn</tt></b></li>
\r
7052 * <li><b><tt>elasticOut</tt></b></li>
\r
7055 * @cfg {String} afterCls A css class to apply after the effect
\r
7056 * @cfg {Number} duration The length of time (in seconds) that the effect should last
\r
7058 * @cfg {Number} endOpacity Only applicable for {@link #fadeIn} or {@link #fadeOut}, a number between
\r
7059 * <tt>0</tt> and <tt>1</tt> inclusive to configure the ending opacity value.
\r
7061 * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
\r
7062 * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to
\r
7063 * effects that end with the element being visually hidden, ignored otherwise)
\r
7064 * @cfg {String/Object/Function} afterStyle A style specification string, e.g. <tt>"width:100px"</tt>, or an object
\r
7065 * in the form <tt>{width:"100px"}</tt>, or a function which returns such a specification that will be applied to the
\r
7066 * Element after the effect finishes.
\r
7067 * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
\r
7068 * @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
7069 * @cfg {Boolean} stopFx Whether preceding effects should be stopped and removed before running current effect (only applies to non blocking effects)
\r
7073 // private - calls the function taking arguments from the argHash based on the key. Returns the return value of the function.
\r
7074 // this is useful for replacing switch statements (for example).
\r
7075 switchStatements : function(key, fn, argHash){
\r
7076 return fn.apply(this, argHash[key]);
\r
7080 * Slides the element into view. An anchor point can be optionally passed to set the point of
\r
7081 * origin for the slide effect. This function automatically handles wrapping the element with
\r
7082 * a fixed-size container if needed. See the Fx class overview for valid anchor point options.
\r
7085 // default: slide the element in from the top
\r
7088 // custom: slide the element in from the right with a 2-second duration
\r
7089 el.slideIn('r', { duration: 2 });
\r
7091 // common config options shown with default values
\r
7093 easing: 'easeOut',
\r
7097 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
\r
7098 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7099 * @return {Ext.Element} The Element
\r
7101 slideIn : function(anchor, o){
\r
7117 anchor = anchor || "t";
\r
7119 me.queueFx(o, function(){
\r
7120 xy = fly(dom).getXY();
\r
7121 // fix display to visibility
\r
7122 fly(dom).fixDisplay();
\r
7124 // restore values after effect
\r
7125 r = fly(dom).getFxRestore();
\r
7126 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
\r
7127 b.right = b.x + b.width;
\r
7128 b.bottom = b.y + b.height;
\r
7130 // fixed size for slide
\r
7131 fly(dom).setWidth(b.width).setHeight(b.height);
\r
7134 wrap = fly(dom).fxWrap(r.pos, o, HIDDEN);
\r
7136 st.visibility = VISIBLE;
\r
7137 st.position = ABSOLUTE;
\r
7139 // clear out temp styles after slide and unwrap
\r
7141 fly(dom).fxUnwrap(wrap, r.pos, o);
\r
7142 st.width = r.width;
\r
7143 st.height = r.height;
\r
7144 fly(dom).afterFx(o);
\r
7147 // time to calculate the positions
\r
7148 pt = {to: [b.x, b.y]};
\r
7149 bw = {to: b.width};
\r
7150 bh = {to: b.height};
\r
7152 function argCalc(wrap, style, ww, wh, sXY, sXYval, s1, s2, w, h, p){
\r
7154 fly(wrap).setWidth(ww).setHeight(wh);
\r
7155 if(fly(wrap)[sXY]){
\r
7156 fly(wrap)[sXY](sXYval);
\r
7158 style[s1] = style[s2] = "0";
\r
7171 args = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
\r
7172 t : [wrap, st, b.width, 0, NULL, NULL, LEFT, BOTTOM, NULL, bh, NULL],
\r
7173 l : [wrap, st, 0, b.height, NULL, NULL, RIGHT, TOP, bw, NULL, NULL],
\r
7174 r : [wrap, st, b.width, b.height, SETX, b.right, LEFT, TOP, NULL, NULL, pt],
\r
7175 b : [wrap, st, b.width, b.height, SETY, b.bottom, LEFT, TOP, NULL, bh, pt],
\r
7176 tl : [wrap, st, 0, 0, NULL, NULL, RIGHT, BOTTOM, bw, bh, pt],
\r
7177 bl : [wrap, st, 0, 0, SETY, b.y + b.height, RIGHT, TOP, bw, bh, pt],
\r
7178 br : [wrap, st, 0, 0, SETXY, [b.right, b.bottom], LEFT, TOP, bw, bh, pt],
\r
7179 tr : [wrap, st, 0, 0, SETX, b.x + b.width, LEFT, BOTTOM, bw, bh, pt]
\r
7182 st.visibility = VISIBLE;
\r
7185 arguments.callee.anim = fly(wrap).fxanim(args,
\r
7196 * Slides the element out of view. An anchor point can be optionally passed to set the end point
\r
7197 * for the slide effect. When the effect is completed, the element will be hidden (visibility =
\r
7198 * 'hidden') but block elements will still take up space in the document. The element must be removed
\r
7199 * from the DOM using the 'remove' config option if desired. This function automatically handles
\r
7200 * wrapping the element with a fixed-size container if needed. See the Fx class overview for valid anchor point options.
\r
7203 // default: slide the element out to the top
\r
7206 // custom: slide the element out to the right with a 2-second duration
\r
7207 el.slideOut('r', { duration: 2 });
\r
7209 // common config options shown with default values
\r
7210 el.slideOut('t', {
\r
7211 easing: 'easeOut',
\r
7217 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
\r
7218 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7219 * @return {Ext.Element} The Element
\r
7221 slideOut : function(anchor, o){
\r
7233 anchor = anchor || "t";
\r
7235 me.queueFx(o, function(){
\r
7237 // restore values after effect
\r
7238 r = fly(dom).getFxRestore();
\r
7239 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight};
\r
7240 b.right = b.x + b.width;
\r
7241 b.bottom = b.y + b.height;
\r
7243 // fixed size for slide
\r
7244 fly(dom).setWidth(b.width).setHeight(b.height);
\r
7247 wrap = fly(dom).fxWrap(r.pos, o, VISIBLE);
\r
7249 st.visibility = VISIBLE;
\r
7250 st.position = ABSOLUTE;
\r
7251 fly(wrap).setWidth(b.width).setHeight(b.height);
\r
7254 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
\r
7255 fly(dom).fxUnwrap(wrap, r.pos, o);
\r
7256 st.width = r.width;
\r
7257 st.height = r.height;
\r
7258 fly(dom).afterFx(o);
\r
7261 function argCalc(style, s1, s2, p1, v1, p2, v2, p3, v3){
\r
7264 style[s1] = style[s2] = "0";
\r
7276 a = fly(dom).switchStatements(anchor.toLowerCase(), argCalc, {
\r
7277 t : [st, LEFT, BOTTOM, HEIGHT, zero],
\r
7278 l : [st, RIGHT, TOP, WIDTH, zero],
\r
7279 r : [st, LEFT, TOP, WIDTH, zero, POINTS, {to : [b.right, b.y]}],
\r
7280 b : [st, LEFT, TOP, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
\r
7281 tl : [st, RIGHT, BOTTOM, WIDTH, zero, HEIGHT, zero],
\r
7282 bl : [st, RIGHT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x, b.bottom]}],
\r
7283 br : [st, LEFT, TOP, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.x + b.width, b.bottom]}],
\r
7284 tr : [st, LEFT, BOTTOM, WIDTH, zero, HEIGHT, zero, POINTS, {to : [b.right, b.y]}]
\r
7287 arguments.callee.anim = fly(wrap).fxanim(a,
\r
7298 * Fades the element out while slowly expanding it in all directions. When the effect is completed, the
\r
7299 * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document.
\r
7300 * The element must be removed from the DOM using the 'remove' config option if desired.
\r
7306 // common config options shown with default values
\r
7308 easing: 'easeOut',
\r
7314 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7315 * @return {Ext.Element} The Element
\r
7317 puff : function(o){
\r
7326 me.queueFx(o, function(){
\r
7327 width = fly(dom).getWidth();
\r
7328 height = fly(dom).getHeight();
\r
7329 fly(dom).clearOpacity();
\r
7332 // restore values after effect
\r
7333 r = fly(dom).getFxRestore();
\r
7336 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
\r
7337 fly(dom).clearOpacity();
\r
7338 fly(dom).setPositioning(r.pos);
\r
7339 st.width = r.width;
\r
7340 st.height = r.height;
\r
7342 fly(dom).afterFx(o);
\r
7345 arguments.callee.anim = fly(dom).fxanim({
\r
7346 width : {to : fly(dom).adjustWidth(width * 2)},
\r
7347 height : {to : fly(dom).adjustHeight(height * 2)},
\r
7348 points : {by : [-width * .5, -height * .5]},
\r
7349 opacity : {to : 0},
\r
7350 fontSize: {to : 200, unit: "%"}
\r
7362 * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
\r
7363 * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still
\r
7364 * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
\r
7370 // all config options shown with default values
\r
7378 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7379 * @return {Ext.Element} The Element
\r
7381 switchOff : function(o){
\r
7388 me.queueFx(o, function(){
\r
7389 fly(dom).clearOpacity();
\r
7392 // restore values after effect
\r
7393 r = fly(dom).getFxRestore();
\r
7396 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
\r
7397 fly(dom).clearOpacity();
\r
7398 fly(dom).setPositioning(r.pos);
\r
7399 st.width = r.width;
\r
7400 st.height = r.height;
\r
7401 fly(dom).afterFx(o);
\r
7404 fly(dom).fxanim({opacity : {to : 0.3}},
\r
7410 fly(dom).clearOpacity();
\r
7413 height : {to : 1},
\r
7414 points : {by : [0, fly(dom).getHeight() * .5]}
\r
7428 * Highlights the Element by setting a color (applies to the background-color by default, but can be
\r
7429 * changed using the "attr" config option) and then fading back to the original color. If no original
\r
7430 * color is available, you should provide the "endColor" config option which will be cleared after the animation.
\r
7433 // default: highlight background to yellow
\r
7436 // custom: highlight foreground text to blue for 2 seconds
\r
7437 el.highlight("0000ff", { attr: 'color', duration: 2 });
\r
7439 // common config options shown with default values
\r
7440 el.highlight("ffff9c", {
\r
7441 attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
\r
7442 endColor: (current color) or "ffffff",
\r
7447 * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
\r
7448 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7449 * @return {Ext.Element} The Element
\r
7451 highlight : function(color, o){
\r
7455 attr = o.attr || "backgroundColor",
\r
7459 me.queueFx(o, function(){
\r
7460 fly(dom).clearOpacity();
\r
7464 dom.style[attr] = restore;
\r
7465 fly(dom).afterFx(o);
\r
7467 restore = dom.style[attr];
\r
7468 a[attr] = {from: color || "ffff9c", to: o.endColor || fly(dom).getColor(attr) || "ffffff"};
\r
7469 arguments.callee.anim = fly(dom).fxanim(a,
\r
7480 * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
\r
7483 // default: a single light blue ripple
\r
7486 // custom: 3 red ripples lasting 3 seconds total
\r
7487 el.frame("ff0000", 3, { duration: 3 });
\r
7489 // common config options shown with default values
\r
7490 el.frame("C3DAF9", 1, {
\r
7491 duration: 1 //duration of each individual ripple.
\r
7492 // Note: Easing is not configurable and will be ignored if included
\r
7495 * @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
7496 * @param {Number} count (optional) The number of ripples to display (defaults to 1)
\r
7497 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7498 * @return {Ext.Element} The Element
\r
7500 frame : function(color, count, o){
\r
7507 me.queueFx(o, function(){
\r
7508 color = color || "#C3DAF9"
\r
7509 if(color.length == 6){
\r
7510 color = "#" + color;
\r
7512 count = count || 1;
\r
7515 var xy = fly(dom).getXY(),
\r
7516 b = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: dom.offsetWidth, height: dom.offsetHeight},
\r
7517 queue = function(){
\r
7518 proxy = fly(document.body || document.documentElement).createChild({
\r
7520 visbility: HIDDEN,
\r
7521 position : ABSOLUTE,
\r
7522 "z-index": 35000, // yee haw
\r
7523 border : "0px solid " + color
\r
7526 return proxy.queueFx({}, animFn);
\r
7530 arguments.callee.anim = {
\r
7532 stop: function() {
\r
7538 function animFn(){
\r
7539 var scale = Ext.isBorderBox ? 2 : 1;
\r
7540 active = proxy.anim({
\r
7541 top : {from : b.y, to : b.y - 20},
\r
7542 left : {from : b.x, to : b.x - 20},
\r
7543 borderWidth : {from : 0, to : 10},
\r
7544 opacity : {from : 1, to : 0},
\r
7545 height : {from : b.height, to : b.height + 20 * scale},
\r
7546 width : {from : b.width, to : b.width + 20 * scale}
\r
7548 duration: o.duration || 1,
\r
7549 callback: function() {
\r
7551 --count > 0 ? queue() : fly(dom).afterFx(o);
\r
7554 arguments.callee.anim = {
\r
7567 * Creates a pause before any subsequent queued effects begin. If there are
\r
7568 * no effects queued after the pause it will have no effect.
\r
7573 * @param {Number} seconds The length of time to pause (in seconds)
\r
7574 * @return {Ext.Element} The Element
\r
7576 pause : function(seconds){
\r
7577 var dom = this.dom,
\r
7580 this.queueFx({}, function(){
\r
7581 t = setTimeout(function(){
\r
7582 fly(dom).afterFx({});
\r
7583 }, seconds * 1000);
\r
7584 arguments.callee.anim = {
\r
7588 fly(dom).afterFx({});
\r
7596 * Fade an element in (from transparent to opaque). The ending opacity can be specified
\r
7597 * using the <tt>{@link #endOpacity}</tt> config option.
\r
7600 // default: fade in from opacity 0 to 100%
\r
7603 // custom: fade in from opacity 0 to 75% over 2 seconds
\r
7604 el.fadeIn({ endOpacity: .75, duration: 2});
\r
7606 // common config options shown with default values
\r
7608 endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
\r
7609 easing: 'easeOut',
\r
7613 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7614 * @return {Ext.Element} The Element
\r
7616 fadeIn : function(o){
\r
7620 to = o.endOpacity || 1;
\r
7622 me.queueFx(o, function(){
\r
7623 fly(dom).setOpacity(0);
\r
7624 fly(dom).fixDisplay();
\r
7625 dom.style.visibility = VISIBLE;
\r
7626 arguments.callee.anim = fly(dom).fxanim({opacity:{to:to}},
\r
7627 o, NULL, .5, EASEOUT, function(){
\r
7629 fly(dom).clearOpacity();
\r
7631 fly(dom).afterFx(o);
\r
7638 * Fade an element out (from opaque to transparent). The ending opacity can be specified
\r
7639 * using the <tt>{@link #endOpacity}</tt> config option. Note that IE may require
\r
7640 * <tt>{@link #useDisplay}:true</tt> in order to redisplay correctly.
\r
7643 // default: fade out from the element's current opacity to 0
\r
7646 // custom: fade out from the element's current opacity to 25% over 2 seconds
\r
7647 el.fadeOut({ endOpacity: .25, duration: 2});
\r
7649 // common config options shown with default values
\r
7651 endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
\r
7652 easing: 'easeOut',
\r
7658 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7659 * @return {Ext.Element} The Element
\r
7661 fadeOut : function(o){
\r
7665 style = dom.style,
\r
7666 to = o.endOpacity || 0;
\r
7668 me.queueFx(o, function(){
\r
7669 arguments.callee.anim = fly(dom).fxanim({
\r
7670 opacity : {to : to}},
\r
7677 Ext.Element.data(dom, 'visibilityMode') == Ext.Element.DISPLAY || o.useDisplay ?
\r
7678 style.display = "none" :
\r
7679 style.visibility = HIDDEN;
\r
7681 fly(dom).clearOpacity();
\r
7683 fly(dom).afterFx(o);
\r
7690 * Animates the transition of an element's dimensions from a starting height/width
\r
7691 * to an ending height/width. This method is a convenience implementation of {@link shift}.
\r
7694 // change height and width to 100x100 pixels
\r
7695 el.scale(100, 100);
\r
7697 // common config options shown with default values. The height and width will default to
\r
7698 // the element's existing values if passed as null.
\r
7700 [element's width],
\r
7701 [element's height], {
\r
7702 easing: 'easeOut',
\r
7707 * @param {Number} width The new width (pass undefined to keep the original width)
\r
7708 * @param {Number} height The new height (pass undefined to keep the original height)
\r
7709 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7710 * @return {Ext.Element} The Element
\r
7712 scale : function(w, h, o){
\r
7713 this.shift(Ext.apply({}, o, {
\r
7721 * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
\r
7722 * Any of these properties not specified in the config object will not be changed. This effect
\r
7723 * requires that at least one new dimension, position or opacity setting must be passed in on
\r
7724 * the config object in order for the function to have any effect.
\r
7727 // slide the element horizontally to x position 200 while changing the height and opacity
\r
7728 el.shift({ x: 200, height: 50, opacity: .8 });
\r
7730 // common config options shown with default values.
\r
7732 width: [element's width],
\r
7733 height: [element's height],
\r
7734 x: [element's x position],
\r
7735 y: [element's y position],
\r
7736 opacity: [element's opacity],
\r
7737 easing: 'easeOut',
\r
7741 * @param {Object} options Object literal with any of the Fx config options
\r
7742 * @return {Ext.Element} The Element
\r
7744 shift : function(o){
\r
7746 var dom = this.dom,
\r
7749 this.queueFx(o, function(){
\r
7750 for (var prop in o) {
\r
7751 if (o[prop] != UNDEFINED) {
\r
7752 a[prop] = {to : o[prop]};
\r
7756 a.width ? a.width.to = fly(dom).adjustWidth(o.width) : a;
\r
7757 a.height ? a.height.to = fly(dom).adjustWidth(o.height) : a;
\r
7759 if (a.x || a.y || a.xy) {
\r
7760 a.points = a.xy ||
\r
7761 {to : [ a.x ? a.x.to : fly(dom).getX(),
\r
7762 a.y ? a.y.to : fly(dom).getY()]};
\r
7765 arguments.callee.anim = fly(dom).fxanim(a,
\r
7771 fly(dom).afterFx(o);
\r
7778 * Slides the element while fading it out of view. An anchor point can be optionally passed to set the
\r
7779 * ending point of the effect.
\r
7782 // default: slide the element downward while fading out
\r
7785 // custom: slide the element out to the right with a 2-second duration
\r
7786 el.ghost('r', { duration: 2 });
\r
7788 // common config options shown with default values
\r
7790 easing: 'easeOut',
\r
7796 * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
\r
7797 * @param {Object} options (optional) Object literal with any of the Fx config options
\r
7798 * @return {Ext.Element} The Element
\r
7800 ghost : function(anchor, o){
\r
7805 a = {opacity: {to: 0}, points: {}},
\r
7811 anchor = anchor || "b";
\r
7813 me.queueFx(o, function(){
\r
7814 // restore values after effect
\r
7815 r = fly(dom).getFxRestore();
\r
7816 w = fly(dom).getWidth();
\r
7817 h = fly(dom).getHeight();
\r
7820 o.useDisplay ? fly(dom).setDisplayed(FALSE) : fly(dom).hide();
\r
7821 fly(dom).clearOpacity();
\r
7822 fly(dom).setPositioning(r.pos);
\r
7823 st.width = r.width;
\r
7824 st.height = r.height;
\r
7825 fly(dom).afterFx(o);
\r
7828 pt.by = fly(dom).switchStatements(anchor.toLowerCase(), function(v1,v2){ return [v1, v2];}, {
\r
7839 arguments.callee.anim = fly(dom).fxanim(a,
\r
7849 * Ensures that all effects queued after syncFx is called on the element are
\r
7850 * run concurrently. This is the opposite of {@link #sequenceFx}.
\r
7851 * @return {Ext.Element} The Element
\r
7853 syncFx : function(){
\r
7855 me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
\r
7857 concurrent : TRUE,
\r
7864 * Ensures that all effects queued after sequenceFx is called on the element are
\r
7865 * run in sequence. This is the opposite of {@link #syncFx}.
\r
7866 * @return {Ext.Element} The Element
\r
7868 sequenceFx : function(){
\r
7870 me.fxDefaults = Ext.apply(me.fxDefaults || {}, {
\r
7872 concurrent : FALSE,
\r
7879 nextFx : function(){
\r
7880 var ef = getQueue(this.dom.id)[0];
\r
7887 * Returns true if the element has any effects actively running or queued, else returns false.
\r
7888 * @return {Boolean} True if element has active effects, else false
\r
7890 hasActiveFx : function(){
\r
7891 return getQueue(this.dom.id)[0];
\r
7895 * Stops any running effects and clears the element's internal effects queue if it contains
\r
7896 * any additional effects that haven't started yet.
\r
7897 * @return {Ext.Element} The Element
\r
7899 stopFx : function(finish){
\r
7902 if(me.hasActiveFx()){
\r
7903 var cur = getQueue(id)[0];
\r
7904 if(cur && cur.anim){
\r
7905 if(cur.anim.isAnimated){
\r
7906 setQueue(id, [cur]); //clear
\r
7907 cur.anim.stop(finish !== undefined ? finish : TRUE);
\r
7917 beforeFx : function(o){
\r
7918 if(this.hasActiveFx() && !o.concurrent){
\r
7929 * Returns true if the element is currently blocking so that no other effect can be queued
\r
7930 * until this effect is finished, else returns false if blocking is not set. This is commonly
\r
7931 * used to ensure that an effect initiated by a user action runs to completion prior to the
\r
7932 * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
\r
7933 * @return {Boolean} True if blocking, else false
\r
7935 hasFxBlock : function(){
\r
7936 var q = getQueue(this.dom.id);
\r
7937 return q && q[0] && q[0].block;
\r
7941 queueFx : function(o, fn){
\r
7943 if(!me.hasFxBlock()){
\r
7944 Ext.applyIf(o, me.fxDefaults);
\r
7945 if(!o.concurrent){
\r
7946 var run = me.beforeFx(o);
\r
7947 fn.block = o.block;
\r
7948 getQueue(me.dom.id).push(fn);
\r
7960 fxWrap : function(pos, o, vis){
\r
7961 var dom = this.dom,
\r
7964 if(!o.wrap || !(wrap = Ext.getDom(o.wrap))){
\r
7965 if(o.fixPosition){
\r
7966 wrapXY = fly(dom).getXY();
\r
7968 var div = document.createElement("div");
\r
7969 div.style.visibility = vis;
\r
7970 wrap = dom.parentNode.insertBefore(div, dom);
\r
7971 fly(wrap).setPositioning(pos);
\r
7972 if(fly(wrap).isStyle(POSITION, "static")){
\r
7973 fly(wrap).position("relative");
\r
7975 fly(dom).clearPositioning('auto');
\r
7977 wrap.appendChild(dom);
\r
7979 fly(wrap).setXY(wrapXY);
\r
7986 fxUnwrap : function(wrap, pos, o){
\r
7987 var dom = this.dom;
\r
7988 fly(dom).clearPositioning();
\r
7989 fly(dom).setPositioning(pos);
\r
7991 wrap.parentNode.insertBefore(dom, wrap);
\r
7992 fly(wrap).remove();
\r
7997 getFxRestore : function(){
\r
7998 var st = this.dom.style;
\r
7999 return {pos: this.getPositioning(), width: st.width, height : st.height};
\r
8003 afterFx : function(o){
\r
8004 var dom = this.dom,
\r
8006 notConcurrent = !o.concurrent;
\r
8008 fly(dom).setStyle(o.afterStyle);
\r
8011 fly(dom).addClass(o.afterCls);
\r
8013 if(o.remove == TRUE){
\r
8014 fly(dom).remove();
\r
8016 if(notConcurrent){
\r
8017 getQueue(id).shift();
\r
8020 o.callback.call(o.scope, fly(dom));
\r
8022 if(notConcurrent){
\r
8023 fly(dom).nextFx();
\r
8028 fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
\r
8029 animType = animType || 'run';
\r
8031 var anim = Ext.lib.Anim[animType](
\r
8034 (opt.duration || defaultDur) || .35,
\r
8035 (opt.easing || defaultEase) || EASEOUT,
\r
8044 // backwards compat
\r
8045 Ext.Fx.resize = Ext.Fx.scale;
\r
8047 //When included, Ext.Fx is automatically applied to Element so that all basic
\r
8048 //effects are available directly via the Element API
\r
8049 Ext.Element.addMethods(Ext.Fx);
\r
8051 * @class Ext.CompositeElementLite
\r
8052 * Flyweight composite class. Reuses the same Ext.Element for element operations.
\r
8054 var els = Ext.select("#some-el div.some-class");
\r
8055 // or select directly from an existing element
\r
8056 var el = Ext.get('some-el');
\r
8057 el.select('div.some-class');
\r
8059 els.setWidth(100); // all elements become 100 width
\r
8060 els.hide(true); // all elements fade out and hide
\r
8062 els.setWidth(100).hide(true);
\r
8063 </code></pre><br><br>
\r
8064 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Ext.Element. All Ext.Element
\r
8065 * actions will be performed on all the elements in this collection.</b>
\r
8067 Ext.CompositeElementLite = function(els, root){
\r
8068 this.elements = [];
\r
8069 this.add(els, root);
\r
8070 this.el = new Ext.Element.Flyweight();
\r
8073 Ext.CompositeElementLite.prototype = {
\r
8074 isComposite: true,
\r
8076 * Returns the number of elements in this composite
\r
8079 getCount : function(){
\r
8080 return this.elements.length;
\r
8082 add : function(els){
\r
8084 if (Ext.isArray(els)) {
\r
8085 this.elements = this.elements.concat(els);
\r
8087 var yels = this.elements;
\r
8088 Ext.each(els, function(e) {
\r
8095 invoke : function(fn, args){
\r
8096 var els = this.elements,
\r
8098 Ext.each(els, function(e) {
\r
8100 Ext.Element.prototype[fn].apply(el, args);
\r
8105 * Returns a flyweight Element of the dom element object at the specified index
\r
8106 * @param {Number} index
\r
8107 * @return {Ext.Element}
\r
8109 item : function(index){
\r
8111 if(!me.elements[index]){
\r
8114 me.el.dom = me.elements[index];
\r
8118 // fixes scope with flyweight
\r
8119 addListener : function(eventName, handler, scope, opt){
\r
8120 Ext.each(this.elements, function(e) {
\r
8121 Ext.EventManager.on(e, eventName, handler, scope || e, opt);
\r
8126 * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
\r
8127 * passed is the flyweight (shared) Ext.Element instance, so if you require a
\r
8128 * a reference to the dom node, use el.dom.</b>
\r
8129 * @param {Function} fn The function to call
\r
8130 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
\r
8131 * @return {CompositeElement} this
\r
8133 each : function(fn, scope){
\r
8137 Ext.each(me.elements, function(e,i) {
\r
8139 return fn.call(scope || el, el, me, i);
\r
8145 * Find the index of the passed element within the composite collection.
\r
8146 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
\r
8147 * @return Number The index of the passed Ext.Element in the composite collection, or -1 if not found.
\r
8149 indexOf : function(el){
\r
8150 return this.elements.indexOf(Ext.getDom(el));
\r
8154 * Replaces the specified element with the passed element.
\r
8155 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
\r
8157 * @param {Mixed} replacement The id of an element or the Element itself.
\r
8158 * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
\r
8159 * @return {CompositeElement} this
\r
8161 replaceElement : function(el, replacement, domReplace){
\r
8162 var index = !isNaN(el) ? el : this.indexOf(el),
\r
8165 replacement = Ext.getDom(replacement);
\r
8167 d = this.elements[index];
\r
8168 d.parentNode.insertBefore(replacement, d);
\r
8169 Ext.removeNode(d);
\r
8171 this.elements.splice(index, 1, replacement);
\r
8177 * Removes all elements.
\r
8179 clear : function(){
\r
8180 this.elements = [];
\r
8184 Ext.CompositeElementLite.prototype.on = Ext.CompositeElementLite.prototype.addListener;
\r
8188 ElProto = Ext.Element.prototype,
\r
8189 CelProto = Ext.CompositeElementLite.prototype;
\r
8191 for(fnName in ElProto){
\r
8192 if(Ext.isFunction(ElProto[fnName])){
\r
8193 (function(fnName){
\r
8194 CelProto[fnName] = CelProto[fnName] || function(){
\r
8195 return this.invoke(fnName, arguments);
\r
8197 }).call(CelProto, fnName);
\r
8204 Ext.Element.selectorFunction = Ext.DomQuery.select;
\r
8208 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
\r
8209 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
\r
8210 * {@link Ext.CompositeElementLite CompositeElementLite} object.
\r
8211 * @param {String/Array} selector The CSS selector or an array of elements
\r
8212 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object) <b>Not supported in core</b>
\r
8213 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
\r
8214 * @return {CompositeElementLite/CompositeElement}
\r
8215 * @member Ext.Element
\r
8218 Ext.Element.select = function(selector, unique, root){
\r
8220 if(typeof selector == "string"){
\r
8221 els = Ext.Element.selectorFunction(selector, root);
\r
8222 }else if(selector.length !== undefined){
\r
8225 throw "Invalid selector";
\r
8227 return new Ext.CompositeElementLite(els);
\r
8230 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
\r
8231 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
\r
8232 * {@link Ext.CompositeElementLite CompositeElementLite} object.
\r
8233 * @param {String/Array} selector The CSS selector or an array of elements
\r
8234 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
\r
8235 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
\r
8236 * @return {CompositeElementLite/CompositeElement}
\r
8240 Ext.select = Ext.Element.select;/**
\r
8241 * @class Ext.CompositeElementLite
\r
8243 Ext.apply(Ext.CompositeElementLite.prototype, {
\r
8244 addElements : function(els, root){
\r
8248 if(typeof els == "string"){
\r
8249 els = Ext.Element.selectorFunction(els, root);
\r
8251 var yels = this.elements;
\r
8252 Ext.each(els, function(e) {
\r
8253 yels.push(Ext.get(e));
\r
8259 * Clears this composite and adds the elements returned by the passed selector.
\r
8260 * @param {String/Array} els A string CSS selector, an array of elements or an element
\r
8261 * @return {CompositeElement} this
\r
8263 fill : function(els){
\r
8264 this.elements = [];
\r
8270 * Returns the first Element
\r
8271 * @return {Ext.Element}
\r
8273 first : function(){
\r
8274 return this.item(0);
\r
8278 * Returns the last Element
\r
8279 * @return {Ext.Element}
\r
8281 last : function(){
\r
8282 return this.item(this.getCount()-1);
\r
8286 * Returns true if this composite contains the passed element
\r
8287 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
\r
8290 contains : function(el){
\r
8291 return this.indexOf(el) != -1;
\r
8295 * Filters this composite to only elements that match the passed selector.
\r
8296 * @param {String} selector A string CSS selector
\r
8297 * @return {CompositeElement} this
\r
8299 filter : function(selector){
\r
8301 this.each(function(el){
\r
8302 if(el.is(selector)){
\r
8303 els[els.length] = el.dom;
\r
8311 * Removes the specified element(s).
\r
8312 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
\r
8313 * or an array of any of those.
\r
8314 * @param {Boolean} removeDom (optional) True to also remove the element from the document
\r
8315 * @return {CompositeElement} this
\r
8317 removeElement : function(keys, removeDom){
\r
8319 els = this.elements,
\r
8321 Ext.each(keys, function(val){
\r
8322 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
\r
8327 Ext.removeNode(el);
\r
8330 els.splice(val, 1);
\r
8337 * @class Ext.CompositeElement
\r
8338 * @extends Ext.CompositeElementLite
\r
8339 * Standard composite class. Creates a Ext.Element for every element in the collection.
\r
8341 * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Ext.Element. All Ext.Element
\r
8342 * actions will be performed on all the elements in this collection.</b>
\r
8344 * All methods return <i>this</i> and can be chained.
\r
8346 var els = Ext.select("#some-el div.some-class", true);
\r
8347 // or select directly from an existing element
\r
8348 var el = Ext.get('some-el');
\r
8349 el.select('div.some-class', true);
\r
8351 els.setWidth(100); // all elements become 100 width
\r
8352 els.hide(true); // all elements fade out and hide
\r
8354 els.setWidth(100).hide(true);
\r
8357 Ext.CompositeElement = function(els, root){
\r
8358 this.elements = [];
\r
8359 this.add(els, root);
\r
8362 Ext.extend(Ext.CompositeElement, Ext.CompositeElementLite, {
\r
8363 invoke : function(fn, args){
\r
8364 Ext.each(this.elements, function(e) {
\r
8365 Ext.Element.prototype[fn].apply(e, args);
\r
8371 * Adds elements to this composite.
\r
8372 * @param {String/Array} els A string CSS selector, an array of elements or an element
\r
8373 * @return {CompositeElement} this
\r
8375 add : function(els, root){
\r
8379 if(typeof els == "string"){
\r
8380 els = Ext.Element.selectorFunction(els, root);
\r
8382 var yels = this.elements;
\r
8383 Ext.each(els, function(e) {
\r
8384 yels.push(Ext.get(e));
\r
8390 * Returns the Element object at the specified index
\r
8391 * @param {Number} index
\r
8392 * @return {Ext.Element}
\r
8394 item : function(index){
\r
8395 return this.elements[index] || null;
\r
8399 indexOf : function(el){
\r
8400 return this.elements.indexOf(Ext.get(el));
\r
8403 filter : function(selector){
\r
8407 Ext.each(me.elements, function(el) {
\r
8408 if(el.is(selector)){
\r
8409 out.push(Ext.get(el));
\r
8412 me.elements = out;
\r
8417 * Calls the passed function passing (el, this, index) for each element in this composite.
\r
8418 * @param {Function} fn The function to call
\r
8419 * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
\r
8420 * @return {CompositeElement} this
\r
8422 each : function(fn, scope){
\r
8423 Ext.each(this.elements, function(e,i) {
\r
8424 return fn.call(scope || e, e, this, i);
\r
8431 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
\r
8432 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
\r
8433 * {@link Ext.CompositeElementLite CompositeElementLite} object.
\r
8434 * @param {String/Array} selector The CSS selector or an array of elements
\r
8435 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
\r
8436 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
\r
8437 * @return {CompositeElementLite/CompositeElement}
\r
8438 * @member Ext.Element
\r
8441 Ext.Element.select = function(selector, unique, root){
\r
8443 if(typeof selector == "string"){
\r
8444 els = Ext.Element.selectorFunction(selector, root);
\r
8445 }else if(selector.length !== undefined){
\r
8448 throw "Invalid selector";
\r
8451 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
\r
8455 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
\r
8456 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
\r
8457 * {@link Ext.CompositeElementLite CompositeElementLite} object.
\r
8458 * @param {String/Array} selector The CSS selector or an array of elements
\r
8459 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
\r
8460 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
\r
8461 * @return {CompositeElementLite/CompositeElement}
\r
8462 * @member Ext.Element
\r
8465 Ext.select = Ext.Element.select;(function(){
\r
8466 var BEFOREREQUEST = "beforerequest",
\r
8467 REQUESTCOMPLETE = "requestcomplete",
\r
8468 REQUESTEXCEPTION = "requestexception",
\r
8469 UNDEFINED = undefined,
\r
8476 * @class Ext.data.Connection
\r
8477 * @extends Ext.util.Observable
\r
8478 * <p>The class encapsulates a connection to the page's originating domain, allowing requests to be made
\r
8479 * either to a configured URL, or to a URL specified at request time.</p>
\r
8480 * <p>Requests made by this class are asynchronous, and will return immediately. No data from
\r
8481 * the server will be available to the statement immediately following the {@link #request} call.
\r
8482 * To process returned data, use a
\r
8483 * <a href="#request-option-success" ext:member="request-option-success" ext:cls="Ext.data.Connection">success callback</a>
\r
8484 * in the request options object,
\r
8485 * or an {@link #requestcomplete event listener}.</p>
\r
8486 * <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
8487 * is they are <b>not</b> performed using XMLHttpRequests. Instead the form is submitted in the standard
\r
8488 * manner with the DOM <tt><form></tt> element temporarily modified to have its
\r
8489 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
\r
8490 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
\r
8491 * but removed after the return data has been gathered.</p>
\r
8492 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
\r
8493 * server is using JSON to send the return object, then the
\r
8494 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
\r
8495 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
\r
8496 * <p>Characters which are significant to an HTML parser must be sent as HTML entities, so encode
\r
8497 * "<" as "&lt;", "&" as "&amp;" etc.</p>
\r
8498 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
\r
8499 * is created containing a <tt>responseText</tt> property in order to conform to the
\r
8500 * requirements of event handlers and callbacks.</p>
\r
8501 * <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
8502 * and some server technologies (notably JEE) may require some custom processing in order to
\r
8503 * retrieve parameter names and parameter values from the packet content.</p>
\r
8505 * @param {Object} config a configuration object.
\r
8507 Ext.data.Connection = function(config){
\r
8508 Ext.apply(this, config);
\r
8511 * @event beforerequest
\r
8512 * Fires before a network request is made to retrieve a data object.
\r
8513 * @param {Connection} conn This Connection object.
\r
8514 * @param {Object} options The options config object passed to the {@link #request} method.
\r
8518 * @event requestcomplete
\r
8519 * Fires if the request was successfully completed.
\r
8520 * @param {Connection} conn This Connection object.
\r
8521 * @param {Object} response The XHR object containing the response data.
\r
8522 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
\r
8524 * @param {Object} options The options config object passed to the {@link #request} method.
\r
8528 * @event requestexception
\r
8529 * Fires if an error HTTP status was returned from the server.
\r
8530 * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
\r
8531 * for details of HTTP status codes.
\r
8532 * @param {Connection} conn This Connection object.
\r
8533 * @param {Object} response The XHR object containing the response data.
\r
8534 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
\r
8536 * @param {Object} options The options config object passed to the {@link #request} method.
\r
8540 Ext.data.Connection.superclass.constructor.call(this);
\r
8544 function handleResponse(response){
\r
8545 this.transId = false;
\r
8546 var options = response.argument.options;
\r
8547 response.argument = options ? options.argument : null;
\r
8548 this.fireEvent(REQUESTCOMPLETE, this, response, options);
\r
8549 if(options.success){
\r
8550 options.success.call(options.scope, response, options);
\r
8552 if(options.callback){
\r
8553 options.callback.call(options.scope, options, true, response);
\r
8558 function handleFailure(response, e){
\r
8559 this.transId = false;
\r
8560 var options = response.argument.options;
\r
8561 response.argument = options ? options.argument : null;
\r
8562 this.fireEvent(REQUESTEXCEPTION, this, response, options, e);
\r
8563 if(options.failure){
\r
8564 options.failure.call(options.scope, response, options);
\r
8566 if(options.callback){
\r
8567 options.callback.call(options.scope, options, false, response);
\r
8572 function doFormUpload(o, ps, url){
\r
8573 var id = Ext.id(),
\r
8575 frame = doc.createElement('iframe'),
\r
8576 form = Ext.getDom(o.form),
\r
8579 encoding = 'multipart/form-data',
\r
8581 target: form.target,
\r
8582 method: form.method,
\r
8583 encoding: form.encoding,
\r
8584 enctype: form.enctype,
\r
8585 action: form.action
\r
8588 Ext.apply(frame, {
\r
8591 className: 'x-hidden',
\r
8592 src: Ext.SSL_SECURE_URL // for IE
\r
8594 doc.body.appendChild(frame);
\r
8596 // This is required so that IE doesn't pop the response up in a new window.
\r
8598 document.frames[id].name = id;
\r
8604 enctype: encoding,
\r
8605 encoding: encoding,
\r
8606 action: url || buf.action
\r
8609 // add dynamic params
\r
8610 ps = Ext.urlDecode(ps, false);
\r
8612 if(ps.hasOwnProperty(k)){
\r
8613 hd = doc.createElement('input');
\r
8614 hd.type = 'hidden';
\r
8615 hd.value = ps[hd.name = k];
\r
8616 form.appendChild(hd);
\r
8623 // bogus response object
\r
8624 r = {responseText : '',
\r
8625 responseXML : null,
\r
8626 argument : o.argument},
\r
8631 doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
\r
8634 if(/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)){ // json response wrapped in textarea
\r
8635 r.responseText = firstChild.value;
\r
8637 r.responseText = doc.body.innerHTML;
\r
8640 //in IE the document may still have a body even if returns XML.
\r
8641 r.responseXML = doc.XMLDocument || doc;
\r
8646 Ext.EventManager.removeListener(frame, LOAD, cb, me);
\r
8648 me.fireEvent(REQUESTCOMPLETE, me, r, o);
\r
8650 function runCallback(fn, scope, args){
\r
8651 if(Ext.isFunction(fn)){
\r
8652 fn.apply(scope, args);
\r
8656 runCallback(o.success, o.scope, [r, o]);
\r
8657 runCallback(o.callback, o.scope, [o, true, r]);
\r
8659 if(!me.debugUploads){
\r
8660 setTimeout(function(){Ext.removeNode(frame);}, 100);
\r
8664 Ext.EventManager.on(frame, LOAD, cb, this);
\r
8667 Ext.apply(form, buf);
\r
8668 Ext.each(hiddens, function(h) {
\r
8669 Ext.removeNode(h);
\r
8673 Ext.extend(Ext.data.Connection, Ext.util.Observable, {
\r
8675 * @cfg {String} url (Optional) <p>The default URL to be used for requests to the server. Defaults to undefined.</p>
\r
8676 * <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
8677 * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p>
\r
8680 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
\r
8681 * extra parameters to each request made by this object. (defaults to undefined)
\r
8684 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
\r
8685 * to each request made by this object. (defaults to undefined)
\r
8688 * @cfg {String} method (Optional) The default HTTP method to be used for requests.
\r
8689 * (defaults to undefined; if not set, but {@link #request} params are present, POST will be used;
\r
8690 * otherwise, GET will be used.)
\r
8693 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
\r
8697 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
\r
8703 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
\r
8706 disableCaching: true,
\r
8709 * @cfg {String} disableCachingParam (Optional) Change the parameter which is sent went disabling caching
\r
8710 * through a cache buster. Defaults to '_dc'
\r
8713 disableCachingParam: '_dc',
\r
8716 * <p>Sends an HTTP request to a remote server.</p>
\r
8717 * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
\r
8718 * return before the response has been received. Process any returned data
\r
8719 * in a callback function.</p>
\r
8721 Ext.Ajax.request({
\r
8722 url: 'ajax_demo/sample.json',
\r
8723 success: function(response, opts) {
\r
8724 var obj = Ext.decode(response.responseText);
\r
8727 failure: function(response, opts) {
\r
8728 console.log('server-side failure with status code ' + response.status);
\r
8732 * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
\r
8733 * @param {Object} options An object which may contain the following properties:<ul>
\r
8734 * <li><b>url</b> : String/Function (Optional)<div class="sub-desc">The URL to
\r
8735 * which to send the request, or a function to call which returns a URL string. The scope of the
\r
8736 * function is specified by the <tt>scope</tt> option. Defaults to the configured
\r
8737 * <tt>{@link #url}</tt>.</div></li>
\r
8738 * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
\r
8739 * An object containing properties which are used as parameters to the
\r
8740 * request, a url encoded string or a function to call to get either. The scope of the function
\r
8741 * is specified by the <tt>scope</tt> option.</div></li>
\r
8742 * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
\r
8743 * for the request. Defaults to the configured method, or if no method was configured,
\r
8744 * "GET" if no parameters are being sent, and "POST" if parameters are being sent. Note that
\r
8745 * the method name is case-sensitive and should be all caps.</div></li>
\r
8746 * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
\r
8747 * function to be called upon receipt of the HTTP response. The callback is
\r
8748 * called regardless of success or failure and is passed the following
\r
8750 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
\r
8751 * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
\r
8752 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.
\r
8753 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about
\r
8754 * accessing elements of the response.</div></li>
\r
8755 * </ul></div></li>
\r
8756 * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
\r
8757 * to be called upon success of the request. The callback is passed the following
\r
8759 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
\r
8760 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
\r
8761 * </ul></div></li>
\r
8762 * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
\r
8763 * to be called upon failure of the request. The callback is passed the
\r
8764 * following parameters:<ul>
\r
8765 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
\r
8766 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
\r
8767 * </ul></div></li>
\r
8768 * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
\r
8769 * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
\r
8770 * specified as functions from which to draw values, then this also serves as the scope for those function calls.
\r
8771 * Defaults to the browser window.</div></li>
\r
8772 * <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
8773 * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt><form></tt>
\r
8774 * Element or the id of the <tt><form></tt> to pull parameters from.</div></li>
\r
8775 * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used
\r
8776 * with the <tt>form</tt> option</b>.
\r
8777 * <p>True if the form object is a file upload (will be set automatically if the form was
\r
8778 * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
\r
8779 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
\r
8780 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
\r
8781 * DOM <tt><form></tt> element temporarily modified to have its
\r
8782 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
\r
8783 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
\r
8784 * but removed after the return data has been gathered.</p>
\r
8785 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
\r
8786 * server is using JSON to send the return object, then the
\r
8787 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
\r
8788 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
\r
8789 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
\r
8790 * is created containing a <tt>responseText</tt> property in order to conform to the
\r
8791 * requirements of event handlers and callbacks.</p>
\r
8792 * <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
8793 * and some server technologies (notably JEE) may require some custom processing in order to
\r
8794 * retrieve parameter names and parameter values from the packet content.</p>
\r
8796 * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
\r
8797 * headers to set for the request.</div></li>
\r
8798 * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
\r
8799 * to use for the post. Note: This will be used instead of params for the post
\r
8800 * data. Any params will be appended to the URL.</div></li>
\r
8801 * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
\r
8802 * data to use as the post. Note: This will be used instead of params for the post
\r
8803 * data. Any params will be appended to the URL.</div></li>
\r
8804 * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
\r
8805 * to add a unique cache-buster param to GET requests.</div></li>
\r
8807 * <p>The options object may also contain any other property which might be needed to perform
\r
8808 * postprocessing in a callback because it is passed to callback functions.</p>
\r
8809 * @return {Number} transactionId The id of the server transaction. This may be used
\r
8810 * to cancel the request.
\r
8812 request : function(o){
\r
8814 if(me.fireEvent(BEFOREREQUEST, me, o)){
\r
8816 if(!Ext.isEmpty(o.indicatorText)){
\r
8817 me.indicatorText = '<div class="loading-indicator">'+o.indicatorText+"</div>";
\r
8819 if(me.indicatorText) {
\r
8820 Ext.getDom(o.el).innerHTML = me.indicatorText;
\r
8822 o.success = (Ext.isFunction(o.success) ? o.success : function(){}).createInterceptor(function(response) {
\r
8823 Ext.getDom(o.el).innerHTML = response.responseText;
\r
8828 url = o.url || me.url,
\r
8830 cb = {success: handleResponse,
\r
8831 failure: handleFailure,
\r
8833 argument: {options: o},
\r
8834 timeout : o.timeout || me.timeout
\r
8840 if (Ext.isFunction(p)) {
\r
8841 p = p.call(o.scope||WINDOW, o);
\r
8844 p = Ext.urlEncode(me.extraParams, Ext.isObject(p) ? Ext.urlEncode(p) : p);
\r
8846 if (Ext.isFunction(url)) {
\r
8847 url = url.call(o.scope || WINDOW, o);
\r
8850 if((form = Ext.getDom(o.form))){
\r
8851 url = url || form.action;
\r
8852 if(o.isUpload || /multipart\/form-data/i.test(form.getAttribute("enctype"))) {
\r
8853 return doFormUpload.call(me, o, p, url);
\r
8855 serForm = Ext.lib.Ajax.serializeForm(form);
\r
8856 p = p ? (p + '&' + serForm) : serForm;
\r
8859 method = o.method || me.method || ((p || o.xmlData || o.jsonData) ? POST : GET);
\r
8861 if(method === GET && (me.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
\r
8862 var dcp = o.disableCachingParam || me.disableCachingParam;
\r
8863 url = Ext.urlAppend(url, dcp + '=' + (new Date().getTime()));
\r
8866 o.headers = Ext.apply(o.headers || {}, me.defaultHeaders || {});
\r
8868 if(o.autoAbort === true || me.autoAbort) {
\r
8872 if((method == GET || o.xmlData || o.jsonData) && p){
\r
8873 url = Ext.urlAppend(url, p);
\r
8876 return (me.transId = Ext.lib.Ajax.request(method, url, cb, p, o));
\r
8878 return o.callback ? o.callback.apply(o.scope, [o,UNDEFINED,UNDEFINED]) : null;
\r
8883 * Determine whether this object has a request outstanding.
\r
8884 * @param {Number} transactionId (Optional) defaults to the last transaction
\r
8885 * @return {Boolean} True if there is an outstanding request.
\r
8887 isLoading : function(transId){
\r
8888 return transId ? Ext.lib.Ajax.isCallInProgress(transId) : !! this.transId;
\r
8892 * Aborts any outstanding request.
\r
8893 * @param {Number} transactionId (Optional) defaults to the last transaction
\r
8895 abort : function(transId){
\r
8896 if(transId || this.isLoading()){
\r
8897 Ext.lib.Ajax.abort(transId || this.transId);
\r
8905 * @extends Ext.data.Connection
\r
8906 * <p>The global Ajax request class that provides a simple way to make Ajax requests
\r
8907 * with maximum flexibility.</p>
\r
8908 * <p>Since Ext.Ajax is a singleton, you can set common properties/events for it once
\r
8909 * and override them at the request function level only if necessary.</p>
\r
8910 * <p>Common <b>Properties</b> you may want to set are:<div class="mdetail-params"><ul>
\r
8911 * <li><b><tt>{@link #method}</tt></b><p class="sub-desc"></p></li>
\r
8912 * <li><b><tt>{@link #extraParams}</tt></b><p class="sub-desc"></p></li>
\r
8913 * <li><b><tt>{@link #url}</tt></b><p class="sub-desc"></p></li>
\r
8916 // Default headers to pass in every request
\r
8917 Ext.Ajax.defaultHeaders = {
\r
8918 'Powered-By': 'Ext'
\r
8922 * <p>Common <b>Events</b> you may want to set are:<div class="mdetail-params"><ul>
\r
8923 * <li><b><tt>{@link Ext.data.Connection#beforerequest beforerequest}</tt></b><p class="sub-desc"></p></li>
\r
8924 * <li><b><tt>{@link Ext.data.Connection#requestcomplete requestcomplete}</tt></b><p class="sub-desc"></p></li>
\r
8925 * <li><b><tt>{@link Ext.data.Connection#requestexception requestexception}</tt></b><p class="sub-desc"></p></li>
\r
8928 // Example: show a spinner during all Ajax requests
\r
8929 Ext.Ajax.on('beforerequest', this.showSpinner, this);
\r
8930 Ext.Ajax.on('requestcomplete', this.hideSpinner, this);
\r
8931 Ext.Ajax.on('requestexception', this.hideSpinner, this);
\r
8934 * <p>An example request:</p>
\r
8937 Ext.Ajax.{@link Ext.data.Connection#request request}({
\r
8942 'my-header': 'foo'
\r
8944 params: { foo: 'bar' }
\r
8947 // Simple ajax form submission
\r
8948 Ext.Ajax.{@link Ext.data.Connection#request request}({
\r
8949 form: 'some-form',
\r
8956 Ext.Ajax = new Ext.data.Connection({
\r
8958 * @cfg {String} url @hide
\r
8961 * @cfg {Object} extraParams @hide
\r
8964 * @cfg {Object} defaultHeaders @hide
\r
8967 * @cfg {String} method (Optional) @hide
\r
8970 * @cfg {Number} timeout (Optional) @hide
\r
8973 * @cfg {Boolean} autoAbort (Optional) @hide
\r
8977 * @cfg {Boolean} disableCaching (Optional) @hide
\r
8981 * @property disableCaching
\r
8982 * True to add a unique cache-buster param to GET requests. (defaults to true)
\r
8987 * The default URL to be used for requests to the server. (defaults to undefined)
\r
8988 * If the server receives all requests through one URL, setting this once is easier than
\r
8989 * entering it on every request.
\r
8993 * @property extraParams
\r
8994 * An object containing properties which are used as extra parameters to each request made
\r
8995 * by this object (defaults to undefined). Session information and other data that you need
\r
8996 * to pass with each request are commonly put here.
\r
9000 * @property defaultHeaders
\r
9001 * An object containing request headers which are added to each request made by this object
\r
9002 * (defaults to undefined).
\r
9006 * @property method
\r
9007 * The default HTTP method to be used for requests. Note that this is case-sensitive and
\r
9008 * should be all caps (defaults to undefined; if not set but params are present will use
\r
9009 * <tt>"POST"</tt>, otherwise will use <tt>"GET"</tt>.)
\r
9013 * @property timeout
\r
9014 * The timeout in milliseconds to be used for requests. (defaults to 30000)
\r
9019 * @property autoAbort
\r
9020 * Whether a new request should abort any pending requests. (defaults to false)
\r
9023 autoAbort : false,
\r
9026 * Serialize the passed form into a url encoded string
\r
9027 * @param {String/HTMLElement} form
\r
9028 * @return {String}
\r
9030 serializeForm : function(form){
\r
9031 return Ext.lib.Ajax.serializeForm(form);
\r
9035 * @class Ext.Updater
9036 * @extends Ext.util.Observable
9037 * Provides AJAX-style update capabilities for Element objects. Updater can be used to {@link #update}
9038 * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
9039 * {@link Ext.Element Element} on a specific interval.<br><br>
9042 * var el = Ext.get("foo"); // Get Ext.Element object
9043 * var mgr = el.getUpdater();
9045 url: "http://myserver.com/index.php",
9052 * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
9054 * // or directly (returns the same Updater instance)
9055 * var mgr = new Ext.Updater("myElementId");
9056 * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
9057 * mgr.on("update", myFcnNeedsToKnow);
9059 * // short handed call directly from the element object
9060 * Ext.get("foo").load({
9063 params: "param1=foo&param2=bar",
9064 text: "Loading Foo..."
9068 * Create new Updater directly.
9069 * @param {Mixed} el The element to update
9070 * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
9071 * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
9073 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
9075 var BEFOREUPDATE = "beforeupdate",
9077 FAILURE = "failure";
9080 function processSuccess(response){
9082 me.transaction = null;
9083 if (response.argument.form && response.argument.reset) {
9084 try { // put in try/catch since some older FF releases had problems with this
9085 response.argument.form.reset();
9088 if (me.loadScripts) {
9089 me.renderer.render(me.el, response, me,
9090 updateComplete.createDelegate(me, [response]));
9092 me.renderer.render(me.el, response, me);
9093 updateComplete.call(me, response);
9098 function updateComplete(response, type, success){
9099 this.fireEvent(type || UPDATE, this.el, response);
9100 if(Ext.isFunction(response.argument.callback)){
9101 response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
9106 function processFailure(response){
9107 updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
9111 constructor: function(el, forceNew){
9114 if(!forceNew && el.updateManager){
9115 return el.updateManager;
9118 * The Element object
9123 * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
9126 me.defaultUrl = null;
9130 * @event beforeupdate
9131 * Fired before an update is made, return false from your handler and the update is cancelled.
9132 * @param {Ext.Element} el
9133 * @param {String/Object/Function} url
9134 * @param {String/Object} params
9139 * Fired after successful update is made.
9140 * @param {Ext.Element} el
9141 * @param {Object} oResponseObject The response Object
9146 * Fired on update failure.
9147 * @param {Ext.Element} el
9148 * @param {Object} oResponseObject The response Object
9153 Ext.apply(me, Ext.Updater.defaults);
9155 * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
9156 * @property sslBlankUrl
9160 * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
9161 * @property disableCaching
9165 * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
9166 * @property indicatorText
9170 * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
9171 * @property showLoadIndicator
9175 * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
9180 * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
9181 * @property loadScripts
9186 * Transaction object of the current executing transaction, or null if there is no active transaction.
9188 me.transaction = null;
9190 * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
9193 me.refreshDelegate = me.refresh.createDelegate(me);
9195 * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
9198 me.updateDelegate = me.update.createDelegate(me);
9200 * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
9203 me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
9206 * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
9208 me.renderer = me.renderer || me.getDefaultRenderer();
9210 Ext.Updater.superclass.constructor.call(me);
9214 * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9215 * @param {Object} renderer The object implementing the render() method
9217 setRenderer : function(renderer){
9218 this.renderer = renderer;
9222 * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
9225 getRenderer : function(){
9226 return this.renderer;
9230 * This is an overrideable method which returns a reference to a default
9231 * renderer class if none is specified when creating the Ext.Updater.
9232 * Defaults to {@link Ext.Updater.BasicRenderer}
9234 getDefaultRenderer: function() {
9235 return new Ext.Updater.BasicRenderer();
9239 * Sets the default URL used for updates.
9240 * @param {String/Function} defaultUrl The url or a function to call to get the url
9242 setDefaultUrl : function(defaultUrl){
9243 this.defaultUrl = defaultUrl;
9247 * Get the Element this Updater is bound to
9248 * @return {Ext.Element} The element
9255 * Performs an <b>asynchronous</b> request, updating this element with the response.
9256 * If params are specified it uses POST, otherwise it uses GET.<br><br>
9257 * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
9258 * will not have been fully updated when the function returns. To post-process the returned
9259 * data, use the callback option, or an <b><tt>update</tt></b> event handler.
9260 * @param {Object} options A config object containing any of the following options:<ul>
9261 * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
9262 * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
9263 * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
9264 * use. Defaults to POST if the <tt>params</tt> argument is present, otherwise GET.</p></li>
9265 * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
9266 * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
9267 * string, or as an object containing properties which represent parameters,
9268 * or as a function, which returns such an object.</p></li>
9269 * <li>scripts : <b>Boolean</b><p class="sub-desc">If <tt>true</tt>
9270 * any <script> tags embedded in the response text will be extracted
9271 * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
9272 * the callback will be called <i>after</i> the execution of the scripts.</p></li>
9273 * <li>callback : <b>Function</b><p class="sub-desc">A function to
9274 * be called when the response from the server arrives. The following
9275 * parameters are passed:<ul>
9276 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
9277 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
9278 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
9279 * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
9281 * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
9282 * to execute the callback (The callback's <tt>this</tt> reference.) If the
9283 * <tt>params</tt> argument is a function, this scope is used for that function also.</p></li>
9284 * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
9285 * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
9286 * calls. To bypass this behavior, pass <tt>discardUrl:true</tt> (defaults to false).</p></li>
9287 * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
9288 * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
9289 * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
9290 * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...'). To replace the entire div, not
9291 * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
9292 * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
9293 * requests, this option causes an extra, auto-generated parameter to be appended to the request
9294 * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
9299 url: "your-url.php",
9300 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9301 callback: yourFunction,
9302 scope: yourObject, //(optional scope)
9307 scripts: false // Save time by avoiding RegExp execution.
9311 update : function(url, params, callback, discardUrl){
9316 if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
9317 if(Ext.isObject(url)){ // must be config object
9320 params = params || cfg.params;
9321 callback = callback || cfg.callback;
9322 discardUrl = discardUrl || cfg.discardUrl;
9323 callerScope = cfg.scope;
9324 if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
9325 if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
9326 if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
9327 if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
9332 me.defaultUrl = url;
9334 if(Ext.isFunction(url)){
9338 var o = Ext.apply({}, {
9340 params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
9341 success: processSuccess,
9342 failure: processFailure,
9344 callback: undefined,
9345 timeout: (me.timeout*1000),
9346 disableCaching: me.disableCaching,
9351 "callback": callback,
9352 "scope": callerScope || window,
9357 me.transaction = Ext.Ajax.request(o);
9362 * <p>Performs an async form post, updating this element with the response. If the form has the attribute
9363 * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
9364 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
9365 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
9366 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
9367 * DOM <tt><form></tt> element temporarily modified to have its
9368 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
9369 * to a dynamically generated, hidden <tt><iframe></tt> which is inserted into the document
9370 * but removed after the return data has been gathered.</p>
9371 * <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>
9372 * and some server technologies (notably JEE) may require some custom processing in order to
9373 * retrieve parameter names and parameter values from the packet content.</p>
9374 * @param {String/HTMLElement} form The form Id or form element
9375 * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
9376 * @param {Boolean} reset (optional) Whether to try to reset the form after the update
9377 * @param {Function} callback (optional) Callback when transaction is complete. The following
9378 * parameters are passed:<ul>
9379 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
9380 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
9381 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
9383 formUpdate : function(form, url, reset, callback){
9385 if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
9386 if(Ext.isFunction(url)){
9389 form = Ext.getDom(form)
9390 me.transaction = Ext.Ajax.request({
9393 success: processSuccess,
9394 failure: processFailure,
9396 timeout: (me.timeout*1000),
9400 "callback": callback,
9404 me.showLoading.defer(1, me);
9409 * Set this element to auto refresh. Can be canceled by calling {@link #stopAutoRefresh}.
9410 * @param {Number} interval How often to update (in seconds).
9411 * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
9412 * supported by {@link #load}, or a function to call to get the url (defaults to the last used url). Note that while
9413 * the url used in a load call can be reused by this method, other load config options will not be reused and must be
9414 * sepcified as part of a config object passed as this paramter if needed.
9415 * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
9416 * "¶m1=1¶m2=2" or as an object {param1: 1, param2: 2}
9417 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9418 * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
9420 startAutoRefresh : function(interval, url, params, callback, refreshNow){
9423 me.update(url || me.defaultUrl, params, callback, true);
9425 if(me.autoRefreshProcId){
9426 clearInterval(me.autoRefreshProcId);
9428 me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
9432 * Stop auto refresh on this element.
9434 stopAutoRefresh : function(){
9435 if(this.autoRefreshProcId){
9436 clearInterval(this.autoRefreshProcId);
9437 delete this.autoRefreshProcId;
9442 * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
9444 isAutoRefreshing : function(){
9445 return !!this.autoRefreshProcId;
9449 * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
9450 * method may be overridden to perform a custom action while this Updater is actively updating its contents.
9452 showLoading : function(){
9453 if(this.showLoadIndicator){
9454 this.el.dom.innerHTML = this.indicatorText;
9459 * Aborts the currently executing transaction, if any.
9462 if(this.transaction){
9463 Ext.Ajax.abort(this.transaction);
9468 * Returns true if an update is in progress, otherwise false.
9471 isUpdating : function(){
9472 return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
9476 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
9477 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9479 refresh : function(callback){
9480 if(this.defaultUrl){
9481 this.update(this.defaultUrl, null, callback, true);
9488 * @class Ext.Updater.defaults
9489 * The defaults collection enables customizing the default properties of Updater
9491 Ext.Updater.defaults = {
9493 * Timeout for requests or form posts in seconds (defaults to 30 seconds).
9498 * True to append a unique parameter to GET requests to disable caching (defaults to false).
9501 disableCaching : false,
9503 * Whether or not to show {@link #indicatorText} during loading (defaults to true).
9506 showLoadIndicator : true,
9508 * Text for loading indicator (defaults to '<div class="loading-indicator">Loading...</div>').
9511 indicatorText : '<div class="loading-indicator">Loading...</div>',
9513 * True to process scripts by default (defaults to false).
9516 loadScripts : false,
9518 * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
9521 sslBlankUrl : (Ext.SSL_SECURE_URL || "javascript:false")
9526 * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
9528 * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
9529 * @param {Mixed} el The element to update
9530 * @param {String} url The url
9531 * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
9532 * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
9533 * example: {disableCaching:true, indicatorText: "Loading data..."}
9536 * @member Ext.Updater
9538 Ext.Updater.updateElement = function(el, url, params, options){
9539 var um = Ext.get(el).getUpdater();
9540 Ext.apply(um, options);
9541 um.update(url, params, options ? options.callback : null);
9545 * @class Ext.Updater.BasicRenderer
9546 * Default Content renderer. Updates the elements innerHTML with the responseText.
9548 Ext.Updater.BasicRenderer = function(){};
9550 Ext.Updater.BasicRenderer.prototype = {
9552 * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
9553 * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
9554 * create an object with a "render(el, response)" method and pass it to setRenderer on the Updater.
9555 * @param {Ext.Element} el The element being rendered
9556 * @param {Object} response The XMLHttpRequest object
9557 * @param {Updater} updateManager The calling update manager
9558 * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
9560 render : function(el, response, updateManager, callback){
9561 el.update(response.responseText, updateManager.loadScripts, callback);
9566 * The date parsing and formatting syntax contains a subset of
9567 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
9568 * supported will provide results equivalent to their PHP versions.
9570 * The following is a list of all currently supported formats:
9572 Format Description Example returned values
9573 ------ ----------------------------------------------------------------------- -----------------------
9574 d Day of the month, 2 digits with leading zeros 01 to 31
9575 D A short textual representation of the day of the week Mon to Sun
9576 j Day of the month without leading zeros 1 to 31
9577 l A full textual representation of the day of the week Sunday to Saturday
9578 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
9579 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
9580 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
9581 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
9582 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
9583 F A full textual representation of a month, such as January or March January to December
9584 m Numeric representation of a month, with leading zeros 01 to 12
9585 M A short textual representation of a month Jan to Dec
9586 n Numeric representation of a month, without leading zeros 1 to 12
9587 t Number of days in the given month 28 to 31
9588 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
9589 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
9590 belongs to the previous or next year, that year is used instead)
9591 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
9592 y A two digit representation of a year Examples: 99 or 03
9593 a Lowercase Ante meridiem and Post meridiem am or pm
9594 A Uppercase Ante meridiem and Post meridiem AM or PM
9595 g 12-hour format of an hour without leading zeros 1 to 12
9596 G 24-hour format of an hour without leading zeros 0 to 23
9597 h 12-hour format of an hour with leading zeros 01 to 12
9598 H 24-hour format of an hour with leading zeros 00 to 23
9599 i Minutes, with leading zeros 00 to 59
9600 s Seconds, with leading zeros 00 to 59
9601 u Decimal fraction of a second Examples:
9602 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
9603 100 (i.e. 0.100s) or
9604 999 (i.e. 0.999s) or
9605 999876543210 (i.e. 0.999876543210s)
9606 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
9607 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
9608 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
9609 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
9612 1) If unspecified, the month / day defaults to the current month / day, 1991 or
9613 the time defaults to midnight, while the timezone defaults to the 1992-10 or
9614 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
9615 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
9616 are optional. 1995-07-18T17:21:28-02:00 or
9617 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
9618 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
9619 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
9620 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
9621 date-time granularity which are supported, or see 2000-02-13T21:25:33
9622 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
9623 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
9624 M$ Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
9625 \/Date(1238606590509+0800)\/
9628 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
9631 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
9633 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
9634 document.write(dt.format('Y-m-d')); // 2007-01-10
9635 document.write(dt.format('F j, Y, g:i a')); // January 10, 2007, 3:05 pm
9636 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
9639 * Here are some standard date/time patterns that you might find helpful. They
9640 * are not part of the source of Date.js, but to use them you can simply copy this
9641 * block of code into any script that is included after Date.js and they will also become
9642 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
9645 ISO8601Long:"Y-m-d H:i:s",
9646 ISO8601Short:"Y-m-d",
9648 LongDate: "l, F d, Y",
9649 FullDateTime: "l, F d, Y g:i:s A",
9652 LongTime: "g:i:s A",
9653 SortableDateTime: "Y-m-d\\TH:i:s",
9654 UniversalSortableDateTime: "Y-m-d H:i:sO",
9661 var dt = new Date();
9662 document.write(dt.format(Date.patterns.ShortDate));
9664 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
9665 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
9669 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
9670 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
9671 * They generate precompiled functions from format patterns instead of parsing and
9672 * processing each pattern every time a date is formatted. These functions are available
9673 * on every Date object.
9679 * Global flag which determines if strict date parsing should be used.
9680 * Strict date parsing will not roll-over invalid dates, which is the
9681 * default behaviour of javascript Date objects.
9682 * (see {@link #parseDate} for more information)
9683 * Defaults to <tt>false</tt>.
9687 Date.useStrict = false;
9690 // create private copy of Ext's String.format() method
9691 // - to remove unnecessary dependency
9692 // - to resolve namespace conflict with M$-Ajax's implementation
9693 function xf(format) {
9694 var args = Array.prototype.slice.call(arguments, 1);
9695 return format.replace(/\{(\d+)\}/g, function(m, i) {
9702 Date.formatCodeToRegex = function(character, currentGroup) {
9703 // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
9704 var p = Date.parseCodes[character];
9707 p = typeof p == 'function'? p() : p;
9708 Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
9711 return p? Ext.applyIf({
9712 c: p.c? xf(p.c, currentGroup || "{0}") : p.c
9716 s:Ext.escapeRe(character) // treat unrecognised characters as literals
9720 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
9721 var $f = Date.formatCodeToRegex;
9725 * <p>An object hash in which each property is a date parsing function. The property name is the
9726 * format string which that function parses.</p>
9727 * <p>This object is automatically populated with date parsing functions as
9728 * date formats are requested for Ext standard formatting strings.</p>
9729 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
9730 * may be used as a format string to {@link #parseDate}.<p>
9731 * <p>Example:</p><pre><code>
9732 Date.parseFunctions['x-date-format'] = myDateParser;
9734 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
9735 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
9736 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
9737 * (i.e. prevent javascript Date "rollover") (The default must be false).
9738 * Invalid date strings should return null when parsed.</div></li>
9740 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
9741 * formatting function must be placed into the {@link #formatFunctions} property.
9742 * @property parseFunctions
9747 "M$": function(input, strict) {
9748 // note: the timezone offset is ignored since the M$ Ajax server sends
9749 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
9750 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
9751 var r = (input || '').match(re);
9752 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
9758 * <p>An object hash in which each property is a date formatting function. The property name is the
9759 * format string which corresponds to the produced formatted date string.</p>
9760 * <p>This object is automatically populated with date formatting functions as
9761 * date formats are requested for Ext standard formatting strings.</p>
9762 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
9763 * may be used as a format string to {@link #format}. Example:</p><pre><code>
9764 Date.formatFunctions['x-date-format'] = myDateFormatter;
9766 * <p>A formatting function should return a string repesentation of the passed Date object:<div class="mdetail-params"><ul>
9767 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
9769 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
9770 * parsing function must be placed into the {@link #parseFunctions} property.
9771 * @property formatFunctions
9777 // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
9778 return '\\/Date(' + this.getTime() + ')\\/';
9785 * Date interval constant
9792 * Date interval constant
9799 * Date interval constant
9805 /** Date interval constant
9812 * Date interval constant
9819 * Date interval constant
9826 * Date interval constant
9833 * <p>An object hash containing default date values used during date parsing.</p>
9834 * <p>The following properties are available:<div class="mdetail-params"><ul>
9835 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
9836 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
9837 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
9838 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
9839 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
9840 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
9841 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
9843 * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
9844 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
9845 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
9846 * It is the responsiblity of the developer to account for this.</b></p>
9849 // set default day value to the first day of the month
9850 Date.defaults.d = 1;
9852 // parse a February date string containing only year and month values.
9853 // setting the default day value to 1 prevents weird date rollover issues
9854 // when attempting to parse the following date string on, for example, March 31st 2009.
9855 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
9857 * @property defaults
9864 * An array of textual day names.
9865 * Override these values for international dates.
9888 * An array of textual month names.
9889 * Override these values for international dates.
9917 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
9918 * Override these values for international dates.
9921 Date.monthNumbers = {
9922 'ShortJanNameInYourLang':0,
9923 'ShortFebNameInYourLang':1,
9946 * Get the short month name for the given month number.
9947 * Override this function for international dates.
9948 * @param {Number} month A zero-based javascript month number.
9949 * @return {String} The short month name.
9952 getShortMonthName : function(month) {
9953 return Date.monthNames[month].substring(0, 3);
9957 * Get the short day name for the given day number.
9958 * Override this function for international dates.
9959 * @param {Number} day A zero-based javascript day number.
9960 * @return {String} The short day name.
9963 getShortDayName : function(day) {
9964 return Date.dayNames[day].substring(0, 3);
9968 * Get the zero-based javascript month number for the given short/full month name.
9969 * Override this function for international dates.
9970 * @param {String} name The short/full month name.
9971 * @return {Number} The zero-based javascript month number.
9974 getMonthNumber : function(name) {
9975 // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
9976 return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
9980 * The base format-code to formatting-function hashmap used by the {@link #format} method.
9981 * Formatting functions are strings (or functions which return strings) which
9982 * will return the appropriate value when evaluated in the context of the Date object
9983 * from which the {@link #format} method is called.
9984 * Add to / override these mappings for custom date formatting.
9985 * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
9988 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
9989 (new Date()).format("X"); // returns the current day of the month
9995 d: "String.leftPad(this.getDate(), 2, '0')",
9996 D: "Date.getShortDayName(this.getDay())", // get localised short day name
9997 j: "this.getDate()",
9998 l: "Date.dayNames[this.getDay()]",
9999 N: "(this.getDay() ? this.getDay() : 7)",
10000 S: "this.getSuffix()",
10001 w: "this.getDay()",
10002 z: "this.getDayOfYear()",
10003 W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
10004 F: "Date.monthNames[this.getMonth()]",
10005 m: "String.leftPad(this.getMonth() + 1, 2, '0')",
10006 M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
10007 n: "(this.getMonth() + 1)",
10008 t: "this.getDaysInMonth()",
10009 L: "(this.isLeapYear() ? 1 : 0)",
10010 o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
10011 Y: "this.getFullYear()",
10012 y: "('' + this.getFullYear()).substring(2, 4)",
10013 a: "(this.getHours() < 12 ? 'am' : 'pm')",
10014 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
10015 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
10016 G: "this.getHours()",
10017 h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
10018 H: "String.leftPad(this.getHours(), 2, '0')",
10019 i: "String.leftPad(this.getMinutes(), 2, '0')",
10020 s: "String.leftPad(this.getSeconds(), 2, '0')",
10021 u: "String.leftPad(this.getMilliseconds(), 3, '0')",
10022 O: "this.getGMTOffset()",
10023 P: "this.getGMTOffset(true)",
10024 T: "this.getTimezone()",
10025 Z: "(this.getTimezoneOffset() * -60)",
10027 c: function() { // ISO-8601 -- GMT format
10028 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
10029 var e = c.charAt(i);
10030 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
10032 return code.join(" + ");
10035 c: function() { // ISO-8601 -- UTC format
10037 "this.getUTCFullYear()", "'-'",
10038 "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
10039 "String.leftPad(this.getUTCDate(), 2, '0')",
10041 "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
10042 "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
10043 "String.leftPad(this.getUTCSeconds(), 2, '0')",
10049 U: "Math.round(this.getTime() / 1000)"
10053 * Checks if the passed Date parameters will cause a javascript Date "rollover".
10054 * @param {Number} year 4-digit year
10055 * @param {Number} month 1-based month-of-year
10056 * @param {Number} day Day of month
10057 * @param {Number} hour (optional) Hour
10058 * @param {Number} minute (optional) Minute
10059 * @param {Number} second (optional) Second
10060 * @param {Number} millisecond (optional) Millisecond
10061 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
10064 isValid : function(y, m, d, h, i, s, ms) {
10071 var dt = new Date(y, m - 1, d, h, i, s, ms);
10073 return y == dt.getFullYear() &&
10074 m == dt.getMonth() + 1 &&
10075 d == dt.getDate() &&
10076 h == dt.getHours() &&
10077 i == dt.getMinutes() &&
10078 s == dt.getSeconds() &&
10079 ms == dt.getMilliseconds();
10083 * Parses the passed string using the specified date format.
10084 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
10085 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
10086 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
10087 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
10088 * Keep in mind that the input date string must precisely match the specified format string
10089 * in order for the parse operation to be successful (failed parse operations return a null value).
10090 * <p>Example:</p><pre><code>
10091 //dt = Fri May 25 2007 (current date)
10092 var dt = new Date();
10094 //dt = Thu May 25 2006 (today's month/day in 2006)
10095 dt = Date.parseDate("2006", "Y");
10097 //dt = Sun Jan 15 2006 (all date parts specified)
10098 dt = Date.parseDate("2006-01-15", "Y-m-d");
10100 //dt = Sun Jan 15 2006 15:20:01
10101 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
10103 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
10104 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
10106 * @param {String} input The raw date string.
10107 * @param {String} format The expected date string format.
10108 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
10109 (defaults to false). Invalid date strings will return null when parsed.
10110 * @return {Date} The parsed Date.
10113 parseDate : function(input, format, strict) {
10114 var p = Date.parseFunctions;
10115 if (p[format] == null) {
10116 Date.createParser(format);
10118 return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
10122 getFormatCode : function(character) {
10123 var f = Date.formatCodes[character];
10126 f = typeof f == 'function'? f() : f;
10127 Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
10130 // note: unknown characters are treated as literals
10131 return f || ("'" + String.escape(character) + "'");
10135 createFormat : function(format) {
10140 for (var i = 0; i < format.length; ++i) {
10141 ch = format.charAt(i);
10142 if (!special && ch == "\\") {
10144 } else if (special) {
10146 code.push("'" + String.escape(ch) + "'");
10148 code.push(Date.getFormatCode(ch))
10151 Date.formatFunctions[format] = new Function("return " + code.join('+'));
10155 createParser : function() {
10157 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
10158 "def = Date.defaults,",
10159 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
10164 "if(u != null){", // i.e. unix time is defined
10165 "v = new Date(u * 1000);", // give top priority to UNIX time
10167 // create Date object representing midnight of the current day;
10168 // this will provide us with our date defaults
10169 // (note: clearTime() handles Daylight Saving Time automatically)
10170 "dt = (new Date()).clearTime();",
10172 // date calculations (note: these calculations create a dependency on Ext.num())
10173 "y = y >= 0? y : Ext.num(def.y, dt.getFullYear());",
10174 "m = m >= 0? m : Ext.num(def.m - 1, dt.getMonth());",
10175 "d = d >= 0? d : Ext.num(def.d, dt.getDate());",
10177 // time calculations (note: these calculations create a dependency on Ext.num())
10178 "h = h || Ext.num(def.h, dt.getHours());",
10179 "i = i || Ext.num(def.i, dt.getMinutes());",
10180 "s = s || Ext.num(def.s, dt.getSeconds());",
10181 "ms = ms || Ext.num(def.ms, dt.getMilliseconds());",
10183 "if(z >= 0 && y >= 0){",
10184 // both the year and zero-based day of year are defined and >= 0.
10185 // these 2 values alone provide sufficient info to create a full date object
10187 // create Date object representing January 1st for the given year
10188 "v = new Date(y, 0, 1, h, i, s, ms);",
10190 // then add day of year, checking for Date "rollover" if necessary
10191 "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
10192 "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
10193 "v = null;", // invalid date, so return null
10195 // plain old Date object
10196 "v = new Date(y, m, d, h, i, s, ms);",
10202 // favour UTC offset over GMT offset
10204 // reset to UTC, then add offset
10205 "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
10207 // reset to GMT, then add offset
10208 "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
10215 return function(format) {
10216 var regexNum = Date.parseRegexes.length,
10223 for (var i = 0; i < format.length; ++i) {
10224 ch = format.charAt(i);
10225 if (!special && ch == "\\") {
10227 } else if (special) {
10229 regex.push(String.escape(ch));
10231 var obj = $f(ch, currentGroup);
10232 currentGroup += obj.g;
10234 if (obj.g && obj.c) {
10240 Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", "i");
10241 Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
10249 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
10250 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
10251 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
10255 c:"d = parseInt(results[{0}], 10);\n",
10256 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
10260 c:"d = parseInt(results[{0}], 10);\n",
10261 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
10264 for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
10268 s:"(?:" + a.join("|") +")"
10275 s:"(?:" + Date.dayNames.join("|") + ")"
10281 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
10286 s:"(?:st|nd|rd|th)"
10291 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
10295 c:"z = parseInt(results[{0}], 10);\n",
10296 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
10301 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
10306 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
10307 s:"(" + Date.monthNames.join("|") + ")"
10311 for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
10312 return Ext.applyIf({
10313 s:"(" + a.join("|") + ")"
10318 c:"m = parseInt(results[{0}], 10) - 1;\n",
10319 s:"(\\d{2})" // month number with leading zeros (01 - 12)
10323 c:"m = parseInt(results[{0}], 10) - 1;\n",
10324 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
10329 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
10341 c:"y = parseInt(results[{0}], 10);\n",
10342 s:"(\\d{4})" // 4-digit year
10346 c:"var ty = parseInt(results[{0}], 10);\n"
10347 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
10352 c:"if (results[{0}] == 'am') {\n"
10353 + "if (h == 12) { h = 0; }\n"
10354 + "} else { if (h < 12) { h += 12; }}",
10359 c:"if (results[{0}] == 'AM') {\n"
10360 + "if (h == 12) { h = 0; }\n"
10361 + "} else { if (h < 12) { h += 12; }}",
10369 c:"h = parseInt(results[{0}], 10);\n",
10370 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
10377 c:"h = parseInt(results[{0}], 10);\n",
10378 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
10382 c:"i = parseInt(results[{0}], 10);\n",
10383 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
10387 c:"s = parseInt(results[{0}], 10);\n",
10388 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
10392 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
10393 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
10398 "o = results[{0}];",
10399 "var sn = o.substring(0,1),", // get + / - sign
10400 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
10401 "mn = o.substring(3,5) % 60;", // get minutes
10402 "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
10404 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
10409 "o = results[{0}];",
10410 "var sn = o.substring(0,1),", // get + / - sign
10411 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
10412 "mn = o.substring(4,6) % 60;", // get minutes
10413 "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
10415 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
10420 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
10424 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
10425 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
10426 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
10431 $f("Y", 1), // year
10432 $f("m", 2), // month
10434 $f("h", 4), // hour
10435 $f("i", 5), // minute
10436 $f("s", 6), // second
10437 {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)
10438 {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
10439 "if(results[8]) {", // timezone specified
10440 "if(results[8] == 'Z'){",
10442 "}else if (results[8].indexOf(':') > -1){",
10443 $f("P", 8).c, // timezone offset with colon separator
10445 $f("O", 8).c, // timezone offset without colon separator
10451 for (var i = 0, l = arr.length; i < l; ++i) {
10452 calc.push(arr[i].c);
10459 arr[0].s, // year (required)
10460 "(?:", "-", arr[1].s, // month (optional)
10461 "(?:", "-", arr[2].s, // day (optional)
10463 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
10464 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
10465 "(?::", arr[5].s, ")?", // seconds (optional)
10466 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
10467 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
10476 c:"u = parseInt(results[{0}], 10);\n",
10477 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
10484 Ext.apply(Date.prototype, {
10486 dateFormat : function(format) {
10487 if (Date.formatFunctions[format] == null) {
10488 Date.createFormat(format);
10490 return Date.formatFunctions[format].call(this);
10494 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
10496 * Note: The date string returned by the javascript Date object's toString() method varies
10497 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
10498 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
10499 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
10500 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
10501 * from the GMT offset portion of the date string.
10502 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
10504 getTimezone : function() {
10505 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
10507 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
10508 // 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)
10509 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
10510 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
10511 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
10513 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
10514 // step 1: (?:\((.*)\) -- find timezone in parentheses
10515 // 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
10516 // step 3: remove all non uppercase characters found in step 1 and 2
10517 return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
10521 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
10522 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
10523 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
10525 getGMTOffset : function(colon) {
10526 return (this.getTimezoneOffset() > 0 ? "-" : "+")
10527 + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
10528 + (colon ? ":" : "")
10529 + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
10533 * Get the numeric day number of the year, adjusted for leap year.
10534 * @return {Number} 0 to 364 (365 in leap years).
10536 getDayOfYear: function() {
10540 m = this.getMonth();
10542 for (i = 0, d.setMonth(0); i < m; d.setMonth(++i)) {
10543 num += d.getDaysInMonth();
10545 return num + this.getDate() - 1;
10549 * Get the numeric ISO-8601 week number of the year.
10550 * (equivalent to the format specifier 'W', but without a leading zero).
10551 * @return {Number} 1 to 53
10553 getWeekOfYear : function() {
10554 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
10555 var ms1d = 864e5, // milliseconds in a day
10556 ms7d = 7 * ms1d; // milliseconds in a week
10558 return function() { // return a closure so constants get calculated only once
10559 var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
10560 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
10561 Wyr = new Date(AWN * ms7d).getUTCFullYear();
10563 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
10568 * Checks if the current date falls within a leap year.
10569 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
10571 isLeapYear : function() {
10572 var year = this.getFullYear();
10573 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
10577 * Get the first day of the current month, adjusted for leap year. The returned value
10578 * is the numeric day index within the week (0-6) which can be used in conjunction with
10579 * the {@link #monthNames} array to retrieve the textual day name.
10582 var dt = new Date('1/10/2007');
10583 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
10585 * @return {Number} The day number (0-6).
10587 getFirstDayOfMonth : function() {
10588 var day = (this.getDay() - (this.getDate() - 1)) % 7;
10589 return (day < 0) ? (day + 7) : day;
10593 * Get the last day of the current month, adjusted for leap year. The returned value
10594 * is the numeric day index within the week (0-6) which can be used in conjunction with
10595 * the {@link #monthNames} array to retrieve the textual day name.
10598 var dt = new Date('1/10/2007');
10599 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
10601 * @return {Number} The day number (0-6).
10603 getLastDayOfMonth : function() {
10604 return this.getLastDateOfMonth().getDay();
10609 * Get the date of the first day of the month in which this date resides.
10612 getFirstDateOfMonth : function() {
10613 return new Date(this.getFullYear(), this.getMonth(), 1);
10617 * Get the date of the last day of the month in which this date resides.
10620 getLastDateOfMonth : function() {
10621 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
10625 * Get the number of days in the current month, adjusted for leap year.
10626 * @return {Number} The number of days in the month.
10628 getDaysInMonth: function() {
10629 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
10631 return function() { // return a closure for efficiency
10632 var m = this.getMonth();
10634 return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
10639 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
10640 * @return {String} 'st, 'nd', 'rd' or 'th'.
10642 getSuffix : function() {
10643 switch (this.getDate()) {
10660 * Creates and returns a new Date instance with the exact same date value as the called instance.
10661 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
10662 * variable will also be changed. When the intention is to create a new variable that will not
10663 * modify the original instance, you should create a clone.
10665 * Example of correctly cloning a date:
10668 var orig = new Date('10/1/2006');
10671 document.write(orig); //returns 'Thu Oct 05 2006'!
10674 var orig = new Date('10/1/2006');
10675 var copy = orig.clone();
10677 document.write(orig); //returns 'Thu Oct 01 2006'
10679 * @return {Date} The new Date instance.
10681 clone : function() {
10682 return new Date(this.getTime());
10686 * Checks if the current date is affected by Daylight Saving Time (DST).
10687 * @return {Boolean} True if the current date is affected by DST.
10689 isDST : function() {
10690 // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
10691 // courtesy of @geoffrey.mcgill
10692 return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
10696 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
10697 * automatically adjusting for Daylight Saving Time (DST) where applicable.
10698 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
10699 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
10700 * @return {Date} this or the clone.
10702 clearTime : function(clone) {
10704 return this.clone().clearTime();
10707 // get current date before clearing time
10708 var d = this.getDate();
10712 this.setMinutes(0);
10713 this.setSeconds(0);
10714 this.setMilliseconds(0);
10716 if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
10717 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
10718 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
10720 // increment hour until cloned date == current date
10721 for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
10724 this.setHours(c.getHours());
10731 * Provides a convenient method for performing basic date arithmetic. This method
10732 * does not modify the Date instance being called - it creates and returns
10733 * a new Date instance containing the resulting date value.
10738 var dt = new Date('10/29/2006').add(Date.DAY, 5);
10739 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
10741 // Negative values will be subtracted:
10742 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
10743 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
10745 // You can even chain several calls together in one line:
10746 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
10747 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
10750 * @param {String} interval A valid date interval enum value.
10751 * @param {Number} value The amount to add to the current date.
10752 * @return {Date} The new Date instance.
10754 add : function(interval, value) {
10755 var d = this.clone();
10756 if (!interval || value === 0) return d;
10758 switch(interval.toLowerCase()) {
10760 d.setMilliseconds(this.getMilliseconds() + value);
10763 d.setSeconds(this.getSeconds() + value);
10766 d.setMinutes(this.getMinutes() + value);
10769 d.setHours(this.getHours() + value);
10772 d.setDate(this.getDate() + value);
10775 var day = this.getDate();
10777 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
10780 d.setMonth(this.getMonth() + value);
10783 d.setFullYear(this.getFullYear() + value);
10790 * Checks if this date falls on or between the given start and end dates.
10791 * @param {Date} start Start date
10792 * @param {Date} end End date
10793 * @return {Boolean} true if this date falls on or between the given start and end dates.
10795 between : function(start, end) {
10796 var t = this.getTime();
10797 return start.getTime() <= t && t <= end.getTime();
10803 * Formats a date given the supplied format string.
10804 * @param {String} format The format string.
10805 * @return {String} The formatted date.
10808 Date.prototype.format = Date.prototype.dateFormat;
10812 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
10813 Ext.apply(Date.prototype, {
10814 _xMonth : Date.prototype.setMonth,
10815 _xDate : Date.prototype.setDate,
10817 // Bug in Safari 1.3, 2.0 (WebKit build < 420)
10818 // Date.setMonth does not work consistently if iMonth is not 0-11
10819 setMonth : function(num) {
10821 var n = Math.ceil(-num),
10822 back_year = Math.ceil(n / 12),
10823 month = (n % 12) ? 12 - n % 12 : 0;
10825 this.setFullYear(this.getFullYear() - back_year);
10827 return this._xMonth(month);
10829 return this._xMonth(num);
10833 // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
10834 // The parameter for Date.setDate() is converted to a signed byte integer in Safari
10835 // http://brianary.blogspot.com/2006/03/safari-date-bug.html
10836 setDate : function(d) {
10837 // use setTime() to workaround setDate() bug
10838 // subtract current day of month in milliseconds, then add desired day of month in milliseconds
10839 return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
10846 /* Some basic Date tests... (requires Firebug)
10848 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
10849 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
10852 console.group('Standard Date.parseDate() Tests');
10853 console.log('Date.parseDate("2009-01-05T11:38:56", "c") = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
10854 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
10855 console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c") = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
10856 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
10857 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
10858 console.groupEnd();
10860 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
10861 // -- accepts ALL 6 levels of date-time granularity
10862 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
10863 console.log('Date.parseDate("1997", "c") = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
10864 console.log('Date.parseDate("1997-07", "c") = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
10865 console.log('Date.parseDate("1997-07-16", "c") = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
10866 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)
10867 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)
10868 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)
10869 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)
10870 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
10871 console.groupEnd();
10874 * @class Ext.util.DelayedTask
10875 * <p> The DelayedTask class provides a convenient way to "buffer" the execution of a method,
10876 * performing setTimeout where a new timeout cancels the old timeout. When called, the
10877 * task will wait the specified time period before executing. If durng that time period,
10878 * the task is called again, the original call will be cancelled. This continues so that
10879 * the function is only called a single time for each iteration.</p>
10880 * <p>This method is especially useful for things like detecting whether a user has finished
10881 * typing in a text field. An example would be performing validation on a keypress. You can
10882 * use this class to buffer the keypress events for a certain number of milliseconds, and
10883 * perform only if they stop for that amount of time. Usage:</p><pre><code>
10884 var task = new Ext.util.DelayedTask(function(){
10885 alert(Ext.getDom('myInputField').value.length);
10887 // Wait 500ms before calling our function. If the user presses another key
10888 // during that 500ms, it will be cancelled and we'll wait another 500ms.
10889 Ext.get('myInputField').on('keypress', function(){
10890 task.{@link #delay}(500);
10893 * <p>Note that we are using a DelayedTask here to illustrate a point. The configuration
10894 * option <tt>buffer</tt> for {@link Ext.util.Observable#addListener addListener/on} will
10895 * also setup a delayed task for you to buffer events.</p>
10896 * @constructor The parameters to this constructor serve as defaults and are not required.
10897 * @param {Function} fn (optional) The default function to timeout
10898 * @param {Object} scope (optional) The default scope of that timeout
10899 * @param {Array} args (optional) The default Array of arguments
10901 Ext.util.DelayedTask = function(fn, scope, args){
10907 fn.apply(scope, args || []);
10911 * Cancels any pending timeout and queues a new one
10912 * @param {Number} delay The milliseconds to delay
10913 * @param {Function} newFn (optional) Overrides function passed to constructor
10914 * @param {Object} newScope (optional) Overrides scope passed to constructor
10915 * @param {Array} newArgs (optional) Overrides args passed to constructor
10917 me.delay = function(delay, newFn, newScope, newArgs){
10920 scope = newScope || scope;
10921 args = newArgs || args;
10922 id = setInterval(call, delay);
10926 * Cancel the last queued timeout
10928 me.cancel = function(){
10935 * @class Ext.util.MixedCollection
\r
10936 * @extends Ext.util.Observable
\r
10937 * A Collection class that maintains both numeric indexes and keys and exposes events.
\r
10939 * @param {Boolean} allowFunctions True if the addAll function should add function references to the
\r
10940 * collection (defaults to false)
\r
10941 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
\r
10942 * and return the key value for that item. This is used when available to look up the key on items that
\r
10943 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
\r
10944 * equivalent to providing an implementation for the {@link #getKey} method.
\r
10946 Ext.util.MixedCollection = function(allowFunctions, keyFn){
\r
10954 * Fires when the collection is cleared.
\r
10959 * Fires when an item is added to the collection.
\r
10960 * @param {Number} index The index at which the item was added.
\r
10961 * @param {Object} o The item added.
\r
10962 * @param {String} key The key associated with the added item.
\r
10967 * Fires when an item is replaced in the collection.
\r
10968 * @param {String} key he key associated with the new added.
\r
10969 * @param {Object} old The item being replaced.
\r
10970 * @param {Object} new The new item.
\r
10975 * Fires when an item is removed from the collection.
\r
10976 * @param {Object} o The item being removed.
\r
10977 * @param {String} key (optional) The key associated with the removed item.
\r
10982 this.allowFunctions = allowFunctions === true;
\r
10984 this.getKey = keyFn;
\r
10986 Ext.util.MixedCollection.superclass.constructor.call(this);
\r
10989 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
\r
10990 allowFunctions : false,
\r
10993 * Adds an item to the collection. Fires the {@link #add} event when complete.
\r
10994 * @param {String} key <p>The key to associate with the item, or the new item.</p>
\r
10995 * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
\r
10996 * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
\r
10997 * will be able to <i>derive</i> the key for the new item. In this case just pass the new item in
\r
10998 * this parameter.</p>
\r
10999 * @param {Object} o The item to add.
\r
11000 * @return {Object} The item added.
\r
11002 add: function(key, o){
\r
11003 if(arguments.length == 1){
\r
11004 o = arguments[0];
\r
11005 key = this.getKey(o);
\r
11007 if(typeof key != 'undefined' && key !== null){
\r
11008 var old = this.map[key];
\r
11009 if(typeof old != 'undefined'){
\r
11010 return this.replace(key, o);
\r
11012 this.map[key] = o;
\r
11015 this.items.push(o);
\r
11016 this.keys.push(key);
\r
11017 this.fireEvent('add', this.length-1, o, key);
\r
11022 * MixedCollection has a generic way to fetch keys if you implement getKey. The default implementation
\r
11023 * simply returns <tt style="font-weight:bold;">item.id</tt> but you can provide your own implementation
\r
11024 * to return a different value as in the following examples:
\r
11027 var mc = new Ext.util.MixedCollection();
\r
11028 mc.add(someEl.dom.id, someEl);
\r
11029 mc.add(otherEl.dom.id, otherEl);
\r
11033 var mc = new Ext.util.MixedCollection();
\r
11034 mc.getKey = function(el){
\r
11035 return el.dom.id;
\r
11040 // or via the constructor
\r
11041 var mc = new Ext.util.MixedCollection(false, function(el){
\r
11042 return el.dom.id;
\r
11047 * @param {Object} item The item for which to find the key.
\r
11048 * @return {Object} The key for the passed item.
\r
11050 getKey : function(o){
\r
11055 * Replaces an item in the collection. Fires the {@link #replace} event when complete.
\r
11056 * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
\r
11057 * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
\r
11058 * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
\r
11059 * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
\r
11060 * with one having the same key value, then just pass the replacement item in this parameter.</p>
\r
11061 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
\r
11063 * @return {Object} The new item.
\r
11065 replace : function(key, o){
\r
11066 if(arguments.length == 1){
\r
11067 o = arguments[0];
\r
11068 key = this.getKey(o);
\r
11070 var old = this.map[key];
\r
11071 if(typeof key == "undefined" || key === null || typeof old == "undefined"){
\r
11072 return this.add(key, o);
\r
11074 var index = this.indexOfKey(key);
\r
11075 this.items[index] = o;
\r
11076 this.map[key] = o;
\r
11077 this.fireEvent("replace", key, old, o);
\r
11082 * Adds all elements of an Array or an Object to the collection.
\r
11083 * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
\r
11084 * an Array of values, each of which are added to the collection.
\r
11086 addAll : function(objs){
\r
11087 if(arguments.length > 1 || Ext.isArray(objs)){
\r
11088 var args = arguments.length > 1 ? arguments : objs;
\r
11089 for(var i = 0, len = args.length; i < len; i++){
\r
11090 this.add(args[i]);
\r
11093 for(var key in objs){
\r
11094 if(this.allowFunctions || typeof objs[key] != "function"){
\r
11095 this.add(key, objs[key]);
\r
11102 * Executes the specified function once for every item in the collection, passing the following arguments:
\r
11103 * <div class="mdetail-params"><ul>
\r
11104 * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
\r
11105 * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
\r
11106 * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
\r
11108 * The function should return a boolean value. Returning false from the function will stop the iteration.
\r
11109 * @param {Function} fn The function to execute for each item.
\r
11110 * @param {Object} scope (optional) The scope in which to execute the function.
\r
11112 each : function(fn, scope){
\r
11113 var items = [].concat(this.items); // each safe for removal
\r
11114 for(var i = 0, len = items.length; i < len; i++){
\r
11115 if(fn.call(scope || items[i], items[i], i, len) === false){
\r
11122 * Executes the specified function once for every key in the collection, passing each
\r
11123 * key, and its associated item as the first two parameters.
\r
11124 * @param {Function} fn The function to execute for each item.
\r
11125 * @param {Object} scope (optional) The scope in which to execute the function.
\r
11127 eachKey : function(fn, scope){
\r
11128 for(var i = 0, len = this.keys.length; i < len; i++){
\r
11129 fn.call(scope || window, this.keys[i], this.items[i], i, len);
\r
11134 * Returns the first item in the collection which elicits a true return value from the
\r
11135 * passed selection function.
\r
11136 * @param {Function} fn The selection function to execute for each item.
\r
11137 * @param {Object} scope (optional) The scope in which to execute the function.
\r
11138 * @return {Object} The first item in the collection which returned true from the selection function.
\r
11140 find : function(fn, scope){
\r
11141 for(var i = 0, len = this.items.length; i < len; i++){
\r
11142 if(fn.call(scope || window, this.items[i], this.keys[i])){
\r
11143 return this.items[i];
\r
11150 * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
\r
11151 * @param {Number} index The index to insert the item at.
\r
11152 * @param {String} key The key to associate with the new item, or the item itself.
\r
11153 * @param {Object} o (optional) If the second parameter was a key, the new item.
\r
11154 * @return {Object} The item inserted.
\r
11156 insert : function(index, key, o){
\r
11157 if(arguments.length == 2){
\r
11158 o = arguments[1];
\r
11159 key = this.getKey(o);
\r
11161 if(this.containsKey(key)){
\r
11162 this.suspendEvents();
\r
11163 this.removeKey(key);
\r
11164 this.resumeEvents();
\r
11166 if(index >= this.length){
\r
11167 return this.add(key, o);
\r
11170 this.items.splice(index, 0, o);
\r
11171 if(typeof key != "undefined" && key !== null){
\r
11172 this.map[key] = o;
\r
11174 this.keys.splice(index, 0, key);
\r
11175 this.fireEvent("add", index, o, key);
\r
11180 * Remove an item from the collection.
\r
11181 * @param {Object} o The item to remove.
\r
11182 * @return {Object} The item removed or false if no item was removed.
\r
11184 remove : function(o){
\r
11185 return this.removeAt(this.indexOf(o));
\r
11189 * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
\r
11190 * @param {Number} index The index within the collection of the item to remove.
\r
11191 * @return {Object} The item removed or false if no item was removed.
\r
11193 removeAt : function(index){
\r
11194 if(index < this.length && index >= 0){
\r
11196 var o = this.items[index];
\r
11197 this.items.splice(index, 1);
\r
11198 var key = this.keys[index];
\r
11199 if(typeof key != "undefined"){
\r
11200 delete this.map[key];
\r
11202 this.keys.splice(index, 1);
\r
11203 this.fireEvent("remove", o, key);
\r
11210 * Removed an item associated with the passed key fom the collection.
\r
11211 * @param {String} key The key of the item to remove.
\r
11212 * @return {Object} The item removed or false if no item was removed.
\r
11214 removeKey : function(key){
\r
11215 return this.removeAt(this.indexOfKey(key));
\r
11219 * Returns the number of items in the collection.
\r
11220 * @return {Number} the number of items in the collection.
\r
11222 getCount : function(){
\r
11223 return this.length;
\r
11227 * Returns index within the collection of the passed Object.
\r
11228 * @param {Object} o The item to find the index of.
\r
11229 * @return {Number} index of the item. Returns -1 if not found.
\r
11231 indexOf : function(o){
\r
11232 return this.items.indexOf(o);
\r
11236 * Returns index within the collection of the passed key.
\r
11237 * @param {String} key The key to find the index of.
\r
11238 * @return {Number} index of the key.
\r
11240 indexOfKey : function(key){
\r
11241 return this.keys.indexOf(key);
\r
11245 * Returns the item associated with the passed key OR index. Key has priority over index. This is the equivalent
\r
11246 * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
\r
11247 * @param {String/Number} key The key or index of the item.
\r
11248 * @return {Object} If the item is found, returns the item. If the item was not found, returns <tt>undefined</tt>.
\r
11249 * If an item was found, but is a Class, returns <tt>null</tt>.
\r
11251 item : function(key){
\r
11252 var mk = this.map[key],
\r
11253 item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
\r
11254 return !Ext.isFunction(item) || this.allowFunctions ? item : null; // for prototype!
\r
11258 * Returns the item at the specified index.
\r
11259 * @param {Number} index The index of the item.
\r
11260 * @return {Object} The item at the specified index.
\r
11262 itemAt : function(index){
\r
11263 return this.items[index];
\r
11267 * Returns the item associated with the passed key.
\r
11268 * @param {String/Number} key The key of the item.
\r
11269 * @return {Object} The item associated with the passed key.
\r
11271 key : function(key){
\r
11272 return this.map[key];
\r
11276 * Returns true if the collection contains the passed Object as an item.
\r
11277 * @param {Object} o The Object to look for in the collection.
\r
11278 * @return {Boolean} True if the collection contains the Object as an item.
\r
11280 contains : function(o){
\r
11281 return this.indexOf(o) != -1;
\r
11285 * Returns true if the collection contains the passed Object as a key.
\r
11286 * @param {String} key The key to look for in the collection.
\r
11287 * @return {Boolean} True if the collection contains the Object as a key.
\r
11289 containsKey : function(key){
\r
11290 return typeof this.map[key] != "undefined";
\r
11294 * Removes all items from the collection. Fires the {@link #clear} event when complete.
\r
11296 clear : function(){
\r
11301 this.fireEvent("clear");
\r
11305 * Returns the first item in the collection.
\r
11306 * @return {Object} the first item in the collection..
\r
11308 first : function(){
\r
11309 return this.items[0];
\r
11313 * Returns the last item in the collection.
\r
11314 * @return {Object} the last item in the collection..
\r
11316 last : function(){
\r
11317 return this.items[this.length-1];
\r
11321 _sort : function(property, dir, fn){
\r
11324 dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1,
\r
11325 c = [], k = this.keys, items = this.items;
\r
11327 fn = fn || function(a, b){
\r
11330 for(i = 0, len = items.length; i < len; i++){
\r
11331 c[c.length] = {key: k[i], value: items[i], index: i};
\r
11333 c.sort(function(a, b){
\r
11334 var v = fn(a[property], b[property]) * dsc;
\r
11336 v = (a.index < b.index ? -1 : 1);
\r
11340 for(i = 0, len = c.length; i < len; i++){
\r
11341 items[i] = c[i].value;
\r
11344 this.fireEvent("sort", this);
\r
11348 * Sorts this collection with the passed comparison function
\r
11349 * @param {String} direction (optional) "ASC" or "DESC"
\r
11350 * @param {Function} fn (optional) comparison function
\r
11352 sort : function(dir, fn){
\r
11353 this._sort("value", dir, fn);
\r
11357 * Sorts this collection by keys
\r
11358 * @param {String} direction (optional) "ASC" or "DESC"
\r
11359 * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
\r
11361 keySort : function(dir, fn){
\r
11362 this._sort("key", dir, fn || function(a, b){
\r
11363 var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
\r
11364 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
\r
11369 * Returns a range of items in this collection
\r
11370 * @param {Number} startIndex (optional) defaults to 0
\r
11371 * @param {Number} endIndex (optional) default to the last item
\r
11372 * @return {Array} An array of items
\r
11374 getRange : function(start, end){
\r
11375 var items = this.items;
\r
11376 if(items.length < 1){
\r
11379 start = start || 0;
\r
11380 end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
\r
11382 if(start <= end){
\r
11383 for(i = start; i <= end; i++) {
\r
11384 r[r.length] = items[i];
\r
11387 for(i = start; i >= end; i--) {
\r
11388 r[r.length] = items[i];
\r
11395 * Filter the <i>objects</i> in this collection by a specific property.
\r
11396 * Returns a new collection that has been filtered.
\r
11397 * @param {String} property A property on your objects
\r
11398 * @param {String/RegExp} value Either string that the property values
\r
11399 * should start with or a RegExp to test against the property
\r
11400 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
\r
11401 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
\r
11402 * @return {MixedCollection} The new filtered collection
\r
11404 filter : function(property, value, anyMatch, caseSensitive){
\r
11405 if(Ext.isEmpty(value, false)){
\r
11406 return this.clone();
\r
11408 value = this.createValueMatcher(value, anyMatch, caseSensitive);
\r
11409 return this.filterBy(function(o){
\r
11410 return o && value.test(o[property]);
\r
11415 * Filter by a function. Returns a <i>new</i> collection that has been filtered.
\r
11416 * The passed function will be called with each object in the collection.
\r
11417 * If the function returns true, the value is included otherwise it is filtered.
\r
11418 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
\r
11419 * @param {Object} scope (optional) The scope of the function (defaults to this)
\r
11420 * @return {MixedCollection} The new filtered collection
\r
11422 filterBy : function(fn, scope){
\r
11423 var r = new Ext.util.MixedCollection();
\r
11424 r.getKey = this.getKey;
\r
11425 var k = this.keys, it = this.items;
\r
11426 for(var i = 0, len = it.length; i < len; i++){
\r
11427 if(fn.call(scope||this, it[i], k[i])){
\r
11428 r.add(k[i], it[i]);
\r
11435 * Finds the index of the first matching object in this collection by a specific property/value.
\r
11436 * @param {String} property The name of a property on your objects.
\r
11437 * @param {String/RegExp} value A string that the property values
\r
11438 * should start with or a RegExp to test against the property.
\r
11439 * @param {Number} start (optional) The index to start searching at (defaults to 0).
\r
11440 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
\r
11441 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
\r
11442 * @return {Number} The matched index or -1
\r
11444 findIndex : function(property, value, start, anyMatch, caseSensitive){
\r
11445 if(Ext.isEmpty(value, false)){
\r
11448 value = this.createValueMatcher(value, anyMatch, caseSensitive);
\r
11449 return this.findIndexBy(function(o){
\r
11450 return o && value.test(o[property]);
\r
11455 * Find the index of the first matching object in this collection by a function.
\r
11456 * If the function returns <i>true</i> it is considered a match.
\r
11457 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
\r
11458 * @param {Object} scope (optional) The scope of the function (defaults to this).
\r
11459 * @param {Number} start (optional) The index to start searching at (defaults to 0).
\r
11460 * @return {Number} The matched index or -1
\r
11462 findIndexBy : function(fn, scope, start){
\r
11463 var k = this.keys, it = this.items;
\r
11464 for(var i = (start||0), len = it.length; i < len; i++){
\r
11465 if(fn.call(scope||this, it[i], k[i])){
\r
11473 createValueMatcher : function(value, anyMatch, caseSensitive){
\r
11474 if(!value.exec){ // not a regex
\r
11475 value = String(value);
\r
11476 value = new RegExp((anyMatch === true ? '' : '^') + Ext.escapeRe(value), caseSensitive ? '' : 'i');
\r
11482 * Creates a shallow copy of this collection
\r
11483 * @return {MixedCollection}
\r
11485 clone : function(){
\r
11486 var r = new Ext.util.MixedCollection();
\r
11487 var k = this.keys, it = this.items;
\r
11488 for(var i = 0, len = it.length; i < len; i++){
\r
11489 r.add(k[i], it[i]);
\r
11491 r.getKey = this.getKey;
\r
11496 * This method calls {@link #item item()}.
\r
11497 * Returns the item associated with the passed key OR index. Key has priority over index. This is the equivalent
\r
11498 * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
\r
11499 * @param {String/Number} key The key or index of the item.
\r
11500 * @return {Object} If the item is found, returns the item. If the item was not found, returns <tt>undefined</tt>.
\r
11501 * If an item was found, but is a Class, returns <tt>null</tt>.
\r
11503 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;/**
11504 * @class Ext.util.JSON
11505 * Modified version of Douglas Crockford"s json.js that doesn"t
11506 * mess with the Object prototype
11507 * http://www.json.org/js.html
11510 Ext.util.JSON = new (function(){
11511 var useHasOwn = !!{}.hasOwnProperty,
11512 isNative = function() {
11513 var useNative = null;
11515 return function() {
11516 if (useNative === null) {
11517 useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
11523 pad = function(n) {
11524 return n < 10 ? "0" + n : n;
11526 doDecode = function(json){
11527 return eval("(" + json + ')');
11529 doEncode = function(o){
11530 if(typeof o == "undefined" || o === null){
11532 }else if(Ext.isArray(o)){
11533 return encodeArray(o);
11534 }else if(Object.prototype.toString.apply(o) === '[object Date]'){
11535 return Ext.util.JSON.encodeDate(o);
11536 }else if(typeof o == "string"){
11537 return encodeString(o);
11538 }else if(typeof o == "number"){
11539 return isFinite(o) ? String(o) : "null";
11540 }else if(typeof o == "boolean"){
11543 var a = ["{"], b, i, v;
11545 if(!useHasOwn || o.hasOwnProperty(i)) {
11547 switch (typeof v) {
11556 a.push(doEncode(i), ":",
11557 v === null ? "null" : doEncode(v));
11575 encodeString = function(s){
11576 if (/["\\\x00-\x1f]/.test(s)) {
11577 return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
11582 c = b.charCodeAt();
11584 Math.floor(c / 16).toString(16) +
11585 (c % 16).toString(16);
11588 return '"' + s + '"';
11590 encodeArray = function(o){
11591 var a = ["["], b, i, l = o.length, v;
11592 for (i = 0; i < l; i += 1) {
11594 switch (typeof v) {
11603 a.push(v === null ? "null" : Ext.util.JSON.encode(v));
11611 this.encodeDate = function(o){
11612 return '"' + o.getFullYear() + "-" +
11613 pad(o.getMonth() + 1) + "-" +
11614 pad(o.getDate()) + "T" +
11615 pad(o.getHours()) + ":" +
11616 pad(o.getMinutes()) + ":" +
11617 pad(o.getSeconds()) + '"';
11621 * Encodes an Object, Array or other value
11622 * @param {Mixed} o The variable to encode
11623 * @return {String} The JSON string
11625 this.encode = function() {
11627 return function(o) {
11629 // setup encoding function on first access
11630 ec = isNative() ? JSON.stringify : doEncode;
11638 * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError unless the safe option is set.
11639 * @param {String} json The JSON string
11640 * @return {Object} The resulting object
11642 this.decode = function() {
11644 return function(json) {
11646 // setup decoding function on first access
11647 dc = isNative() ? JSON.parse : doDecode;
11655 * Shorthand for {@link Ext.util.JSON#encode}
11656 * @param {Mixed} o The variable to encode
11657 * @return {String} The JSON string
11661 Ext.encode = Ext.util.JSON.encode;
11663 * Shorthand for {@link Ext.util.JSON#decode}
11664 * @param {String} json The JSON string
11665 * @param {Boolean} safe (optional) Whether to return null or throw an exception if the JSON is invalid.
11666 * @return {Object} The resulting object
11670 Ext.decode = Ext.util.JSON.decode;
11672 * @class Ext.util.Format
\r
11673 * Reusable data formatting functions
\r
11676 Ext.util.Format = function(){
\r
11677 var trimRe = /^\s+|\s+$/g;
\r
11680 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
\r
11681 * @param {String} value The string to truncate
\r
11682 * @param {Number} length The maximum length to allow before truncating
\r
11683 * @param {Boolean} word True to try to find a common work break
\r
11684 * @return {String} The converted text
\r
11686 ellipsis : function(value, len, word){
\r
11687 if(value && value.length > len){
\r
11689 var vs = value.substr(0, len - 2);
\r
11690 var index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
\r
11691 if(index == -1 || index < (len - 15)){
\r
11692 return value.substr(0, len - 3) + "...";
\r
11694 return vs.substr(0, index) + "...";
\r
11697 return value.substr(0, len - 3) + "...";
\r
11704 * Checks a reference and converts it to empty string if it is undefined
\r
11705 * @param {Mixed} value Reference to check
\r
11706 * @return {Mixed} Empty string if converted, otherwise the original value
\r
11708 undef : function(value){
\r
11709 return value !== undefined ? value : "";
\r
11713 * Checks a reference and converts it to the default value if it's empty
\r
11714 * @param {Mixed} value Reference to check
\r
11715 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
\r
11716 * @return {String}
\r
11718 defaultValue : function(value, defaultValue){
\r
11719 return value !== undefined && value !== '' ? value : defaultValue;
\r
11723 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
\r
11724 * @param {String} value The string to encode
\r
11725 * @return {String} The encoded text
\r
11727 htmlEncode : function(value){
\r
11728 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
\r
11732 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
\r
11733 * @param {String} value The string to decode
\r
11734 * @return {String} The decoded text
\r
11736 htmlDecode : function(value){
\r
11737 return !value ? value : String(value).replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&");
\r
11741 * Trims any whitespace from either side of a string
\r
11742 * @param {String} value The text to trim
\r
11743 * @return {String} The trimmed text
\r
11745 trim : function(value){
\r
11746 return String(value).replace(trimRe, "");
\r
11750 * Returns a substring from within an original string
\r
11751 * @param {String} value The original text
\r
11752 * @param {Number} start The start index of the substring
\r
11753 * @param {Number} length The length of the substring
\r
11754 * @return {String} The substring
\r
11756 substr : function(value, start, length){
\r
11757 return String(value).substr(start, length);
\r
11761 * Converts a string to all lower case letters
\r
11762 * @param {String} value The text to convert
\r
11763 * @return {String} The converted text
\r
11765 lowercase : function(value){
\r
11766 return String(value).toLowerCase();
\r
11770 * Converts a string to all upper case letters
\r
11771 * @param {String} value The text to convert
\r
11772 * @return {String} The converted text
\r
11774 uppercase : function(value){
\r
11775 return String(value).toUpperCase();
\r
11779 * Converts the first character only of a string to upper case
\r
11780 * @param {String} value The text to convert
\r
11781 * @return {String} The converted text
\r
11783 capitalize : function(value){
\r
11784 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
\r
11788 call : function(value, fn){
\r
11789 if(arguments.length > 2){
\r
11790 var args = Array.prototype.slice.call(arguments, 2);
\r
11791 args.unshift(value);
\r
11792 return eval(fn).apply(window, args);
\r
11794 return eval(fn).call(window, value);
\r
11799 * Format a number as US currency
\r
11800 * @param {Number/String} value The numeric value to format
\r
11801 * @return {String} The formatted currency string
\r
11803 usMoney : function(v){
\r
11804 v = (Math.round((v-0)*100))/100;
\r
11805 v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
\r
11807 var ps = v.split('.');
\r
11808 var whole = ps[0];
\r
11809 var sub = ps[1] ? '.'+ ps[1] : '.00';
\r
11810 var r = /(\d+)(\d{3})/;
\r
11811 while (r.test(whole)) {
\r
11812 whole = whole.replace(r, '$1' + ',' + '$2');
\r
11815 if(v.charAt(0) == '-'){
\r
11816 return '-$' + v.substr(1);
\r
11822 * Parse a value into a formatted date using the specified format pattern.
\r
11823 * @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
11824 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
\r
11825 * @return {String} The formatted date string
\r
11827 date : function(v, format){
\r
11831 if(!Ext.isDate(v)){
\r
11832 v = new Date(Date.parse(v));
\r
11834 return v.dateFormat(format || "m/d/Y");
\r
11838 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
\r
11839 * @param {String} format Any valid date format string
\r
11840 * @return {Function} The date formatting function
\r
11842 dateRenderer : function(format){
\r
11843 return function(v){
\r
11844 return Ext.util.Format.date(v, format);
\r
11849 stripTagsRE : /<\/?[^>]+>/gi,
\r
11852 * Strips all HTML tags
\r
11853 * @param {Mixed} value The text from which to strip tags
\r
11854 * @return {String} The stripped text
\r
11856 stripTags : function(v){
\r
11857 return !v ? v : String(v).replace(this.stripTagsRE, "");
\r
11860 stripScriptsRe : /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
\r
11863 * Strips all script tags
\r
11864 * @param {Mixed} value The text from which to strip script tags
\r
11865 * @return {String} The stripped text
\r
11867 stripScripts : function(v){
\r
11868 return !v ? v : String(v).replace(this.stripScriptsRe, "");
\r
11872 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
\r
11873 * @param {Number/String} size The numeric value to format
\r
11874 * @return {String} The formatted file size
\r
11876 fileSize : function(size){
\r
11877 if(size < 1024) {
\r
11878 return size + " bytes";
\r
11879 } else if(size < 1048576) {
\r
11880 return (Math.round(((size*10) / 1024))/10) + " KB";
\r
11882 return (Math.round(((size*10) / 1048576))/10) + " MB";
\r
11887 * It does simple math for use in a template, for example:<pre><code>
\r
11888 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
\r
11890 * @return {Function} A function that operates on the passed value.
\r
11892 math : function(){
\r
11894 return function(v, a){
\r
11896 fns[a] = new Function('v', 'return v ' + a + ';');
\r
11898 return fns[a](v);
\r
11903 * Rounds the passed number to the required decimal precision.
\r
11904 * @param {Number/String} value The numeric value to round.
\r
11905 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
\r
11906 * @return {Number} The rounded value.
\r
11908 round : function(value, precision) {
\r
11909 var result = Number(value);
\r
11910 if (typeof precision == 'number') {
\r
11911 precision = Math.pow(10, precision);
\r
11912 result = Math.round(value * precision) / precision;
\r
11918 * Formats the number according to the format string.
\r
11919 * <div style="margin-left:40px">examples (123456.789):
\r
11920 * <div style="margin-left:10px">
\r
11921 * 0 - (123456) show only digits, no precision<br>
\r
11922 * 0.00 - (123456.78) show only digits, 2 precision<br>
\r
11923 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
\r
11924 * 0,000 - (123,456) show comma and digits, no precision<br>
\r
11925 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
\r
11926 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
\r
11927 * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
\r
11928 * For example: 0.000,00/i
\r
11930 * @param {Number} v The number to format.
\r
11931 * @param {String} format The way you would like to format this text.
\r
11932 * @return {String} The formatted number.
\r
11934 number: function(v, format) {
\r
11938 v = Ext.num(v, NaN);
\r
11948 if(format.substr(format.length - 2) == '/i'){
\r
11949 format = format.substr(0, format.length - 2);
\r
11955 var hasComma = format.indexOf(comma) != -1,
\r
11956 psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
\r
11958 if(1 < psplit.length){
\r
11959 v = v.toFixed(psplit[1].length);
\r
11960 }else if(2 < psplit.length){
\r
11961 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
\r
11963 v = v.toFixed(0);
\r
11966 var fnum = v.toString();
\r
11968 psplit = fnum.split('.');
\r
11970 var cnum = psplit[0], parr = [], j = cnum.length, m = Math.floor(j / 3), n = cnum.length % 3 || 3;
\r
11972 for(var i = 0; i < j; i += n){
\r
11976 parr[parr.length] = cnum.substr(i, n);
\r
11979 fnum = parr.join(comma);
\r
11981 fnum += dec + psplit[1];
\r
11985 return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
\r
11989 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
\r
11990 * @param {String} format Any valid number format string for {@link #number}
\r
11991 * @return {Function} The number formatting function
\r
11993 numberRenderer : function(format){
\r
11994 return function(v){
\r
11995 return Ext.util.Format.number(v, format);
\r
12000 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
\r
12001 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
\r
12002 * if the value is 0 or greater than 1.
\r
12003 * @param {Number} value The value to compare against
\r
12004 * @param {String} singular The singular form of the word
\r
12005 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
\r
12007 plural : function(v, s, p){
\r
12008 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
\r
12012 * Converts newline characters to the HTML tag <br/>
\r
12013 * @param {String} The string value to format.
\r
12014 * @return {String} The string with embedded <br/> tags in place of newlines.
\r
12016 nl2br : function(v){
\r
12017 return v === undefined || v === null ? '' : v.replace(/\n/g, '<br/>');
\r
12021 * @class Ext.XTemplate
12022 * @extends Ext.Template
12023 * <p>A template class that supports advanced functionality like autofilling arrays, conditional processing with
12024 * basic comparison operators, sub-templates, basic math function support, special built-in template variables,
12025 * inline code execution and more. XTemplate also provides the templating mechanism built into {@link Ext.DataView}.</p>
12026 * <p>XTemplate supports many special tags and built-in operators that aren't defined as part of the API, but are
12027 * supported in the templates that can be created. The following examples demonstrate all of the supported features.
12028 * This is the data object used for reference in each code example:</p>
12031 name: 'Jack Slocum',
12032 title: 'Lead Developer',
12033 company: 'Ext JS, LLC',
12034 email: 'jack@extjs.com',
12035 address: '4 Red Bulls Drive',
12039 drinks: ['Red Bull', 'Coffee', 'Water'],
12041 name: 'Sara Grace',
12047 name: 'John James',
12052 * <p><b>Auto filling of arrays</b><br/>The <tt>tpl</tt> tag and the <tt>for</tt> operator are used
12053 * to process the provided data object. If <tt>for="."</tt> is specified, the data object provided
12054 * is examined. If the variable in <tt>for</tt> is an array, it will auto-fill, repeating the template
12055 * block inside the <tt>tpl</tt> tag for each item in the array:</p>
12057 var tpl = new Ext.XTemplate(
12059 '<tpl for=".">',
12060 '<p>{name}</p>',
12063 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
12065 * <p><b>Scope switching</b><br/>The <tt>for</tt> property can be leveraged to access specified members
12066 * of the provided data object to populate the template:</p>
12068 var tpl = new Ext.XTemplate(
12069 '<p>Name: {name}</p>',
12070 '<p>Title: {title}</p>',
12071 '<p>Company: {company}</p>',
12073 '<tpl <b>for="kids"</b>>', // interrogate the kids property within the data
12074 '<p>{name}</p>',
12077 tpl.overwrite(panel.body, data);
12079 * <p><b>Access to parent object from within sub-template scope</b><br/>When processing a sub-template, for example while
12080 * looping through a child array, you can access the parent object's members via the <tt>parent</tt> object:</p>
12082 var tpl = new Ext.XTemplate(
12083 '<p>Name: {name}</p>',
12085 '<tpl for="kids">',
12086 '<tpl if="age &gt; 1">', // <-- Note that the > is encoded
12087 '<p>{name}</p>',
12088 '<p>Dad: {parent.name}</p>',
12092 tpl.overwrite(panel.body, data);
12094 * <p><b>Array item index and basic math support</b> <br/>While processing an array, the special variable <tt>{#}</tt>
12095 * will provide the current array index + 1 (starts at 1, not 0). Templates also support the basic math operators
12096 * + - * and / that can be applied directly on numeric data values:</p>
12098 var tpl = new Ext.XTemplate(
12099 '<p>Name: {name}</p>',
12101 '<tpl for="kids">',
12102 '<tpl if="age &gt; 1">', // <-- Note that the > is encoded
12103 '<p>{#}: {name}</p>', // <-- Auto-number each item
12104 '<p>In 5 Years: {age+5}</p>', // <-- Basic math
12105 '<p>Dad: {parent.name}</p>',
12109 tpl.overwrite(panel.body, data);
12111 * <p><b>Auto-rendering of flat arrays</b> <br/>Flat arrays that contain values (and not objects) can be auto-rendered
12112 * using the special <tt>{.}</tt> variable inside a loop. This variable will represent the value of
12113 * the array at the current index:</p>
12115 var tpl = new Ext.XTemplate(
12116 '<p>{name}\'s favorite beverages:</p>',
12117 '<tpl for="drinks">',
12118 '<div> - {.}</div>',
12121 tpl.overwrite(panel.body, data);
12123 * <p><b>Basic conditional logic</b> <br/>Using the <tt>tpl</tt> tag and the <tt>if</tt>
12124 * operator you can provide conditional checks for deciding whether or not to render specific parts of the template.
12125 * Note that there is no <tt>else</tt> operator — if needed, you should use two opposite <tt>if</tt> statements.
12126 * Properly-encoded attributes are required as seen in the following example:</p>
12128 var tpl = new Ext.XTemplate(
12129 '<p>Name: {name}</p>',
12131 '<tpl for="kids">',
12132 '<tpl if="age &gt; 1">', // <-- Note that the > is encoded
12133 '<p>{name}</p>',
12137 tpl.overwrite(panel.body, data);
12139 * <p><b>Ability to execute arbitrary inline code</b> <br/>In an XTemplate, anything between {[ ... ]} is considered
12140 * code to be executed in the scope of the template. There are some special variables available in that code:
12142 * <li><b><tt>values</tt></b>: The values in the current scope. If you are using scope changing sub-templates, you
12143 * can change what <tt>values</tt> is.</li>
12144 * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
12145 * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the loop you are in (1-based).</li>
12146 * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length of the array you are looping.</li>
12147 * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
12149 * This example demonstrates basic row striping using an inline code block and the <tt>xindex</tt> variable:</p>
12151 var tpl = new Ext.XTemplate(
12152 '<p>Name: {name}</p>',
12153 '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
12155 '<tpl for="kids">',
12156 '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
12161 tpl.overwrite(panel.body, data);
12163 * <p><b>Template member functions</b> <br/>One or more member functions can be defined directly on the config
12164 * object passed into the XTemplate constructor for more complex processing:</p>
12166 var tpl = new Ext.XTemplate(
12167 '<p>Name: {name}</p>',
12169 '<tpl for="kids">',
12170 '<tpl if="this.isGirl(name)">',
12171 '<p>Girl: {name} - {age}</p>',
12173 '<tpl if="this.isGirl(name) == false">',
12174 '<p>Boy: {name} - {age}</p>',
12176 '<tpl if="this.isBaby(age)">',
12177 '<p>{name} is a baby!</p>',
12179 '</tpl></p>', {
12180 isGirl: function(name){
12181 return name == 'Sara Grace';
12183 isBaby: function(age){
12187 tpl.overwrite(panel.body, data);
12190 * @param {String/Array/Object} parts The HTML fragment or an array of fragments to join(""), or multiple arguments
12191 * to join("") that can also include a config object
12193 Ext.XTemplate = function(){
12194 Ext.XTemplate.superclass.constructor.apply(this, arguments);
12198 re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
12199 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
12200 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
12201 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
12209 RETURN = 'return ',
12210 WITHVALUES = 'with(values){ ';
12212 s = ['<tpl>', s, '</tpl>'].join('');
12214 while((m = s.match(re))){
12215 var m2 = m[0].match(nameRe),
12216 m3 = m[0].match(ifRe),
12217 m4 = m[0].match(execRe),
12221 name = m2 && m2[1] ? m2[1] : '';
12224 exp = m3 && m3[1] ? m3[1] : null;
12226 fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
12230 exp = m4 && m4[1] ? m4[1] : null;
12232 exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
12237 case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
12238 case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
12239 default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
12249 s = s.replace(m[0], '{xtpl'+ id + '}');
12252 Ext.each(tpls, function(t) {
12255 me.master = tpls[tpls.length-1];
12258 Ext.extend(Ext.XTemplate, Ext.Template, {
12260 re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
12262 codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
12265 applySubTemplate : function(id, values, parent, xindex, xcount){
12271 if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
12272 (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
12275 vs = t.target ? t.target.call(me, values, parent) : values;
12277 parent = t.target ? values : parent;
12278 if(t.target && Ext.isArray(vs)){
12279 Ext.each(vs, function(v, i) {
12280 buf[buf.length] = t.compiled.call(me, v, parent, i+1, len);
12282 return buf.join('');
12284 return t.compiled.call(me, vs, parent, xindex, xcount);
12288 compileTpl : function(tpl){
12289 var fm = Ext.util.Format,
12290 useF = this.disableFormats !== true,
12291 sep = Ext.isGecko ? "+" : ",",
12294 function fn(m, name, format, args, math){
12295 if(name.substr(0, 4) == 'xtpl'){
12296 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
12301 }else if(name === '#'){
12303 }else if(name.indexOf('.') != -1){
12306 v = "values['" + name + "']";
12309 v = '(' + v + math + ')';
12311 if (format && useF) {
12312 args = args ? ',' + args : "";
12313 if(format.substr(0, 5) != "this."){
12314 format = "fm." + format + '(';
12316 format = 'this.call("'+ format.substr(5) + '", ';
12320 args= ''; format = "("+v+" === undefined ? '' : ";
12322 return "'"+ sep + format + v + args + ")"+sep+"'";
12325 function codeFn(m, code){
12326 return "'"+ sep +'('+code+')'+sep+"'";
12329 // branched to use + in gecko and [].join() in others
12331 body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
12332 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
12335 body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
12336 body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
12337 body.push("'].join('');};");
12338 body = body.join('');
12345 * Returns an HTML fragment of this template with the specified values applied.
12346 * @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'})
12347 * @return {String} The HTML fragment
12349 applyTemplate : function(values){
12350 return this.master.compiled.call(this, values, {}, 1, 1);
12354 * Compile the template to a function for optimized performance. Recommended if the template will be used frequently.
12355 * @return {Function} The compiled function
12357 compile : function(){return this;}
12364 * @property disableFormats
12374 * Alias for {@link #applyTemplate}
12375 * Returns an HTML fragment of this template with the specified values applied.
12376 * @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'})
12377 * @return {String} The HTML fragment
12378 * @member Ext.XTemplate
12381 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
12384 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
12385 * @param {String/HTMLElement} el A DOM element or its id
12386 * @return {Ext.Template} The created template
12389 Ext.XTemplate.from = function(el){
12390 el = Ext.getDom(el);
12391 return new Ext.XTemplate(el.value || el.innerHTML);
12393 * @class Ext.util.CSS
\r
12394 * Utility class for manipulating CSS rules
\r
12397 Ext.util.CSS = function(){
\r
12398 var rules = null;
\r
12399 var doc = document;
\r
12401 var camelRe = /(-[a-z])/gi;
\r
12402 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
\r
12406 * Creates a stylesheet from a text blob of rules.
\r
12407 * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
\r
12408 * @param {String} cssText The text containing the css rules
\r
12409 * @param {String} id An id to add to the stylesheet for later removal
\r
12410 * @return {StyleSheet}
\r
12412 createStyleSheet : function(cssText, id){
\r
12414 var head = doc.getElementsByTagName("head")[0];
\r
12415 var rules = doc.createElement("style");
\r
12416 rules.setAttribute("type", "text/css");
\r
12418 rules.setAttribute("id", id);
\r
12421 head.appendChild(rules);
\r
12422 ss = rules.styleSheet;
\r
12423 ss.cssText = cssText;
\r
12426 rules.appendChild(doc.createTextNode(cssText));
\r
12428 rules.cssText = cssText;
\r
12430 head.appendChild(rules);
\r
12431 ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
\r
12433 this.cacheStyleSheet(ss);
\r
12438 * Removes a style or link tag by id
\r
12439 * @param {String} id The id of the tag
\r
12441 removeStyleSheet : function(id){
\r
12442 var existing = doc.getElementById(id);
\r
12444 existing.parentNode.removeChild(existing);
\r
12449 * Dynamically swaps an existing stylesheet reference for a new one
\r
12450 * @param {String} id The id of an existing link tag to remove
\r
12451 * @param {String} url The href of the new stylesheet to include
\r
12453 swapStyleSheet : function(id, url){
\r
12454 this.removeStyleSheet(id);
\r
12455 var ss = doc.createElement("link");
\r
12456 ss.setAttribute("rel", "stylesheet");
\r
12457 ss.setAttribute("type", "text/css");
\r
12458 ss.setAttribute("id", id);
\r
12459 ss.setAttribute("href", url);
\r
12460 doc.getElementsByTagName("head")[0].appendChild(ss);
\r
12464 * Refresh the rule cache if you have dynamically added stylesheets
\r
12465 * @return {Object} An object (hash) of rules indexed by selector
\r
12467 refreshCache : function(){
\r
12468 return this.getRules(true);
\r
12472 cacheStyleSheet : function(ss){
\r
12476 try{// try catch for cross domain access issue
\r
12477 var ssRules = ss.cssRules || ss.rules;
\r
12478 for(var j = ssRules.length-1; j >= 0; --j){
\r
12479 rules[ssRules[j].selectorText] = ssRules[j];
\r
12485 * Gets all css rules for the document
\r
12486 * @param {Boolean} refreshCache true to refresh the internal cache
\r
12487 * @return {Object} An object (hash) of rules indexed by selector
\r
12489 getRules : function(refreshCache){
\r
12490 if(rules === null || refreshCache){
\r
12492 var ds = doc.styleSheets;
\r
12493 for(var i =0, len = ds.length; i < len; i++){
\r
12495 this.cacheStyleSheet(ds[i]);
\r
12503 * Gets an an individual CSS rule by selector(s)
\r
12504 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
\r
12505 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
\r
12506 * @return {CSSRule} The CSS rule or null if one is not found
\r
12508 getRule : function(selector, refreshCache){
\r
12509 var rs = this.getRules(refreshCache);
\r
12510 if(!Ext.isArray(selector)){
\r
12511 return rs[selector];
\r
12513 for(var i = 0; i < selector.length; i++){
\r
12514 if(rs[selector[i]]){
\r
12515 return rs[selector[i]];
\r
12523 * Updates a rule property
\r
12524 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
\r
12525 * @param {String} property The css property
\r
12526 * @param {String} value The new value for the property
\r
12527 * @return {Boolean} true If a rule was found and updated
\r
12529 updateRule : function(selector, property, value){
\r
12530 if(!Ext.isArray(selector)){
\r
12531 var rule = this.getRule(selector);
\r
12533 rule.style[property.replace(camelRe, camelFn)] = value;
\r
12537 for(var i = 0; i < selector.length; i++){
\r
12538 if(this.updateRule(selector[i], property, value)){
\r
12547 @class Ext.util.ClickRepeater
12548 @extends Ext.util.Observable
12550 A wrapper class which can be applied to any element. Fires a "click" event while the
12551 mouse is pressed. The interval between firings may be specified in the config but
12552 defaults to 20 milliseconds.
12554 Optionally, a CSS class may be applied to the element during the time it is pressed.
12556 @cfg {Mixed} el The element to act as a button.
12557 @cfg {Number} delay The initial delay before the repeating event begins firing.
12558 Similar to an autorepeat key delay.
12559 @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
12560 @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
12561 @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
12562 "interval" and "delay" are ignored.
12563 @cfg {Boolean} preventDefault True to prevent the default click event
12564 @cfg {Boolean} stopDefault True to stop the default click event
12567 2007-02-02 jvs Original code contributed by Nige "Animal" White
12568 2007-02-02 jvs Renamed to ClickRepeater
12569 2007-02-03 jvs Modifications for FF Mac and Safari
12572 @param {Mixed} el The element to listen on
12573 @param {Object} config
12575 Ext.util.ClickRepeater = function(el, config)
12577 this.el = Ext.get(el);
12578 this.el.unselectable();
12580 Ext.apply(this, config);
12585 * Fires when the mouse button is depressed.
12586 * @param {Ext.util.ClickRepeater} this
12591 * Fires on a specified interval during the time the element is pressed.
12592 * @param {Ext.util.ClickRepeater} this
12597 * Fires when the mouse key is released.
12598 * @param {Ext.util.ClickRepeater} this
12603 if(!this.disabled){
12604 this.disabled = true;
12608 // allow inline handler
12610 this.on("click", this.handler, this.scope || this);
12613 Ext.util.ClickRepeater.superclass.constructor.call(this);
12616 Ext.extend(Ext.util.ClickRepeater, Ext.util.Observable, {
12619 preventDefault : true,
12620 stopDefault : false,
12624 * Enables the repeater and allows events to fire.
12626 enable: function(){
12628 this.el.on('mousedown', this.handleMouseDown, this);
12629 if(this.preventDefault || this.stopDefault){
12630 this.el.on('click', this.eventOptions, this);
12633 this.disabled = false;
12637 * Disables the repeater and stops events from firing.
12639 disable: function(/* private */ force){
12640 if(force || !this.disabled){
12641 clearTimeout(this.timer);
12642 if(this.pressClass){
12643 this.el.removeClass(this.pressClass);
12645 Ext.getDoc().un('mouseup', this.handleMouseUp, this);
12646 this.el.removeAllListeners();
12648 this.disabled = true;
12652 * Convenience function for setting disabled/enabled by boolean.
12653 * @param {Boolean} disabled
12655 setDisabled: function(disabled){
12656 this[disabled ? 'disable' : 'enable']();
12659 eventOptions: function(e){
12660 if(this.preventDefault){
12661 e.preventDefault();
12663 if(this.stopDefault){
12669 destroy : function() {
12670 this.disable(true);
12671 Ext.destroy(this.el);
12672 this.purgeListeners();
12676 handleMouseDown : function(){
12677 clearTimeout(this.timer);
12679 if(this.pressClass){
12680 this.el.addClass(this.pressClass);
12682 this.mousedownTime = new Date();
12684 Ext.getDoc().on("mouseup", this.handleMouseUp, this);
12685 this.el.on("mouseout", this.handleMouseOut, this);
12687 this.fireEvent("mousedown", this);
12688 this.fireEvent("click", this);
12690 // Do not honor delay or interval if acceleration wanted.
12691 if (this.accelerate) {
12694 this.timer = this.click.defer(this.delay || this.interval, this);
12698 click : function(){
12699 this.fireEvent("click", this);
12700 this.timer = this.click.defer(this.accelerate ?
12701 this.easeOutExpo(this.mousedownTime.getElapsed(),
12705 this.interval, this);
12708 easeOutExpo : function (t, b, c, d) {
12709 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
12713 handleMouseOut : function(){
12714 clearTimeout(this.timer);
12715 if(this.pressClass){
12716 this.el.removeClass(this.pressClass);
12718 this.el.on("mouseover", this.handleMouseReturn, this);
12722 handleMouseReturn : function(){
12723 this.el.un("mouseover", this.handleMouseReturn, this);
12724 if(this.pressClass){
12725 this.el.addClass(this.pressClass);
12731 handleMouseUp : function(){
12732 clearTimeout(this.timer);
12733 this.el.un("mouseover", this.handleMouseReturn, this);
12734 this.el.un("mouseout", this.handleMouseOut, this);
12735 Ext.getDoc().un("mouseup", this.handleMouseUp, this);
12736 this.el.removeClass(this.pressClass);
12737 this.fireEvent("mouseup", this);
12740 * @class Ext.KeyNav
12741 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
12742 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
12743 * way to implement custom navigation schemes for any UI component.</p>
12744 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
12745 * pageUp, pageDown, del, home, end. Usage:</p>
12747 var nav = new Ext.KeyNav("my-element", {
12748 "left" : function(e){
12749 this.moveLeft(e.ctrlKey);
12751 "right" : function(e){
12752 this.moveRight(e.ctrlKey);
12754 "enter" : function(e){
12761 * @param {Mixed} el The element to bind to
12762 * @param {Object} config The config
12764 Ext.KeyNav = function(el, config){
12765 this.el = Ext.get(el);
12766 Ext.apply(this, config);
12767 if(!this.disabled){
12768 this.disabled = true;
12773 Ext.KeyNav.prototype = {
12775 * @cfg {Boolean} disabled
12776 * True to disable this KeyNav instance (defaults to false)
12780 * @cfg {String} defaultEventAction
12781 * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key. Valid values are
12782 * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
12783 * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
12785 defaultEventAction: "stopEvent",
12787 * @cfg {Boolean} forceKeyDown
12788 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
12789 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
12790 * handle keydown instead of keypress.
12792 forceKeyDown : false,
12795 prepareEvent : function(e){
12796 var k = e.getKey();
12797 var h = this.keyToHandler[k];
12798 if(Ext.isSafari2 && h && k >= 37 && k <= 40){
12804 relay : function(e){
12805 var k = e.getKey();
12806 var h = this.keyToHandler[k];
12808 if(this.doRelay(e, this[h], h) !== true){
12809 e[this.defaultEventAction]();
12815 doRelay : function(e, h, hname){
12816 return h.call(this.scope || this, e);
12819 // possible handlers
12833 // quick lookup hash
12850 * Enable this KeyNav
12852 enable: function(){
12854 // ie won't do special keys on keypress, no one else will repeat keys with keydown
12855 // the EventObject will normalize Safari automatically
12856 if(this.isKeydown()){
12857 this.el.on("keydown", this.relay, this);
12859 this.el.on("keydown", this.prepareEvent, this);
12860 this.el.on("keypress", this.relay, this);
12862 this.disabled = false;
12867 * Disable this KeyNav
12869 disable: function(){
12870 if(!this.disabled){
12871 if(this.isKeydown()){
12872 this.el.un("keydown", this.relay, this);
12874 this.el.un("keydown", this.prepareEvent, this);
12875 this.el.un("keypress", this.relay, this);
12877 this.disabled = true;
12882 * Convenience function for setting disabled/enabled by boolean.
12883 * @param {Boolean} disabled
12885 setDisabled : function(disabled){
12886 this[disabled ? "disable" : "enable"]();
12890 isKeydown: function(){
12891 return this.forceKeyDown || Ext.EventManager.useKeydown;
12895 * @class Ext.KeyMap
\r
12896 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
\r
12897 * The constructor accepts the same config object as defined by {@link #addBinding}.
\r
12898 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
\r
12899 * combination it will call the function with this signature (if the match is a multi-key
\r
12900 * combination the callback will still be called only once): (String key, Ext.EventObject e)
\r
12901 * A KeyMap can also handle a string representation of keys.<br />
\r
12904 // map one key by key code
\r
12905 var map = new Ext.KeyMap("my-element", {
\r
12906 key: 13, // or Ext.EventObject.ENTER
\r
12911 // map multiple keys to one action by string
\r
12912 var map = new Ext.KeyMap("my-element", {
\r
12918 // map multiple keys to multiple actions by strings and array of codes
\r
12919 var map = new Ext.KeyMap("my-element", [
\r
12922 fn: function(){ alert("Return was pressed"); }
\r
12925 fn: function(){ alert('a, b or c was pressed'); }
\r
12930 fn: function(){ alert('Control + shift + tab was pressed.'); }
\r
12934 * <b>Note: A KeyMap starts enabled</b>
\r
12936 * @param {Mixed} el The element to bind to
\r
12937 * @param {Object} config The config (see {@link #addBinding})
\r
12938 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
\r
12940 Ext.KeyMap = function(el, config, eventName){
\r
12941 this.el = Ext.get(el);
\r
12942 this.eventName = eventName || "keydown";
\r
12943 this.bindings = [];
\r
12945 this.addBinding(config);
\r
12950 Ext.KeyMap.prototype = {
\r
12952 * True to stop the event from bubbling and prevent the default browser action if the
\r
12953 * key was handled by the KeyMap (defaults to false)
\r
12956 stopEvent : false,
\r
12959 * Add a new binding to this KeyMap. The following config object properties are supported:
\r
12961 Property Type Description
\r
12962 ---------- --------------- ----------------------------------------------------------------------
\r
12963 key String/Array A single keycode or an array of keycodes to handle
\r
12964 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
12965 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
12966 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
12967 handler Function The function to call when KeyMap finds the expected key combination
\r
12968 fn Function Alias of handler (for backwards-compatibility)
\r
12969 scope Object The scope of the callback function
\r
12970 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
12975 // Create a KeyMap
\r
12976 var map = new Ext.KeyMap(document, {
\r
12977 key: Ext.EventObject.ENTER,
\r
12982 //Add a new binding to the existing KeyMap later
\r
12990 * @param {Object/Array} config A single KeyMap config or an array of configs
\r
12992 addBinding : function(config){
\r
12993 if(Ext.isArray(config)){
\r
12994 Ext.each(config, function(c){
\r
12995 this.addBinding(c);
\r
12999 var keyCode = config.key,
\r
13000 fn = config.fn || config.handler,
\r
13001 scope = config.scope;
\r
13003 if (config.stopEvent) {
\r
13004 this.stopEvent = config.stopEvent;
\r
13007 if(typeof keyCode == "string"){
\r
13009 var keyString = keyCode.toUpperCase();
\r
13010 for(var j = 0, len = keyString.length; j < len; j++){
\r
13011 ks.push(keyString.charCodeAt(j));
\r
13015 var keyArray = Ext.isArray(keyCode);
\r
13017 var handler = function(e){
\r
13018 if(this.checkModifiers(config, e)){
\r
13019 var k = e.getKey();
\r
13021 for(var i = 0, len = keyCode.length; i < len; i++){
\r
13022 if(keyCode[i] == k){
\r
13023 if(this.stopEvent){
\r
13026 fn.call(scope || window, k, e);
\r
13031 if(k == keyCode){
\r
13032 if(this.stopEvent){
\r
13035 fn.call(scope || window, k, e);
\r
13040 this.bindings.push(handler);
\r
13044 checkModifiers: function(config, e){
\r
13045 var val, key, keys = ['shift', 'ctrl', 'alt'];
\r
13046 for (var i = 0, len = keys.length; i < len; ++i){
\r
13048 val = config[key];
\r
13049 if(!(val === undefined || (val === e[key + 'Key']))){
\r
13057 * Shorthand for adding a single key listener
\r
13058 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
\r
13059 * following options:
\r
13060 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
\r
13061 * @param {Function} fn The function to call
\r
13062 * @param {Object} scope (optional) The scope of the function
\r
13064 on : function(key, fn, scope){
\r
13065 var keyCode, shift, ctrl, alt;
\r
13066 if(typeof key == "object" && !Ext.isArray(key)){
\r
13067 keyCode = key.key;
\r
13068 shift = key.shift;
\r
13074 this.addBinding({
\r
13085 handleKeyDown : function(e){
\r
13086 if(this.enabled){ //just in case
\r
13087 var b = this.bindings;
\r
13088 for(var i = 0, len = b.length; i < len; i++){
\r
13089 b[i].call(this, e);
\r
13095 * Returns true if this KeyMap is enabled
\r
13096 * @return {Boolean}
\r
13098 isEnabled : function(){
\r
13099 return this.enabled;
\r
13103 * Enables this KeyMap
\r
13105 enable: function(){
\r
13106 if(!this.enabled){
\r
13107 this.el.on(this.eventName, this.handleKeyDown, this);
\r
13108 this.enabled = true;
\r
13113 * Disable this KeyMap
\r
13115 disable: function(){
\r
13116 if(this.enabled){
\r
13117 this.el.removeListener(this.eventName, this.handleKeyDown, this);
\r
13118 this.enabled = false;
\r
13123 * Convenience function for setting disabled/enabled by boolean.
\r
13124 * @param {Boolean} disabled
\r
13126 setDisabled : function(disabled){
\r
13127 this[disabled ? "disable" : "enable"]();
\r
13130 * @class Ext.util.TextMetrics
13131 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13132 * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
13133 * should not contain any HTML, otherwise it may not be measured correctly.
13136 Ext.util.TextMetrics = function(){
13140 * Measures the size of the specified text
13141 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13142 * that can affect the size of the rendered text
13143 * @param {String} text The text to measure
13144 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13145 * in order to accurately measure the text height
13146 * @return {Object} An object containing the text's size {width: (width), height: (height)}
13148 measure : function(el, text, fixedWidth){
13150 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
13153 shared.setFixedWidth(fixedWidth || 'auto');
13154 return shared.getSize(text);
13158 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
13159 * the overhead of multiple calls to initialize the style properties on each measurement.
13160 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13161 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13162 * in order to accurately measure the text height
13163 * @return {Ext.util.TextMetrics.Instance} instance The new instance
13165 createInstance : function(el, fixedWidth){
13166 return Ext.util.TextMetrics.Instance(el, fixedWidth);
13171 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13172 var ml = new Ext.Element(document.createElement('div'));
13173 document.body.appendChild(ml.dom);
13174 ml.position('absolute');
13175 ml.setLeftTop(-1000, -1000);
13179 ml.setWidth(fixedWidth);
13184 * Returns the size of the specified text based on the internal element's style and width properties
13185 * @param {String} text The text to measure
13186 * @return {Object} An object containing the text's size {width: (width), height: (height)}
13188 getSize : function(text){
13190 var s = ml.getSize();
13196 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13197 * that can affect the size of the rendered text
13198 * @param {String/HTMLElement} el The element, dom node or id
13200 bind : function(el){
13202 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
13207 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
13208 * to set a fixed width in order to accurately measure the text height.
13209 * @param {Number} width The width to set on the element
13211 setFixedWidth : function(width){
13212 ml.setWidth(width);
13216 * Returns the measured width of the specified text
13217 * @param {String} text The text to measure
13218 * @return {Number} width The width in pixels
13220 getWidth : function(text){
13221 ml.dom.style.width = 'auto';
13222 return this.getSize(text).width;
13226 * Returns the measured height of the specified text. For multiline text, be sure to call
13227 * {@link #setFixedWidth} if necessary.
13228 * @param {String} text The text to measure
13229 * @return {Number} height The height in pixels
13231 getHeight : function(text){
13232 return this.getSize(text).height;
13236 instance.bind(bindTo);
13241 Ext.Element.addMethods({
13243 * Returns the width in pixels of the passed text, or the width of the text in this Element.
13244 * @param {String} text The text to measure. Defaults to the innerHTML of the element.
13245 * @param {Number} min (Optional) The minumum value to return.
13246 * @param {Number} max (Optional) The maximum value to return.
13247 * @return {Number} The text width in pixels.
13248 * @member Ext.Element getTextWidth
13250 getTextWidth : function(text, min, max){
13251 return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
13255 * @class Ext.util.Cookies
\r
13256 * Utility class for managing and interacting with cookies.
\r
13259 Ext.util.Cookies = {
\r
13261 * Create a cookie with the specified name and value. Additional settings
\r
13262 * for the cookie may be optionally specified (for example: expiration,
\r
13263 * access restriction, SSL).
\r
13264 * @param {Object} name
\r
13265 * @param {Object} value
\r
13266 * @param {Object} expires (Optional) Specify an expiration date the
\r
13267 * cookie is to persist until. Note that the specified Date object will
\r
13268 * be converted to Greenwich Mean Time (GMT).
\r
13269 * @param {String} path (Optional) Setting a path on the cookie restricts
\r
13270 * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>).
\r
13271 * @param {String} domain (Optional) Setting a domain restricts access to
\r
13272 * pages on a given domain (typically used to allow cookie access across
\r
13273 * subdomains). For example, "extjs.com" will create a cookie that can be
\r
13274 * accessed from any subdomain of extjs.com, including www.extjs.com,
\r
13275 * support.extjs.com, etc.
\r
13276 * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
\r
13277 * should only be accessible via SSL on a page using the HTTPS protocol.
\r
13278 * Defaults to <tt>false</tt>. Note that this will only work if the page
\r
13279 * calling this code uses the HTTPS protocol, otherwise the cookie will be
\r
13280 * created with default options.
\r
13282 set : function(name, value){
\r
13283 var argv = arguments;
\r
13284 var argc = arguments.length;
\r
13285 var expires = (argc > 2) ? argv[2] : null;
\r
13286 var path = (argc > 3) ? argv[3] : '/';
\r
13287 var domain = (argc > 4) ? argv[4] : null;
\r
13288 var secure = (argc > 5) ? argv[5] : false;
\r
13289 document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
\r
13293 * Retrieves cookies that are accessible by the current page. If a cookie
\r
13294 * does not exist, <code>get()</code> returns <tt>null</tt>. The following
\r
13295 * example retrieves the cookie called "valid" and stores the String value
\r
13296 * in the variable <tt>validStatus</tt>.
\r
13298 * var validStatus = Ext.util.Cookies.get("valid");
\r
13300 * @param {Object} name The name of the cookie to get
\r
13301 * @return {Mixed} Returns the cookie value for the specified name;
\r
13302 * null if the cookie name does not exist.
\r
13304 get : function(name){
\r
13305 var arg = name + "=";
\r
13306 var alen = arg.length;
\r
13307 var clen = document.cookie.length;
\r
13312 if(document.cookie.substring(i, j) == arg){
\r
13313 return Ext.util.Cookies.getCookieVal(j);
\r
13315 i = document.cookie.indexOf(" ", i) + 1;
\r
13324 * Removes a cookie with the provided name from the browser
\r
13326 * @param {Object} name The name of the cookie to remove
\r
13328 clear : function(name){
\r
13329 if(Ext.util.Cookies.get(name)){
\r
13330 document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
\r
13336 getCookieVal : function(offset){
\r
13337 var endstr = document.cookie.indexOf(";", offset);
\r
13338 if(endstr == -1){
\r
13339 endstr = document.cookie.length;
\r
13341 return unescape(document.cookie.substring(offset, endstr));
\r
13344 * Framework-wide error-handler. Developers can override this method to provide
13345 * custom exception-handling. Framework errors will often extend from the base
13347 * @param {Object/Error} e The thrown exception object.
13349 Ext.handleError = function(e) {
13356 * <p>A base error class. Future implementations are intended to provide more
13357 * robust error handling throughout the framework (<b>in the debug build only</b>)
13358 * to check for common errors and problems. The messages issued by this class
13359 * will aid error checking. Error checks will be automatically removed in the
13360 * production build so that performance is not negatively impacted.</p>
13361 * <p>Some sample messages currently implemented:</p><pre>
13362 "DataProxy attempted to execute an API-action but found an undefined
13363 url / function. Please review your Proxy url/api-configuration."
13365 "Could not locate your "root" property in your server response.
13366 Please review your JsonReader config to ensure the config-property
13367 "root" matches the property your server-response. See the JsonReader
13368 docs for additional assistance."
13370 * <p>An example of the code used for generating error messages:</p><pre><code>
13379 function generateError(data) {
13380 throw new Ext.Error('foo-error', data);
13383 * @param {String} message
13385 Ext.Error = function(message) {
13386 // Try to read the message from Ext.Error.lang
13387 this.message = (this.lang[message]) ? this.lang[message] : message;
13389 Ext.Error.prototype = new Error();
13390 Ext.apply(Ext.Error.prototype, {
13391 // protected. Extensions place their error-strings here.
13399 getName : function() {
13406 getMessage : function() {
13407 return this.message;
13413 toJson : function() {
13414 return Ext.encode(this);