3 * Copyright(c) 2006-2010 Sencha Inc.
5 * http://www.sencha.com/license
11 Ext.ns("Ext.grid", "Ext.list", "Ext.dd", "Ext.tree", "Ext.form", "Ext.menu",
12 "Ext.state", "Ext.layout", "Ext.app", "Ext.ux", "Ext.chart", "Ext.direct");
14 * Namespace alloted for extensions to the framework.
19 Ext.apply(Ext, function(){
26 * A reusable empty function
30 emptyFn : function(){},
33 * URL to a 1x1 transparent gif image used by Ext to create inline icons with CSS background images.
34 * In older versions of IE, this defaults to "http://extjs.com/s.gif" and you should change this to a URL on your server.
35 * For other browsers it uses an inline data URL.
38 BLANK_IMAGE_URL : Ext.isIE6 || Ext.isIE7 || Ext.isAir ?
39 'http:/' + '/www.extjs.com/s.gif' :
40 'data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
42 extendX : function(supr, fn){
43 return Ext.extend(supr, fn(supr.prototype));
47 * Returns the current HTML document object as an {@link Ext.Element}.
48 * @return Ext.Element The document
51 return Ext.get(document);
55 * Utility method for validating that a value is numeric, returning the specified default value if it is not.
56 * @param {Mixed} value Should be a number, but any type will be handled appropriately
57 * @param {Number} defaultValue The value to return if the original value is non-numeric
58 * @return {Number} Value, if numeric, else defaultValue
60 num : function(v, defaultValue){
61 v = Number(Ext.isEmpty(v) || Ext.isArray(v) || typeof v == 'boolean' || (typeof v == 'string' && v.trim().length == 0) ? NaN : v);
62 return isNaN(v) ? defaultValue : v;
66 * <p>Utility method for returning a default value if the passed value is empty.</p>
67 * <p>The value is deemed to be empty if it is<div class="mdetail-params"><ul>
70 * <li>an empty array</li>
71 * <li>a zero length string (Unless the <tt>allowBlank</tt> parameter is <tt>true</tt>)</li>
73 * @param {Mixed} value The value to test
74 * @param {Mixed} defaultValue The value to return if the original value is empty
75 * @param {Boolean} allowBlank (optional) true to allow zero length strings to qualify as non-empty (defaults to false)
76 * @return {Mixed} value, if non-empty, else defaultValue
78 value : function(v, defaultValue, allowBlank){
79 return Ext.isEmpty(v, allowBlank) ? defaultValue : v;
83 * Escapes the passed string for use in a regular expression
87 escapeRe : function(s) {
88 return s.replace(/([-.*+?^${}()|[\]\/\\])/g, "\\$1");
91 sequence : function(o, name, fn, scope){
92 o[name] = o[name].createSequence(fn, scope);
96 * Applies event listeners to elements by selectors when the document is ready.
97 * The event name is specified with an <tt>@</tt> suffix.
100 // add a listener for click on all anchors in element with id foo
101 '#foo a@click' : function(e, t){
105 // add the same listener to multiple selectors (separated by comma BEFORE the @)
106 '#foo a, #bar span.some-class@mouseover' : function(){
111 * @param {Object} obj The list of behaviors to apply
113 addBehaviors : function(o){
115 Ext.onReady(function(){
119 var cache = {}, // simple cache for applying multiple behaviors to same selector does query multiple times
124 if ((parts = b.split('@'))[1]) { // for Object prototype breakers
127 cache[s] = Ext.select(s);
129 cache[s].on(parts[1], o[b]);
137 * Utility method for getting the width of the browser scrollbar. This can differ depending on
138 * operating system settings, such as the theme or font size.
139 * @param {Boolean} force (optional) true to force a recalculation of the value.
140 * @return {Number} The width of the scrollbar.
142 getScrollBarWidth: function(force){
147 if(force === true || scrollWidth === null){
148 // Append our div, do our calculation and then remove it
149 var div = Ext.getBody().createChild('<div class="x-hide-offsets" style="width:100px;height:50px;overflow:hidden;"><div style="height:200px;"></div></div>'),
150 child = div.child('div', true);
151 var w1 = child.offsetWidth;
152 div.setStyle('overflow', (Ext.isWebKit || Ext.isGecko) ? 'auto' : 'scroll');
153 var w2 = child.offsetWidth;
155 // Need to add 2 to ensure we leave enough space
156 scrollWidth = w1 - w2 + 2;
163 combine : function(){
164 var as = arguments, l = as.length, r = [];
165 for(var i = 0; i < l; i++){
169 }else if(a.length !== undefined && !a.substr){
170 r = r.concat(Array.prototype.slice.call(a, 0));
179 * Copies a set of named properties fom the source object to the destination object.
180 * <p>example:<pre><code>
181 ImageComponent = Ext.extend(Ext.BoxComponent, {
182 initComponent: function() {
183 this.autoEl = { tag: 'img' };
184 MyComponent.superclass.initComponent.apply(this, arguments);
185 this.initialBox = Ext.copyTo({}, this.initialConfig, 'x,y,width,height');
189 * @param {Object} dest The destination object.
190 * @param {Object} source The source object.
191 * @param {Array/String} names Either an Array of property names, or a comma-delimited list
192 * of property names to copy.
193 * @return {Object} The modified object.
195 copyTo : function(dest, source, names){
196 if(typeof names == 'string'){
197 names = names.split(/[,;\s]/);
199 Ext.each(names, function(name){
200 if(source.hasOwnProperty(name)){
201 dest[name] = source[name];
208 * Attempts to destroy any objects passed to it by removing all event listeners, removing them from the
209 * DOM (if applicable) and calling their destroy functions (if available). This method is primarily
210 * intended for arguments of type {@link Ext.Element} and {@link Ext.Component}, but any subclass of
211 * {@link Ext.util.Observable} can be passed in. Any number of elements and/or components can be
212 * passed into this function in a single call as separate arguments.
213 * @param {Mixed} arg1 An {@link Ext.Element}, {@link Ext.Component}, or an Array of either of these to destroy
214 * @param {Mixed} arg2 (optional)
215 * @param {Mixed} etc... (optional)
217 destroy : function(){
218 Ext.each(arguments, function(arg){
220 if(Ext.isArray(arg)){
221 this.destroy.apply(this, arg);
222 }else if(typeof arg.destroy == 'function'){
232 * Attempts to destroy and then remove a set of named properties of the passed object.
233 * @param {Object} o The object (most likely a Component) who's properties you wish to destroy.
234 * @param {Mixed} arg1 The name of the property to destroy and remove from the object.
235 * @param {Mixed} etc... More property names to destroy and remove.
237 destroyMembers : function(o, arg1, arg2, etc){
238 for(var i = 1, a = arguments, len = a.length; i < len; i++) {
239 Ext.destroy(o[a[i]]);
245 * Creates a copy of the passed Array with falsy values removed.
246 * @param {Array/NodeList} arr The Array from which to remove falsy values.
247 * @return {Array} The new, compressed Array.
249 clean : function(arr){
251 Ext.each(arr, function(v){
260 * Creates a copy of the passed Array, filtered to contain only unique values.
261 * @param {Array} arr The Array to filter
262 * @return {Array} The new Array containing unique values.
264 unique : function(arr){
268 Ext.each(arr, function(v) {
278 * Recursively flattens into 1-d Array. Injects Arrays inline.
279 * @param {Array} arr The array to flatten
280 * @return {Array} The new, flattened array.
282 flatten : function(arr){
284 function rFlatten(a) {
285 Ext.each(a, function(v) {
294 return rFlatten(arr);
298 * Returns the minimum value in the Array.
299 * @param {Array|NodeList} arr The Array from which to select the minimum value.
300 * @param {Function} comp (optional) a function to perform the comparision which determines minimization.
301 * If omitted the "<" operator will be used. Note: gt = 1; eq = 0; lt = -1
302 * @return {Object} The minimum value in the Array.
304 min : function(arr, comp){
306 comp = comp || function(a,b){ return a < b ? -1 : 1; };
307 Ext.each(arr, function(v) {
308 ret = comp(ret, v) == -1 ? ret : v;
314 * Returns the maximum value in the Array
315 * @param {Array|NodeList} arr The Array from which to select the maximum value.
316 * @param {Function} comp (optional) a function to perform the comparision which determines maximization.
317 * If omitted the ">" operator will be used. Note: gt = 1; eq = 0; lt = -1
318 * @return {Object} The maximum value in the Array.
320 max : function(arr, comp){
322 comp = comp || function(a,b){ return a > b ? 1 : -1; };
323 Ext.each(arr, function(v) {
324 ret = comp(ret, v) == 1 ? ret : v;
330 * Calculates the mean of the Array
331 * @param {Array} arr The Array to calculate the mean value of.
332 * @return {Number} The mean.
334 mean : function(arr){
335 return arr.length > 0 ? Ext.sum(arr) / arr.length : undefined;
339 * Calculates the sum of the Array
340 * @param {Array} arr The Array to calculate the sum value of.
341 * @return {Number} The sum.
345 Ext.each(arr, function(v) {
352 * Partitions the set into two sets: a true set and a false set.
357 Ext.partition([true, false, true, true, false]); // [[true, true, true], [false, false]]
363 return val.className == "class1"
366 // true are those paragraph elements with a className of "class1",
367 // false set are those that do not have that className.
369 * @param {Array|NodeList} arr The array to partition
370 * @param {Function} truth (optional) a function to determine truth. If this is omitted the element
371 * itself must be able to be evaluated for its truthfulness.
372 * @return {Array} [true<Array>,false<Array>]
374 partition : function(arr, truth){
376 Ext.each(arr, function(v, i, a) {
377 ret[ (truth && truth(v, i, a)) || (!truth && v) ? 0 : 1].push(v);
383 * Invokes a method on each item in an Array.
386 Ext.invoke(Ext.query("p"), "getAttribute", "id");
387 // [el1.getAttribute("id"), el2.getAttribute("id"), ..., elN.getAttribute("id")]
389 * @param {Array|NodeList} arr The Array of items to invoke the method on.
390 * @param {String} methodName The method name to invoke.
391 * @param {...*} args Arguments to send into the method invocation.
392 * @return {Array} The results of invoking the method on each item in the array.
394 invoke : function(arr, methodName){
396 args = Array.prototype.slice.call(arguments, 2);
397 Ext.each(arr, function(v,i) {
398 if (v && typeof v[methodName] == 'function') {
399 ret.push(v[methodName].apply(v, args));
408 * Plucks the value of a property from each item in the Array
411 Ext.pluck(Ext.query("p"), "className"); // [el1.className, el2.className, ..., elN.className]
413 * @param {Array|NodeList} arr The Array of items to pluck the value from.
414 * @param {String} prop The property name to pluck from each element.
415 * @return {Array} The value from each item in the Array.
417 pluck : function(arr, prop){
419 Ext.each(arr, function(v) {
426 * <p>Zips N sets together.</p>
429 Ext.zip([1,2,3],[4,5,6]); // [[1,4],[2,5],[3,6]]
436 return "$" + a + "" + b + "." + c
438 ); // ["$+12.43", "$-10.15", "$+22.96"]
440 * @param {Arrays|NodeLists} arr This argument may be repeated. Array(s) to contribute values.
441 * @param {Function} zipper (optional) The last item in the argument list. This will drive how the items are zipped together.
442 * @return {Array} The zipped set.
445 var parts = Ext.partition(arguments, function( val ){ return typeof val != 'function'; }),
448 len = Ext.max(Ext.pluck(arrs, "length")),
451 for (var i = 0; i < len; i++) {
454 ret[i] = fn.apply(fn, Ext.pluck(arrs, i));
456 for (var j = 0, aLen = arrs.length; j < aLen; j++){
457 ret[i].push( arrs[j][i] );
465 * This is shorthand reference to {@link Ext.ComponentMgr#get}.
466 * Looks up an existing {@link Ext.Component Component} by {@link Ext.Component#id id}
467 * @param {String} id The component {@link Ext.Component#id id}
468 * @return Ext.Component The Component, <tt>undefined</tt> if not found, or <tt>null</tt> if a
471 getCmp : function(id){
472 return Ext.ComponentMgr.get(id);
476 * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
477 * you may want to set this to true.
480 useShims: E.isIE6 || (E.isMac && E.isGecko2),
482 // inpired by a similar function in mootools library
484 * Returns the type of object that is passed in. If the object passed in is null or undefined it
485 * return false otherwise it returns one of the following values:<div class="mdetail-params"><ul>
486 * <li><b>string</b>: If the object passed is a string</li>
487 * <li><b>number</b>: If the object passed is a number</li>
488 * <li><b>boolean</b>: If the object passed is a boolean value</li>
489 * <li><b>date</b>: If the object passed is a Date object</li>
490 * <li><b>function</b>: If the object passed is a function reference</li>
491 * <li><b>object</b>: If the object passed is an object</li>
492 * <li><b>array</b>: If the object passed is an array</li>
493 * <li><b>regexp</b>: If the object passed is a regular expression</li>
494 * <li><b>element</b>: If the object passed is a DOM Element</li>
495 * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
496 * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
497 * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
499 * @param {Mixed} object
503 if(o === undefined || o === null){
510 if(t == 'object' && o.nodeName) {
512 case 1: return 'element';
513 case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
516 if(t == 'object' || t == 'function') {
517 switch(o.constructor) {
518 case Array: return 'array';
519 case RegExp: return 'regexp';
520 case Date: return 'date';
522 if(typeof o.length == 'number' && typeof o.item == 'function') {
529 intercept : function(o, name, fn, scope){
530 o[name] = o[name].createInterceptor(fn, scope);
534 callback : function(cb, scope, args, delay){
535 if(typeof cb == 'function'){
537 cb.defer(delay, scope, args || []);
539 cb.apply(scope, args || []);
548 * These functions are available on every Function object (any JavaScript function).
550 Ext.apply(Function.prototype, {
552 * Create a combined function call sequence of the original function + the passed function.
553 * The resulting function returns the results of the original function.
554 * The passed fcn is called with the parameters of the original function. Example usage:
556 var sayHi = function(name){
557 alert('Hi, ' + name);
560 sayHi('Fred'); // alerts "Hi, Fred"
562 var sayGoodbye = sayHi.createSequence(function(name){
563 alert('Bye, ' + name);
566 sayGoodbye('Fred'); // both alerts show
568 * @param {Function} fcn The function to sequence
569 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
570 * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
571 * @return {Function} The new function
573 createSequence : function(fcn, scope){
575 return (typeof fcn != 'function') ?
578 var retval = method.apply(this || window, arguments);
579 fcn.apply(scope || this || window, arguments);
588 * These functions are available as static methods on the JavaScript String object.
590 Ext.applyIf(String, {
593 * Escapes the passed string for ' and \
594 * @param {String} string The string to escape
595 * @return {String} The escaped string
598 escape : function(string) {
599 return string.replace(/('|\\)/g, "\\$1");
603 * Pads the left side of a string with a specified character. This is especially useful
604 * for normalizing number and date strings. Example usage:
606 var s = String.leftPad('123', 5, '0');
607 // s now contains the string: '00123'
609 * @param {String} string The original string
610 * @param {Number} size The total length of the output string
611 * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
612 * @return {String} The padded string
615 leftPad : function (val, size, ch) {
616 var result = String(val);
620 while (result.length < size) {
621 result = ch + result;
628 * Utility function that allows you to easily switch a string between two alternating values. The passed value
629 * is compared to the current string, and if they are equal, the other value that was passed in is returned. If
630 * they are already different, the first value passed in is returned. Note that this method returns the new value
631 * but does not change the current string.
633 // alternate sort directions
634 sort = sort.toggle('ASC', 'DESC');
636 // instead of conditional logic:
637 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
639 * @param {String} value The value to compare to the current string
640 * @param {String} other The new value to use if the string already equals the first value passed in
641 * @return {String} The new value
643 String.prototype.toggle = function(value, other){
644 return this == value ? other : value;
648 * Trims whitespace from either end of a string, leaving spaces within the string intact. Example:
651 alert('-' + s + '-'); //alerts "- foo bar -"
652 alert('-' + s.trim() + '-'); //alerts "-foo bar-"
654 * @return {String} The trimmed string
656 String.prototype.trim = function(){
657 var re = /^\s+|\s+$/g;
658 return function(){ return this.replace(re, ""); };
661 // here to prevent dependency on Date.js
663 Returns the number of milliseconds between this date and date
664 @param {Date} date (optional) Defaults to now
665 @return {Number} The diff in milliseconds
666 @member Date getElapsed
668 Date.prototype.getElapsed = function(date) {
669 return Math.abs((date || new Date()).getTime()-this.getTime());
676 Ext.applyIf(Number.prototype, {
678 * Checks whether or not the current number is within a desired range. If the number is already within the
679 * range it is returned, otherwise the min or max value is returned depending on which side of the range is
680 * exceeded. Note that this method returns the constrained value but does not change the current number.
681 * @param {Number} min The minimum number in the range
682 * @param {Number} max The maximum number in the range
683 * @return {Number} The constrained value if outside the range, otherwise the current value
685 constrain : function(min, max){
686 return Math.min(Math.max(this, min), max);
689 Ext.lib.Dom.getRegion = function(el) {
690 return Ext.lib.Region.getRegion(el);
691 }; Ext.lib.Region = function(t, r, b, l) {
701 Ext.lib.Region.prototype = {
702 contains : function(region) {
704 return ( region.left >= me.left &&
705 region.right <= me.right &&
706 region.top >= me.top &&
707 region.bottom <= me.bottom );
711 getArea : function() {
713 return ( (me.bottom - me.top) * (me.right - me.left) );
716 intersect : function(region) {
718 t = Math.max(me.top, region.top),
719 r = Math.min(me.right, region.right),
720 b = Math.min(me.bottom, region.bottom),
721 l = Math.max(me.left, region.left);
723 if (b >= t && r >= l) {
724 return new Ext.lib.Region(t, r, b, l);
728 union : function(region) {
730 t = Math.min(me.top, region.top),
731 r = Math.max(me.right, region.right),
732 b = Math.max(me.bottom, region.bottom),
733 l = Math.min(me.left, region.left);
735 return new Ext.lib.Region(t, r, b, l);
738 constrainTo : function(r) {
740 me.top = me.top.constrain(r.top, r.bottom);
741 me.bottom = me.bottom.constrain(r.top, r.bottom);
742 me.left = me.left.constrain(r.left, r.right);
743 me.right = me.right.constrain(r.left, r.right);
747 adjust : function(t, l, b, r) {
757 Ext.lib.Region.getRegion = function(el) {
758 var p = Ext.lib.Dom.getXY(el),
760 r = p[0] + el.offsetWidth,
761 b = p[1] + el.offsetHeight,
764 return new Ext.lib.Region(t, r, b, l);
765 }; Ext.lib.Point = function(x, y) {
766 if (Ext.isArray(x)) {
771 me.x = me.right = me.left = me[0] = x;
772 me.y = me.top = me.bottom = me[1] = y;
775 Ext.lib.Point.prototype = new Ext.lib.Region();
777 * @class Ext.DomHelper
779 Ext.apply(Ext.DomHelper,
782 afterbegin = 'afterbegin',
783 afterend = 'afterend',
784 beforebegin = 'beforebegin',
785 beforeend = 'beforeend',
786 confRe = /tag|children|cn|html$/i;
789 function doInsert(el, o, returnElement, pos, sibling, append){
793 newNode = createDom(o, null);
795 el.appendChild(newNode);
797 (sibling == 'firstChild' ? el : el.parentNode).insertBefore(newNode, el[sibling] || el);
800 newNode = Ext.DomHelper.insertHtml(pos, el, Ext.DomHelper.createHtml(o));
802 return returnElement ? Ext.get(newNode, true) : newNode;
807 function createDom(o, parentNode){
815 if (Ext.isArray(o)) { // Allow Arrays of siblings to be inserted
816 el = doc.createDocumentFragment(); // in one shot using a DocumentFragment
817 for (var i = 0, l = o.length; i < l; i++) {
820 } else if (typeof o == 'string') { // Allow a string as a child spec.
821 el = doc.createTextNode(o);
823 el = doc.createElement( o.tag || 'div' );
824 useSet = !!el.setAttribute; // In IE some elements don't have setAttribute
825 for (var attr in o) {
826 if(!confRe.test(attr)){
832 el.setAttribute(attr, val);
839 Ext.DomHelper.applyStyles(el, o.style);
841 if ((cn = o.children || o.cn)) {
844 el.innerHTML = o.html;
848 parentNode.appendChild(el);
855 * Creates a new Ext.Template from the DOM object spec.
856 * @param {Object} o The DOM object spec (and children)
857 * @return {Ext.Template} The new template
859 createTemplate : function(o){
860 var html = Ext.DomHelper.createHtml(o);
861 return new Ext.Template(html);
864 /** True to force the use of DOM instead of html fragments @type Boolean */
868 * Creates new DOM element(s) and inserts them before el.
869 * @param {Mixed} el The context element
870 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
871 * @param {Boolean} returnElement (optional) true to return a Ext.Element
872 * @return {HTMLElement/Ext.Element} The new node
875 insertBefore : function(el, o, returnElement){
876 return doInsert(el, o, returnElement, beforebegin);
880 * Creates new DOM element(s) and inserts them after el.
881 * @param {Mixed} el The context element
882 * @param {Object} o The DOM object spec (and children)
883 * @param {Boolean} returnElement (optional) true to return a Ext.Element
884 * @return {HTMLElement/Ext.Element} The new node
887 insertAfter : function(el, o, returnElement){
888 return doInsert(el, o, returnElement, afterend, 'nextSibling');
892 * Creates new DOM element(s) and inserts them as the first child of el.
893 * @param {Mixed} el The context element
894 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
895 * @param {Boolean} returnElement (optional) true to return a Ext.Element
896 * @return {HTMLElement/Ext.Element} The new node
899 insertFirst : function(el, o, returnElement){
900 return doInsert(el, o, returnElement, afterbegin, 'firstChild');
904 * Creates new DOM element(s) and appends them to el.
905 * @param {Mixed} el The context element
906 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
907 * @param {Boolean} returnElement (optional) true to return a Ext.Element
908 * @return {HTMLElement/Ext.Element} The new node
911 append: function(el, o, returnElement){
912 return doInsert(el, o, returnElement, beforeend, '', true);
916 * Creates new DOM element(s) without inserting them to the document.
917 * @param {Object/String} o The DOM object spec (and children) or raw HTML blob
918 * @return {HTMLElement} The new uninserted node
925 * @class Ext.Template
927 Ext.apply(Ext.Template.prototype, {
929 * @cfg {Boolean} disableFormats Specify <tt>true</tt> to disable format
930 * functions in the template. If the template does not contain
931 * {@link Ext.util.Format format functions}, setting <code>disableFormats</code>
932 * to true will reduce <code>{@link #apply}</code> time. Defaults to <tt>false</tt>.
934 var t = new Ext.Template(
935 '<div name="{id}">',
936 '<span class="{cls}">{name} {value}</span>',
939 compiled: true, // {@link #compile} immediately
940 disableFormats: true // reduce <code>{@link #apply}</code> time since no formatting
944 * For a list of available format functions, see {@link Ext.util.Format}.
946 disableFormats : false,
948 * See <code>{@link #disableFormats}</code>.
950 * @property disableFormats
954 * The regular expression used to match template variables
959 re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
960 argsRe : /^\s*['"](.*)["']\s*$/,
962 compileBRe : /(\r\n|\n)/g,
966 * Returns an HTML fragment of this template with the specified values applied.
967 * @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'})
968 * @return {String} The HTML fragment
971 applyTemplate : function(values){
973 useF = me.disableFormats !== true,
974 fm = Ext.util.Format,
978 return me.compiled(values);
980 function fn(m, name, format, args){
981 if (format && useF) {
982 if (format.substr(0, 5) == "this.") {
983 return tpl.call(format.substr(5), values[name], values);
986 // quoted values are required for strings in compiled templates,
987 // but for non compiled we need to strip them
988 // quoted reversed for jsmin
990 args = args.split(',');
991 for(var i = 0, len = args.length; i < len; i++){
992 args[i] = args[i].replace(re, "$1");
994 args = [values[name]].concat(args);
996 args = [values[name]];
998 return fm[format].apply(fm, args);
1001 return values[name] !== undefined ? values[name] : "";
1004 return me.html.replace(me.re, fn);
1008 * Compiles the template into an internal function, eliminating the RegEx overhead.
1009 * @return {Ext.Template} this
1012 compile : function(){
1014 fm = Ext.util.Format,
1015 useF = me.disableFormats !== true,
1016 sep = Ext.isGecko ? "+" : ",",
1019 function fn(m, name, format, args){
1021 args = args ? ',' + args : "";
1022 if(format.substr(0, 5) != "this."){
1023 format = "fm." + format + '(';
1025 format = 'this.call("'+ format.substr(5) + '", ';
1029 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
1031 return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
1034 // branched to use + in gecko and [].join() in others
1036 body = "this.compiled = function(values){ return '" +
1037 me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn) +
1040 body = ["this.compiled = function(values){ return ['"];
1041 body.push(me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn));
1042 body.push("'].join('');};");
1043 body = body.join('');
1049 // private function used to call members
1050 call : function(fnName, value, allValues){
1051 return this[fnName](value, allValues);
1054 Ext.Template.prototype.apply = Ext.Template.prototype.applyTemplate;
1056 * @class Ext.util.Functions
1059 Ext.util.Functions = {
1061 * Creates an interceptor function. The passed function is called before the original one. If it returns false,
1062 * the original one is not called. The resulting function returns the results of the original function.
1063 * The passed function is called with the parameters of the original function. Example usage:
1065 var sayHi = function(name){
1066 alert('Hi, ' + name);
1069 sayHi('Fred'); // alerts "Hi, Fred"
1071 // create a new function that validates input without
1072 // directly modifying the original function:
1073 var sayHiToFriend = Ext.createInterceptor(sayHi, function(name){
1074 return name == 'Brian';
1077 sayHiToFriend('Fred'); // no alert
1078 sayHiToFriend('Brian'); // alerts "Hi, Brian"
1080 * @param {Function} origFn The original function.
1081 * @param {Function} newFn The function to call before the original
1082 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
1083 * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
1084 * @return {Function} The new function
1086 createInterceptor: function(origFn, newFn, scope) {
1087 var method = origFn;
1088 if (!Ext.isFunction(newFn)) {
1096 newFn.method = origFn;
1097 return (newFn.apply(scope || me || window, args) !== false) ?
1098 origFn.apply(me || window, args) :
1105 * Creates a delegate (callback) that sets the scope to obj.
1106 * Call directly on any function. Example: <code>Ext.createDelegate(this.myFunction, this, [arg1, arg2])</code>
1107 * Will create a function that is automatically scoped to obj so that the <tt>this</tt> variable inside the
1108 * callback points to obj. Example usage:
1110 var sayHi = function(name){
1111 // Note this use of "this.text" here. This function expects to
1112 // execute within a scope that contains a text property. In this
1113 // example, the "this" variable is pointing to the btn object that
1114 // was passed in createDelegate below.
1115 alert('Hi, ' + name + '. You clicked the "' + this.text + '" button.');
1118 var btn = new Ext.Button({
1120 renderTo: Ext.getBody()
1123 // This callback will execute in the scope of the
1124 // button instance. Clicking the button alerts
1125 // "Hi, Fred. You clicked the "Say Hi" button."
1126 btn.on('click', Ext.createDelegate(sayHi, btn, ['Fred']));
1128 * @param {Function} fn The function to delegate.
1129 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
1130 * <b>If omitted, defaults to the browser window.</b>
1131 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
1132 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
1133 * if a number the args are inserted at the specified position
1134 * @return {Function} The new function
1136 createDelegate: function(fn, obj, args, appendArgs) {
1137 if (!Ext.isFunction(fn)) {
1141 var callArgs = args || arguments;
1142 if (appendArgs === true) {
1143 callArgs = Array.prototype.slice.call(arguments, 0);
1144 callArgs = callArgs.concat(args);
1146 else if (Ext.isNumber(appendArgs)) {
1147 callArgs = Array.prototype.slice.call(arguments, 0);
1148 // copy arguments first
1149 var applyArgs = [appendArgs, 0].concat(args);
1150 // create method call params
1151 Array.prototype.splice.apply(callArgs, applyArgs);
1154 return fn.apply(obj || window, callArgs);
1159 * Calls this function after the number of millseconds specified, optionally in a specific scope. Example usage:
1161 var sayHi = function(name){
1162 alert('Hi, ' + name);
1165 // executes immediately:
1168 // executes after 2 seconds:
1169 Ext.defer(sayHi, 2000, this, ['Fred']);
1171 // this syntax is sometimes useful for deferring
1172 // execution of an anonymous function:
1173 Ext.defer(function(){
1177 * @param {Function} fn The function to defer.
1178 * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately)
1179 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
1180 * <b>If omitted, defaults to the browser window.</b>
1181 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
1182 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
1183 * if a number the args are inserted at the specified position
1184 * @return {Number} The timeout id that can be used with clearTimeout
1186 defer: function(fn, millis, obj, args, appendArgs) {
1187 fn = Ext.util.Functions.createDelegate(fn, obj, args, appendArgs);
1189 return setTimeout(fn, millis);
1197 * Create a combined function call sequence of the original function + the passed function.
1198 * The resulting function returns the results of the original function.
1199 * The passed fcn is called with the parameters of the original function. Example usage:
1202 var sayHi = function(name){
1203 alert('Hi, ' + name);
1206 sayHi('Fred'); // alerts "Hi, Fred"
1208 var sayGoodbye = Ext.createSequence(sayHi, function(name){
1209 alert('Bye, ' + name);
1212 sayGoodbye('Fred'); // both alerts show
1214 * @param {Function} origFn The original function.
1215 * @param {Function} newFn The function to sequence
1216 * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed.
1217 * If omitted, defaults to the scope in which the original function is called or the browser window.
1218 * @return {Function} The new function
1220 createSequence: function(origFn, newFn, scope) {
1221 if (!Ext.isFunction(newFn)) {
1226 var retval = origFn.apply(this || window, arguments);
1227 newFn.apply(scope || this || window, arguments);
1235 * Shorthand for {@link Ext.util.Functions#defer}
1236 * @param {Function} fn The function to defer.
1237 * @param {Number} millis The number of milliseconds for the setTimeout call (if less than or equal to 0 the function is executed immediately)
1238 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
1239 * <b>If omitted, defaults to the browser window.</b>
1240 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
1241 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
1242 * if a number the args are inserted at the specified position
1243 * @return {Number} The timeout id that can be used with clearTimeout
1248 Ext.defer = Ext.util.Functions.defer;
1251 * Shorthand for {@link Ext.util.Functions#createInterceptor}
1252 * @param {Function} origFn The original function.
1253 * @param {Function} newFn The function to call before the original
1254 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the passed function is executed.
1255 * <b>If omitted, defaults to the scope in which the original function is called or the browser window.</b>
1256 * @return {Function} The new function
1261 Ext.createInterceptor = Ext.util.Functions.createInterceptor;
1264 * Shorthand for {@link Ext.util.Functions#createSequence}
1265 * @param {Function} origFn The original function.
1266 * @param {Function} newFn The function to sequence
1267 * @param {Object} scope (optional) The scope (this reference) in which the passed function is executed.
1268 * If omitted, defaults to the scope in which the original function is called or the browser window.
1269 * @return {Function} The new function
1274 Ext.createSequence = Ext.util.Functions.createSequence;
1277 * Shorthand for {@link Ext.util.Functions#createDelegate}
1278 * @param {Function} fn The function to delegate.
1279 * @param {Object} scope (optional) The scope (<code><b>this</b></code> reference) in which the function is executed.
1280 * <b>If omitted, defaults to the browser window.</b>
1281 * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
1282 * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
1283 * if a number the args are inserted at the specified position
1284 * @return {Function} The new function
1288 Ext.createDelegate = Ext.util.Functions.createDelegate;
1290 * @class Ext.util.Observable
1292 Ext.apply(Ext.util.Observable.prototype, function(){
1293 // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
1294 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
1296 function getMethodEvent(method){
1297 var e = (this.methodEvents = this.methodEvents ||
1298 {})[method], returnValue, v, cancel, obj = this;
1301 this.methodEvents[method] = e = {};
1302 e.originalFn = this[method];
1303 e.methodName = method;
1307 var makeCall = function(fn, scope, args){
1308 if((v = fn.apply(scope || obj, args)) !== undefined){
1309 if (typeof v == 'object') {
1310 if(v.returnValue !== undefined){
1311 returnValue = v.returnValue;
1315 cancel = !!v.cancel;
1327 this[method] = function(){
1328 var args = Array.prototype.slice.call(arguments, 0),
1330 returnValue = v = undefined;
1333 for(var i = 0, len = e.before.length; i < len; i++){
1335 makeCall(b.fn, b.scope, args);
1341 if((v = e.originalFn.apply(obj, args)) !== undefined){
1345 for(var i = 0, len = e.after.length; i < len; i++){
1347 makeCall(b.fn, b.scope, args);
1359 // these are considered experimental
1360 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
1361 // adds an 'interceptor' called before the original method
1362 beforeMethod : function(method, fn, scope){
1363 getMethodEvent.call(this, method).before.push({
1369 // adds a 'sequence' called after the original method
1370 afterMethod : function(method, fn, scope){
1371 getMethodEvent.call(this, method).after.push({
1377 removeMethodListener: function(method, fn, scope){
1378 var e = this.getMethodEvent(method);
1379 for(var i = 0, len = e.before.length; i < len; i++){
1380 if(e.before[i].fn == fn && e.before[i].scope == scope){
1381 e.before.splice(i, 1);
1385 for(var i = 0, len = e.after.length; i < len; i++){
1386 if(e.after[i].fn == fn && e.after[i].scope == scope){
1387 e.after.splice(i, 1);
1394 * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
1395 * @param {Object} o The Observable whose events this object is to relay.
1396 * @param {Array} events Array of event names to relay.
1398 relayEvents : function(o, events){
1400 function createHandler(ename){
1402 return me.fireEvent.apply(me, [ename].concat(Array.prototype.slice.call(arguments, 0)));
1405 for(var i = 0, len = events.length; i < len; i++){
1406 var ename = events[i];
1407 me.events[ename] = me.events[ename] || true;
1408 o.on(ename, createHandler(ename), me);
1413 * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling
1414 * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p>
1415 * <p>This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component.getBubbleTarget}. The default
1416 * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
1417 * access the required target more quickly.</p>
1418 * <p>Example:</p><pre><code>
1419 Ext.override(Ext.form.Field, {
1420 // Add functionality to Field's initComponent to enable the change event to bubble
1421 initComponent : Ext.form.Field.prototype.initComponent.createSequence(function() {
1422 this.enableBubble('change');
1425 // We know that we want Field's events to bubble directly to the FormPanel.
1426 getBubbleTarget : function() {
1427 if (!this.formPanel) {
1428 this.formPanel = this.findParentByType('form');
1430 return this.formPanel;
1434 var myForm = new Ext.formPanel({
1435 title: 'User Details',
1440 change: function() {
1441 // Title goes red if form has been modified.
1442 myForm.header.setStyle('color', 'red');
1447 * @param {String/Array} events The event name to bubble, or an Array of event names.
1449 enableBubble : function(events){
1451 if(!Ext.isEmpty(events)){
1452 events = Ext.isArray(events) ? events : Array.prototype.slice.call(arguments, 0);
1453 for(var i = 0, len = events.length; i < len; i++){
1454 var ename = events[i];
1455 ename = ename.toLowerCase();
1456 var ce = me.events[ename] || true;
1457 if (typeof ce == 'boolean') {
1458 ce = new Ext.util.Event(me, ename);
1459 me.events[ename] = ce;
1470 * Starts capture on the specified Observable. All events will be passed
1471 * to the supplied function with the event name + standard signature of the event
1472 * <b>before</b> the event is fired. If the supplied function returns false,
1473 * the event will not fire.
1474 * @param {Observable} o The Observable to capture events from.
1475 * @param {Function} fn The function to call when an event is fired.
1476 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Observable firing the event.
1479 Ext.util.Observable.capture = function(o, fn, scope){
1480 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
1485 * Sets observability on the passed class constructor.<p>
1486 * <p>This makes any event fired on any instance of the passed class also fire a single event through
1487 * the <i>class</i> allowing for central handling of events on many instances at once.</p>
1488 * <p>Usage:</p><pre><code>
1489 Ext.util.Observable.observeClass(Ext.data.Connection);
1490 Ext.data.Connection.on('beforerequest', function(con, options) {
1491 console.log('Ajax request made to ' + options.url);
1493 * @param {Function} c The class constructor to make observable.
1494 * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
1497 Ext.util.Observable.observeClass = function(c, listeners){
1500 Ext.apply(c, new Ext.util.Observable());
1501 Ext.util.Observable.capture(c.prototype, c.fireEvent, c);
1503 if(typeof listeners == 'object'){
1510 * @class Ext.EventManager
1512 Ext.apply(Ext.EventManager, function(){
1518 propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
1521 // note 1: IE fires ONLY the keydown event on specialkey autorepeat
1522 // note 2: Safari < 3.1, Gecko (Mac/Linux) & Opera fire only the keypress event on specialkey autorepeat
1523 // (research done by @Jan Wolter at http://unixpapa.com/js/key.html)
1524 useKeydown = Ext.isWebKit ?
1525 Ext.num(navigator.userAgent.match(/AppleWebKit\/(\d+)/)[1]) >= 525 :
1526 !((Ext.isGecko && !Ext.isWindows) || Ext.isOpera);
1530 doResizeEvent: function(){
1531 var h = D.getViewHeight(),
1532 w = D.getViewWidth();
1534 //whacky problem in IE where the resize event will fire even though the w/h are the same.
1535 if(curHeight != h || curWidth != w){
1536 resizeEvent.fire(curWidth = w, curHeight = h);
1541 * Adds a listener to be notified when the browser window is resized and provides resize event buffering (100 milliseconds),
1542 * passes new viewport width and height to handlers.
1543 * @param {Function} fn The handler function the window resize event invokes.
1544 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
1545 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
1547 onWindowResize : function(fn, scope, options){
1549 resizeEvent = new Ext.util.Event();
1550 resizeTask = new Ext.util.DelayedTask(this.doResizeEvent);
1551 Ext.EventManager.on(window, "resize", this.fireWindowResize, this);
1553 resizeEvent.addListener(fn, scope, options);
1556 // exposed only to allow manual firing
1557 fireWindowResize : function(){
1559 resizeTask.delay(100);
1564 * Adds a listener to be notified when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
1565 * @param {Function} fn The function the event invokes.
1566 * @param {Object} scope The scope (<code>this</code> reference) in which the handler function executes. Defaults to the browser window.
1567 * @param {boolean} options Options object as passed to {@link Ext.Element#addListener}
1569 onTextResize : function(fn, scope, options){
1571 textEvent = new Ext.util.Event();
1572 var textEl = new Ext.Element(document.createElement('div'));
1573 textEl.dom.className = 'x-text-resize';
1574 textEl.dom.innerHTML = 'X';
1575 textEl.appendTo(document.body);
1576 textSize = textEl.dom.offsetHeight;
1577 setInterval(function(){
1578 if(textEl.dom.offsetHeight != textSize){
1579 textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
1581 }, this.textResizeInterval);
1583 textEvent.addListener(fn, scope, options);
1587 * Removes the passed window resize listener.
1588 * @param {Function} fn The method the event invokes
1589 * @param {Object} scope The scope of handler
1591 removeResizeListener : function(fn, scope){
1593 resizeEvent.removeListener(fn, scope);
1598 fireResize : function(){
1600 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
1605 * The frequency, in milliseconds, to check for text resize events (defaults to 50)
1607 textResizeInterval : 50,
1610 * Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
1614 // protected, short accessor for useKeydown
1615 getKeyEvent : function(){
1616 return useKeydown ? 'keydown' : 'keypress';
1619 // protected for use inside the framework
1620 // detects whether we should use keydown or keypress based on the browser.
1621 useKeydown: useKeydown
1625 Ext.EventManager.on = Ext.EventManager.addListener;
1628 Ext.apply(Ext.EventObjectImpl.prototype, {
1629 /** Key constant @type Number */
1631 /** Key constant @type Number */
1633 /** Key constant @type Number */
1635 /** Key constant @type Number */
1637 /** Key constant @type Number */
1639 /** Key constant @type Number */
1641 /** Key constant @type Number */
1643 CONTROL : 17, // legacy
1644 /** Key constant @type Number */
1646 /** Key constant @type Number */
1648 /** Key constant @type Number */
1650 /** Key constant @type Number */
1652 /** Key constant @type Number */
1654 /** Key constant @type Number */
1656 PAGEUP : 33, // legacy
1657 /** Key constant @type Number */
1659 PAGEDOWN : 34, // legacy
1660 /** Key constant @type Number */
1662 /** Key constant @type Number */
1664 /** Key constant @type Number */
1666 /** Key constant @type Number */
1668 /** Key constant @type Number */
1670 /** Key constant @type Number */
1672 /** Key constant @type Number */
1674 /** Key constant @type Number */
1676 /** Key constant @type Number */
1678 /** Key constant @type Number */
1680 /** Key constant @type Number */
1682 /** Key constant @type Number */
1684 /** Key constant @type Number */
1686 /** Key constant @type Number */
1688 /** Key constant @type Number */
1690 /** Key constant @type Number */
1692 /** Key constant @type Number */
1694 /** Key constant @type Number */
1696 /** Key constant @type Number */
1698 /** Key constant @type Number */
1700 /** Key constant @type Number */
1702 /** Key constant @type Number */
1704 /** Key constant @type Number */
1706 /** Key constant @type Number */
1708 /** Key constant @type Number */
1710 /** Key constant @type Number */
1712 /** Key constant @type Number */
1714 /** Key constant @type Number */
1716 /** Key constant @type Number */
1718 /** Key constant @type Number */
1720 /** Key constant @type Number */
1722 /** Key constant @type Number */
1724 /** Key constant @type Number */
1726 /** Key constant @type Number */
1728 /** Key constant @type Number */
1730 /** Key constant @type Number */
1732 /** Key constant @type Number */
1734 /** Key constant @type Number */
1736 /** Key constant @type Number */
1738 /** Key constant @type Number */
1740 /** Key constant @type Number */
1742 /** Key constant @type Number */
1744 /** Key constant @type Number */
1746 /** Key constant @type Number */
1748 /** Key constant @type Number */
1750 /** Key constant @type Number */
1752 /** Key constant @type Number */
1754 /** Key constant @type Number */
1756 /** Key constant @type Number */
1758 /** Key constant @type Number */
1760 /** Key constant @type Number */
1762 /** Key constant @type Number */
1764 /** Key constant @type Number */
1766 /** Key constant @type Number */
1768 /** Key constant @type Number */
1770 /** Key constant @type Number */
1772 /** Key constant @type Number */
1774 /** Key constant @type Number */
1776 /** Key constant @type Number */
1778 /** Key constant @type Number */
1780 /** Key constant @type Number */
1782 /** Key constant @type Number */
1784 /** Key constant @type Number */
1786 /** Key constant @type Number */
1788 /** Key constant @type Number */
1790 /** Key constant @type Number */
1792 /** Key constant @type Number */
1794 /** Key constant @type Number */
1796 /** Key constant @type Number */
1798 /** Key constant @type Number */
1800 /** Key constant @type Number */
1802 /** Key constant @type Number */
1804 /** Key constant @type Number */
1808 isNavKeyPress : function(){
1810 k = this.normalizeKey(me.keyCode);
1811 return (k >= 33 && k <= 40) || // Page Up/Down, End, Home, Left, Up, Right, Down
1817 isSpecialKey : function(){
1818 var k = this.normalizeKey(this.keyCode);
1819 return (this.type == 'keypress' && this.ctrlKey) ||
1820 this.isNavKeyPress() ||
1821 (k == this.BACKSPACE) || // Backspace
1822 (k >= 16 && k <= 20) || // Shift, Ctrl, Alt, Pause, Caps Lock
1823 (k >= 44 && k <= 46); // Print Screen, Insert, Delete
1826 getPoint : function(){
1827 return new Ext.lib.Point(this.xy[0], this.xy[1]);
1831 * Returns true if the control, meta, shift or alt key was pressed during this event.
1834 hasModifier : function(){
1835 return ((this.ctrlKey || this.altKey) || this.shiftKey);
1838 * @class Ext.Element
1840 Ext.Element.addMethods({
1842 * Stops the specified event(s) from bubbling and optionally prevents the default action
1843 * @param {String/Array} eventName an event / array of events to stop from bubbling
1844 * @param {Boolean} preventDefault (optional) true to prevent the default action too
1845 * @return {Ext.Element} this
1847 swallowEvent : function(eventName, preventDefault) {
1850 e.stopPropagation();
1851 if (preventDefault) {
1856 if (Ext.isArray(eventName)) {
1857 Ext.each(eventName, function(e) {
1862 me.on(eventName, fn);
1867 * Create an event handler on this element such that when the event fires and is handled by this element,
1868 * it will be relayed to another object (i.e., fired again as if it originated from that object instead).
1869 * @param {String} eventName The type of event to relay
1870 * @param {Object} object Any object that extends {@link Ext.util.Observable} that will provide the context
1871 * for firing the relayed event
1873 relayEvent : function(eventName, observable) {
1874 this.on(eventName, function(e) {
1875 observable.fireEvent(eventName, e);
1880 * Removes worthless text nodes
1881 * @param {Boolean} forceReclean (optional) By default the element
1882 * keeps track if it has been cleaned already so
1883 * you can call this over and over. However, if you update the element and
1884 * need to force a reclean, you can pass true.
1886 clean : function(forceReclean) {
1892 if (Ext.Element.data(dom, 'isCleaned') && forceReclean !== true) {
1897 var nx = n.nextSibling;
1898 if (n.nodeType == 3 && !(/\S/.test(n.nodeValue))) {
1906 Ext.Element.data(dom, 'isCleaned', true);
1911 * Direct access to the Updater {@link Ext.Updater#update} method. The method takes the same object
1912 * parameter as {@link Ext.Updater#update}
1913 * @return {Ext.Element} this
1916 var updateManager = this.getUpdater();
1917 updateManager.update.apply(updateManager, arguments);
1923 * Gets this element's {@link Ext.Updater Updater}
1924 * @return {Ext.Updater} The Updater
1926 getUpdater : function() {
1927 return this.updateManager || (this.updateManager = new Ext.Updater(this));
1931 * Update the innerHTML of this element, optionally searching for and processing scripts
1932 * @param {String} html The new HTML
1933 * @param {Boolean} loadScripts (optional) True to look for and process scripts (defaults to false)
1934 * @param {Function} callback (optional) For async script loading you can be notified when the update completes
1935 * @return {Ext.Element} this
1937 update : function(html, loadScripts, callback) {
1943 if (loadScripts !== true) {
1944 this.dom.innerHTML = html;
1945 if (typeof callback == 'function') {
1954 html += '<span id="' + id + '"></span>';
1956 Ext.lib.Event.onAvailable(id, function() {
1958 hd = DOC.getElementsByTagName("head")[0],
1959 re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig,
1960 srcRe = /\ssrc=([\'\"])(.*?)\1/i,
1961 typeRe = /\stype=([\'\"])(.*?)\1/i,
1969 while ((match = re.exec(html))) {
1971 srcMatch = attrs ? attrs.match(srcRe) : false;
1972 if (srcMatch && srcMatch[2]) {
1973 s = DOC.createElement("script");
1974 s.src = srcMatch[2];
1975 typeMatch = attrs.match(typeRe);
1976 if (typeMatch && typeMatch[2]) {
1977 s.type = typeMatch[2];
1980 } else if (match[2] && match[2].length > 0) {
1981 if (window.execScript) {
1982 window.execScript(match[2]);
1984 window.eval(match[2]);
1989 el = DOC.getElementById(id);
1994 if (typeof callback == 'function') {
1998 dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
2002 // inherit docs, overridden so we can add removeAnchor
2003 removeAllListeners : function() {
2004 this.removeAnchor();
2005 Ext.EventManager.removeAll(this.dom);
2010 * Creates a proxy element of this element
2011 * @param {String/Object} config The class name of the proxy element or a DomHelper config object
2012 * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
2013 * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
2014 * @return {Ext.Element} The new proxy element
2016 createProxy : function(config, renderTo, matchBox) {
2017 config = (typeof config == 'object') ? config : {tag : "div", cls: config};
2020 proxy = renderTo ? Ext.DomHelper.append(renderTo, config, true) :
2021 Ext.DomHelper.insertBefore(me.dom, config, true);
2023 if (matchBox && me.setBox && me.getBox) { // check to make sure Element.position.js is loaded
2024 proxy.setBox(me.getBox());
2030 Ext.Element.prototype.getUpdateManager = Ext.Element.prototype.getUpdater;
2032 * @class Ext.Element
2034 Ext.Element.addMethods({
2036 * Gets the x,y coordinates specified by the anchor position on the element.
2037 * @param {String} anchor (optional) The specified anchor position (defaults to "c"). See {@link #alignTo}
2038 * for details on supported anchor positions.
2039 * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead
2040 * of page coordinates
2041 * @param {Object} size (optional) An object containing the size to use for calculating anchor position
2042 * {width: (target width), height: (target height)} (defaults to the element's current size)
2043 * @return {Array} [x, y] An array containing the element's x and y coordinates
2045 getAnchorXY : function(anchor, local, s){
2046 //Passing a different size is useful for pre-calculating anchors,
2047 //especially for anchored animations that change the el size.
2048 anchor = (anchor || "tl").toLowerCase();
2052 vp = me.dom == document.body || me.dom == document,
2053 w = s.width || vp ? Ext.lib.Dom.getViewWidth() : me.getWidth(),
2054 h = s.height || vp ? Ext.lib.Dom.getViewHeight() : me.getHeight(),
2058 scroll = me.getScroll(),
2059 extraX = vp ? scroll.left : !local ? o[0] : 0,
2060 extraY = vp ? scroll.top : !local ? o[1] : 0,
2062 c : [r(w * 0.5), r(h * 0.5)],
2063 t : [r(w * 0.5), 0],
2064 l : [0, r(h * 0.5)],
2065 r : [w, r(h * 0.5)],
2066 b : [r(w * 0.5), h],
2074 return [xy[0] + extraX, xy[1] + extraY];
2078 * Anchors an element to another element and realigns it when the window is resized.
2079 * @param {Mixed} element The element to align to.
2080 * @param {String} position The position to align to.
2081 * @param {Array} offsets (optional) Offset the positioning by [x, y]
2082 * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
2083 * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
2084 * is a number, it is used as the buffer delay (defaults to 50ms).
2085 * @param {Function} callback The function to call after the animation finishes
2086 * @return {Ext.Element} this
2088 anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
2091 scroll = !Ext.isEmpty(monitorScroll),
2092 action = function(){
2093 Ext.fly(dom).alignTo(el, alignment, offsets, animate);
2094 Ext.callback(callback, Ext.fly(dom));
2096 anchor = this.getAnchor();
2098 // previous listener anchor, remove it
2099 this.removeAnchor();
2105 Ext.EventManager.onWindowResize(action, null);
2108 Ext.EventManager.on(window, 'scroll', action, null,
2109 {buffer: !isNaN(monitorScroll) ? monitorScroll : 50});
2111 action.call(me); // align immediately
2116 * Remove any anchor to this element. See {@link #anchorTo}.
2117 * @return {Ext.Element} this
2119 removeAnchor : function(){
2121 anchor = this.getAnchor();
2123 if(anchor && anchor.fn){
2124 Ext.EventManager.removeResizeListener(anchor.fn);
2126 Ext.EventManager.un(window, 'scroll', anchor.fn);
2134 getAnchor : function(){
2135 var data = Ext.Element.data,
2140 var anchor = data(dom, '_anchor');
2143 anchor = data(dom, '_anchor', {});
2149 * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
2150 * supported position values.
2151 * @param {Mixed} element The element to align to.
2152 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
2153 * @param {Array} offsets (optional) Offset the positioning by [x, y]
2154 * @return {Array} [x, y]
2156 getAlignToXY : function(el, p, o){
2160 throw "Element.alignToXY with an element that doesn't exist";
2164 p = (!p || p == "?" ? "tl-bl?" : (!(/-/).test(p) && p !== "" ? "tl-" + p : p || "tl-bl")).toLowerCase();
2172 //constrain the aligned el to viewport if necessary
2176 dw = Ext.lib.Dom.getViewWidth() -10, // 10px of margin for ie
2177 dh = Ext.lib.Dom.getViewHeight()-10, // 10px of margin for ie
2185 docElement = doc.documentElement,
2187 scrollX = (docElement.scrollLeft || docBody.scrollLeft || 0)+5,
2188 scrollY = (docElement.scrollTop || docBody.scrollTop || 0)+5,
2189 c = false, //constrain to viewport
2192 m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
2195 throw "Element.alignTo with an invalid alignment " + p;
2202 //Subtract the aligned el's internal xy from the target's offset xy
2203 //plus custom offset to get the aligned el's new offset xy
2204 a1 = me.getAnchorXY(p1, true);
2205 a2 = el.getAnchorXY(p2, false);
2207 x = a2[0] - a1[0] + o[0];
2208 y = a2[1] - a1[1] + o[1];
2214 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
2215 //perpendicular to the vp border, allow the aligned el to slide on that border,
2216 //otherwise swap the aligned el to the opposite border of the target.
2218 p1x = p1.charAt(p1.length-1);
2220 p2x = p2.charAt(p2.length-1);
2221 swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
2222 swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
2225 if (x + w > dw + scrollX) {
2226 x = swapX ? r.left-w : dw+scrollX-w;
2229 x = swapX ? r.right : scrollX;
2231 if (y + h > dh + scrollY) {
2232 y = swapY ? r.top-h : dh+scrollY-h;
2235 y = swapY ? r.bottom : scrollY;
2242 * Aligns this element with another element relative to the specified anchor points. If the other element is the
2243 * document it aligns it to the viewport.
2244 * The position parameter is optional, and can be specified in any one of the following formats:
2246 * <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
2247 * <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
2248 * The element being aligned will position its top-left corner (tl) to that point. <i>This method has been
2249 * deprecated in favor of the newer two anchor syntax below</i>.</li>
2250 * <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
2251 * element's anchor point, and the second value is used as the target's anchor point.</li>
2253 * In addition to the anchor points, the position parameter also supports the "?" character. If "?" is passed at the end of
2254 * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
2255 * the viewport if necessary. Note that the element being aligned might be swapped to align to a different position than
2256 * that specified in order to enforce the viewport constraints.
2257 * Following are all of the supported anchor positions:
2260 ----- -----------------------------
2261 tl The top left corner (default)
2262 t The center of the top edge
2263 tr The top right corner
2264 l The center of the left edge
2265 c In the center of the element
2266 r The center of the right edge
2267 bl The bottom left corner
2268 b The center of the bottom edge
2269 br The bottom right corner
2273 // align el to other-el using the default positioning ("tl-bl", non-constrained)
2274 el.alignTo("other-el");
2276 // align the top left corner of el with the top right corner of other-el (constrained to viewport)
2277 el.alignTo("other-el", "tr?");
2279 // align the bottom right corner of el with the center left edge of other-el
2280 el.alignTo("other-el", "br-l?");
2282 // align the center of el with the bottom left corner of other-el and
2283 // adjust the x position by -6 pixels (and the y position by 0)
2284 el.alignTo("other-el", "c-bl", [-6, 0]);
2286 * @param {Mixed} element The element to align to.
2287 * @param {String} position (optional, defaults to "tl-bl?") The position to align to.
2288 * @param {Array} offsets (optional) Offset the positioning by [x, y]
2289 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
2290 * @return {Ext.Element} this
2292 alignTo : function(element, position, offsets, animate){
2294 return me.setXY(me.getAlignToXY(element, position, offsets),
2295 me.preanim && !!animate ? me.preanim(arguments, 3) : false);
2298 // private ==> used outside of core
2299 adjustForConstraints : function(xy, parent, offsets){
2300 return this.getConstrainToXY(parent || document, false, offsets, xy) || xy;
2303 // private ==> used outside of core
2304 getConstrainToXY : function(el, local, offsets, proposedXY){
2305 var os = {top:0, left:0, bottom:0, right: 0};
2307 return function(el, local, offsets, proposedXY){
2309 offsets = offsets ? Ext.applyIf(offsets, os) : os;
2311 var vw, vh, vx = 0, vy = 0;
2312 if(el.dom == document.body || el.dom == document){
2313 vw =Ext.lib.Dom.getViewWidth();
2314 vh = Ext.lib.Dom.getViewHeight();
2316 vw = el.dom.clientWidth;
2317 vh = el.dom.clientHeight;
2319 var vxy = el.getXY();
2325 var s = el.getScroll();
2327 vx += offsets.left + s.left;
2328 vy += offsets.top + s.top;
2330 vw -= offsets.right;
2331 vh -= offsets.bottom;
2335 xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]),
2336 x = xy[0], y = xy[1],
2337 offset = this.getConstrainOffset(),
2338 w = this.dom.offsetWidth + offset,
2339 h = this.dom.offsetHeight + offset;
2341 // only move it if it needs it
2344 // first validate right/bottom
2353 // then make sure top/left isn't negative
2362 return moved ? [x, y] : false;
2368 // el = Ext.get(el);
2369 // offsets = Ext.applyIf(offsets || {}, {top : 0, left : 0, bottom : 0, right : 0});
2373 // s = el.getScroll(),
2374 // vxy = el.getXY(),
2375 // vx = offsets.left + s.left,
2376 // vy = offsets.top + s.top,
2377 // vw = -offsets.right,
2378 // vh = -offsets.bottom,
2381 // xy = proposedXY || (!local ? me.getXY() : [me.getLeft(true), me.getTop(true)]),
2384 // w = me.dom.offsetWidth, h = me.dom.offsetHeight,
2385 // moved = false; // only move it if it needs it
2388 // if(el.dom == doc.body || el.dom == doc){
2389 // vw += Ext.lib.Dom.getViewWidth();
2390 // vh += Ext.lib.Dom.getViewHeight();
2392 // vw += el.dom.clientWidth;
2393 // vh += el.dom.clientHeight;
2400 // // first validate right/bottom
2401 // if(x + w > vx + vw){
2405 // if(y + h > vy + vh){
2409 // // then make sure top/left isn't negative
2418 // return moved ? [x, y] : false;
2421 // private, used internally
2422 getConstrainOffset : function(){
2427 * Calculates the x, y to center this element on the screen
2428 * @return {Array} The x, y values [x, y]
2430 getCenterXY : function(){
2431 return this.getAlignToXY(document, 'c-c');
2435 * Centers the Element in either the viewport, or another Element.
2436 * @param {Mixed} centerIn (optional) The element in which to center the element.
2438 center : function(centerIn){
2439 return this.alignTo(centerIn || document, 'c-c');
2443 * @class Ext.Element
2445 Ext.Element.addMethods({
2447 * Creates a {@link Ext.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
2448 * @param {String} selector The CSS selector
2449 * @param {Boolean} unique (optional) True to create a unique Ext.Element for each child (defaults to false, which creates a single shared flyweight object)
2450 * @return {CompositeElement/CompositeElementLite} The composite element
2452 select : function(selector, unique){
2453 return Ext.Element.select(selector, unique, this.dom);
2456 * @class Ext.Element
2458 Ext.apply(Ext.Element.prototype, function() {
2459 var GETDOM = Ext.getDom,
2465 * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
2466 * @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.
2467 * @param {String} where (optional) 'before' or 'after' defaults to before
2468 * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Ext.Element
2469 * @return {Ext.Element} The inserted Element. If an array is passed, the last inserted element is returned.
2471 insertSibling: function(el, where, returnDom){
2474 isAfter = (where || 'before').toLowerCase() == 'after',
2477 if(Ext.isArray(el)){
2479 Ext.each(el, function(e) {
2480 rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
2490 if(el.nodeType || el.dom){
2491 rt = me.dom.parentNode.insertBefore(GETDOM(el), isAfter ? me.dom.nextSibling : me.dom);
2496 if (isAfter && !me.dom.nextSibling) {
2497 rt = DH.append(me.dom.parentNode, el, !returnDom);
2499 rt = DH[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
2506 * @class Ext.Element
2509 // special markup used throughout Ext when box wrapping elements
2510 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>';
2512 Ext.Element.addMethods(function(){
2513 var INTERNAL = "_internal",
2514 pxMatch = /(\d+\.?\d+)px/;
2517 * More flexible version of {@link #setStyle} for setting style properties.
2518 * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
2519 * a function which returns such a specification.
2520 * @return {Ext.Element} this
2522 applyStyles : function(style){
2523 Ext.DomHelper.applyStyles(this.dom, style);
2528 * Returns an object with properties matching the styles requested.
2529 * For example, el.getStyles('color', 'font-size', 'width') might return
2530 * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
2531 * @param {String} style1 A style name
2532 * @param {String} style2 A style name
2533 * @param {String} etc.
2534 * @return {Object} The style object
2536 getStyles : function(){
2538 Ext.each(arguments, function(v) {
2539 ret[v] = this.getStyle(v);
2545 // private ==> used by ext full
2546 setOverflow : function(v){
2548 if(v=='auto' && Ext.isMac && Ext.isGecko2){ // work around stupid FF 2.0/Mac scroll bar bug
2549 dom.style.overflow = 'hidden';
2550 (function(){dom.style.overflow = 'auto';}).defer(1);
2552 dom.style.overflow = v;
2557 * <p>Wraps the specified element with a special 9 element markup/CSS block that renders by default as
2558 * a gray container with a gradient background, rounded corners and a 4-way shadow.</p>
2559 * <p>This special markup is used throughout Ext when box wrapping elements ({@link Ext.Button},
2560 * {@link Ext.Panel} when <tt>{@link Ext.Panel#frame frame=true}</tt>, {@link Ext.Window}). The markup
2561 * is of this form:</p>
2563 Ext.Element.boxMarkup =
2564 '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div>
2565 <div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div>
2566 <div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
2568 * <p>Example usage:</p>
2571 Ext.get("foo").boxWrap();
2573 // You can also add a custom class and use CSS inheritance rules to customize the box look.
2574 // 'x-box-blue' is a built-in alternative -- look at the related CSS definitions as an example
2575 // for how to create a custom box wrap style.
2576 Ext.get("foo").boxWrap().addClass("x-box-blue");
2578 * @param {String} class (optional) A base CSS class to apply to the containing wrapper element
2579 * (defaults to <tt>'x-box'</tt>). Note that there are a number of CSS rules that are dependent on
2580 * this name to make the overall effect work, so if you supply an alternate base class, make sure you
2581 * also supply all of the necessary rules.
2582 * @return {Ext.Element} The outermost wrapping element of the created box structure.
2584 boxWrap : function(cls){
2585 cls = cls || 'x-box';
2586 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)));
2587 Ext.DomQuery.selectNode('.' + cls + '-mc', el.dom).appendChild(this.dom);
2592 * Set the size of this Element. If animation is true, both width and height will be animated concurrently.
2593 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
2594 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels).</li>
2595 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
2596 * <li>A size object in the format <code>{width: widthValue, height: heightValue}</code>.</li>
2598 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
2599 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels).</li>
2600 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
2602 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
2603 * @return {Ext.Element} this
2605 setSize : function(width, height, animate){
2607 if(typeof width == 'object'){ // in case of object from getSize()
2608 height = width.height;
2609 width = width.width;
2611 width = me.adjustWidth(width);
2612 height = me.adjustHeight(height);
2613 if(!animate || !me.anim){
2614 me.dom.style.width = me.addUnits(width);
2615 me.dom.style.height = me.addUnits(height);
2617 me.anim({width: {to: width}, height: {to: height}}, me.preanim(arguments, 2));
2623 * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
2624 * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
2625 * if a height has not been set using CSS.
2628 getComputedHeight : function(){
2630 h = Math.max(me.dom.offsetHeight, me.dom.clientHeight);
2632 h = parseFloat(me.getStyle('height')) || 0;
2633 if(!me.isBorderBox()){
2634 h += me.getFrameWidth('tb');
2641 * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
2642 * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
2643 * if a width has not been set using CSS.
2646 getComputedWidth : function(){
2647 var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
2649 w = parseFloat(this.getStyle('width')) || 0;
2650 if(!this.isBorderBox()){
2651 w += this.getFrameWidth('lr');
2658 * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
2659 for more information about the sides.
2660 * @param {String} sides
2663 getFrameWidth : function(sides, onlyContentBox){
2664 return onlyContentBox && this.isBorderBox() ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
2668 * Sets up event handlers to add and remove a css class when the mouse is over this element
2669 * @param {String} className
2670 * @return {Ext.Element} this
2672 addClassOnOver : function(className){
2675 Ext.fly(this, INTERNAL).addClass(className);
2678 Ext.fly(this, INTERNAL).removeClass(className);
2685 * Sets up event handlers to add and remove a css class when this element has the focus
2686 * @param {String} className
2687 * @return {Ext.Element} this
2689 addClassOnFocus : function(className){
2690 this.on("focus", function(){
2691 Ext.fly(this, INTERNAL).addClass(className);
2693 this.on("blur", function(){
2694 Ext.fly(this, INTERNAL).removeClass(className);
2700 * 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)
2701 * @param {String} className
2702 * @return {Ext.Element} this
2704 addClassOnClick : function(className){
2706 this.on("mousedown", function(){
2707 Ext.fly(dom, INTERNAL).addClass(className);
2708 var d = Ext.getDoc(),
2710 Ext.fly(dom, INTERNAL).removeClass(className);
2711 d.removeListener("mouseup", fn);
2713 d.on("mouseup", fn);
2719 * <p>Returns the dimensions of the element available to lay content out in.<p>
2720 * <p>If the element (or any ancestor element) has CSS style <code>display : none</code>, the dimensions will be zero.</p>
2721 * example:<pre><code>
2722 var vpSize = Ext.getBody().getViewSize();
2724 // all Windows created afterwards will have a default value of 90% height and 95% width
2725 Ext.Window.override({
2726 width: vpSize.width * 0.9,
2727 height: vpSize.height * 0.95
2729 // To handle window resizing you would have to hook onto onWindowResize.
2732 * getViewSize utilizes clientHeight/clientWidth which excludes sizing of scrollbars.
2733 * To obtain the size including scrollbars, use getStyleSize
2735 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
2738 getViewSize : function(){
2741 isDoc = (d == doc || d == doc.body);
2743 // If the body, use Ext.lib.Dom
2745 var extdom = Ext.lib.Dom;
2747 width : extdom.getViewWidth(),
2748 height : extdom.getViewHeight()
2751 // Else use clientHeight/clientWidth
2754 width : d.clientWidth,
2755 height : d.clientHeight
2761 * <p>Returns the dimensions of the element available to lay content out in.<p>
2763 * getStyleSize utilizes prefers style sizing if present, otherwise it chooses the larger of offsetHeight/clientHeight and offsetWidth/clientWidth.
2764 * To obtain the size excluding scrollbars, use getViewSize
2766 * Sizing of the document body is handled at the adapter level which handles special cases for IE and strict modes, etc.
2769 getStyleSize : function(){
2774 isDoc = (d == doc || d == doc.body),
2777 // If the body, use Ext.lib.Dom
2779 var extdom = Ext.lib.Dom;
2781 width : extdom.getViewWidth(),
2782 height : extdom.getViewHeight()
2785 // Use Styles if they are set
2786 if(s.width && s.width != 'auto'){
2787 w = parseFloat(s.width);
2788 if(me.isBorderBox()){
2789 w -= me.getFrameWidth('lr');
2792 // Use Styles if they are set
2793 if(s.height && s.height != 'auto'){
2794 h = parseFloat(s.height);
2795 if(me.isBorderBox()){
2796 h -= me.getFrameWidth('tb');
2799 // Use getWidth/getHeight if style not set.
2800 return {width: w || me.getWidth(true), height: h || me.getHeight(true)};
2804 * Returns the size of the element.
2805 * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
2806 * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
2808 getSize : function(contentSize){
2809 return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
2813 * Forces the browser to repaint this element
2814 * @return {Ext.Element} this
2816 repaint : function(){
2818 this.addClass("x-repaint");
2819 setTimeout(function(){
2820 Ext.fly(dom).removeClass("x-repaint");
2826 * Disables text selection for this element (normalized across browsers)
2827 * @return {Ext.Element} this
2829 unselectable : function(){
2830 this.dom.unselectable = "on";
2831 return this.swallowEvent("selectstart", true).
2832 applyStyles("-moz-user-select:none;-khtml-user-select:none;").
2833 addClass("x-unselectable");
2837 * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
2838 * then it returns the calculated width of the sides (see getPadding)
2839 * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
2840 * @return {Object/Number}
2842 getMargins : function(side){
2845 hash = {t:"top", l:"left", r:"right", b: "bottom"},
2849 for (key in me.margins){
2850 o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
2854 return me.addStyles.call(me, side, me.margins);
2860 * @class Ext.Element
2862 Ext.Element.addMethods({
2864 * 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.
2865 * @param {Object} box The box to fill {x, y, width, height}
2866 * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
2867 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
2868 * @return {Ext.Element} this
2870 setBox : function(box, adjust, animate){
2874 if((adjust && !me.autoBoxAdjust) && !me.isBorderBox()){
2875 w -= (me.getBorderWidth("lr") + me.getPadding("lr"));
2876 h -= (me.getBorderWidth("tb") + me.getPadding("tb"));
2878 me.setBounds(box.x, box.y, w, h, me.animTest.call(me, arguments, animate, 2));
2883 * Return an object defining the area of this Element which can be passed to {@link #setBox} to
2884 * set another Element's size/location to match this element.
2885 * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
2886 * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
2887 * @return {Object} box An object in the format<pre><code>
2889 x: <Element's X position>,
2890 y: <Element's Y position>,
2891 width: <Element's width>,
2892 height: <Element's height>,
2893 bottom: <Element's lower bound>,
2894 right: <Element's rightmost bound>
2897 * The returned object may also be addressed as an Array where index 0 contains the X position
2898 * and index 1 contains the Y position. So the result may also be used for {@link #setXY}
2900 getBox : function(contentBox, local) {
2905 getBorderWidth = me.getBorderWidth,
2906 getPadding = me.getPadding,
2914 left = parseInt(me.getStyle("left"), 10) || 0;
2915 top = parseInt(me.getStyle("top"), 10) || 0;
2918 var el = me.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
2920 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
2922 l = getBorderWidth.call(me, "l") + getPadding.call(me, "l");
2923 r = getBorderWidth.call(me, "r") + getPadding.call(me, "r");
2924 t = getBorderWidth.call(me, "t") + getPadding.call(me, "t");
2925 b = getBorderWidth.call(me, "b") + getPadding.call(me, "b");
2926 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)};
2928 bx.right = bx.x + bx.width;
2929 bx.bottom = bx.y + bx.height;
2934 * Move this element relative to its current position.
2935 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
2936 * @param {Number} distance How far to move the element in pixels
2937 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
2938 * @return {Ext.Element} this
2940 move : function(direction, distance, animate){
2945 left = [x - distance, y],
2946 right = [x + distance, y],
2947 top = [x, y - distance],
2948 bottom = [x, y + distance],
2962 direction = direction.toLowerCase();
2963 me.moveTo(hash[direction][0], hash[direction][1], me.animTest.call(me, arguments, animate, 2));
2967 * Quick set left and top adding default units
2968 * @param {String} left The left CSS property value
2969 * @param {String} top The top CSS property value
2970 * @return {Ext.Element} this
2972 setLeftTop : function(left, top){
2974 style = me.dom.style;
2975 style.left = me.addUnits(left);
2976 style.top = me.addUnits(top);
2981 * Returns the region of the given element.
2982 * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
2983 * @return {Region} A Ext.lib.Region containing "top, left, bottom, right" member data.
2985 getRegion : function(){
2986 return Ext.lib.Dom.getRegion(this.dom);
2990 * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
2991 * @param {Number} x X value for new position (coordinates are page-based)
2992 * @param {Number} y Y value for new position (coordinates are page-based)
2993 * @param {Mixed} width The new width. This may be one of:<div class="mdetail-params"><ul>
2994 * <li>A Number specifying the new width in this Element's {@link #defaultUnit}s (by default, pixels)</li>
2995 * <li>A String used to set the CSS width style. Animation may <b>not</b> be used.
2997 * @param {Mixed} height The new height. This may be one of:<div class="mdetail-params"><ul>
2998 * <li>A Number specifying the new height in this Element's {@link #defaultUnit}s (by default, pixels)</li>
2999 * <li>A String used to set the CSS height style. Animation may <b>not</b> be used.</li>
3001 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3002 * @return {Ext.Element} this
3004 setBounds : function(x, y, width, height, animate){
3006 if (!animate || !me.anim) {
3007 me.setSize(width, height);
3008 me.setLocation(x, y);
3010 me.anim({points: {to: [x, y]},
3011 width: {to: me.adjustWidth(width)},
3012 height: {to: me.adjustHeight(height)}},
3013 me.preanim(arguments, 4),
3020 * Sets the element's position and size the specified region. If animation is true then width, height, x and y will be animated concurrently.
3021 * @param {Ext.lib.Region} region The region to fill
3022 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3023 * @return {Ext.Element} this
3025 setRegion : function(region, animate) {
3026 return this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.animTest.call(this, arguments, animate, 1));
3029 * @class Ext.Element
3031 Ext.Element.addMethods({
3033 * 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().
3034 * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
3035 * @param {Number} value The new scroll value
3036 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3037 * @return {Element} this
3039 scrollTo : function(side, value, animate) {
3040 //check if we're scrolling top or left
3041 var top = /top/i.test(side),
3045 if (!animate || !me.anim) {
3046 // just setting the value, so grab the direction
3047 prop = 'scroll' + (top ? 'Top' : 'Left');
3051 // if scrolling top, we need to grab scrollLeft, if left, scrollTop
3052 prop = 'scroll' + (top ? 'Left' : 'Top');
3053 me.anim({scroll: {to: top ? [dom[prop], value] : [value, dom[prop]]}}, me.preanim(arguments, 2), 'scroll');
3059 * Scrolls this element into view within the passed container.
3060 * @param {Mixed} container (optional) The container element to scroll (defaults to document.body). Should be a
3061 * string (id), dom node, or Ext.Element.
3062 * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
3063 * @return {Ext.Element} this
3065 scrollIntoView : function(container, hscroll) {
3066 var c = Ext.getDom(container) || Ext.getBody().dom,
3068 o = this.getOffsetsTo(c),
3069 l = o[0] + c.scrollLeft,
3070 t = o[1] + c.scrollTop,
3071 b = t + el.offsetHeight,
3072 r = l + el.offsetWidth,
3073 ch = c.clientHeight,
3074 ct = parseInt(c.scrollTop, 10),
3075 cl = parseInt(c.scrollLeft, 10),
3077 cr = cl + c.clientWidth;
3079 if (el.offsetHeight > ch || t < ct) {
3085 // corrects IE, other browsers will ignore
3086 c.scrollTop = c.scrollTop;
3088 if (hscroll !== false) {
3089 if (el.offsetWidth > c.clientWidth || l < cl) {
3093 c.scrollLeft = r - c.clientWidth;
3095 c.scrollLeft = c.scrollLeft;
3101 scrollChildIntoView : function(child, hscroll) {
3102 Ext.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
3106 * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
3107 * within this element's scrollable range.
3108 * @param {String} direction Possible values are: "l" (or "left"), "r" (or "right"), "t" (or "top", or "up"), "b" (or "bottom", or "down").
3109 * @param {Number} distance How far to scroll the element in pixels
3110 * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
3111 * @return {Boolean} Returns true if a scroll was triggered or false if the element
3112 * was scrolled as far as it could go.
3114 scroll : function(direction, distance, animate) {
3115 if (!this.isScrollable()) {
3119 l = el.scrollLeft, t = el.scrollTop,
3120 w = el.scrollWidth, h = el.scrollHeight,
3121 cw = el.clientWidth, ch = el.clientHeight,
3122 scrolled = false, v,
3124 l: Math.min(l + distance, w-cw),
3125 r: v = Math.max(l - distance, 0),
3126 t: Math.max(t - distance, 0),
3127 b: Math.min(t + distance, h-ch)
3132 direction = direction.substr(0, 1);
3133 if ((v = hash[direction]) > -1) {
3135 this.scrollTo(direction == 'l' || direction == 'r' ? 'left' : 'top', v, this.preanim(arguments, 2));
3140 * @class Ext.Element
3142 Ext.Element.addMethods(
3144 var VISIBILITY = "visibility",
3145 DISPLAY = "display",
3148 XMASKED = "x-masked",
3149 XMASKEDRELATIVE = "x-masked-relative",
3150 data = Ext.Element.data;
3154 * Checks whether the element is currently visible using both visibility and display properties.
3155 * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
3156 * @return {Boolean} True if the element is currently visible, else false
3158 isVisible : function(deep) {
3159 var vis = !this.isStyle(VISIBILITY, HIDDEN) && !this.isStyle(DISPLAY, NONE),
3160 p = this.dom.parentNode;
3162 if (deep !== true || !vis) {
3166 while (p && !(/^body/i.test(p.tagName))) {
3167 if (!Ext.fly(p, '_isVisible').isVisible()) {
3176 * Returns true if display is not "none"
3179 isDisplayed : function() {
3180 return !this.isStyle(DISPLAY, NONE);
3184 * Convenience method for setVisibilityMode(Element.DISPLAY)
3185 * @param {String} display (optional) What to set display to when visible
3186 * @return {Ext.Element} this
3188 enableDisplayMode : function(display) {
3189 this.setVisibilityMode(Ext.Element.DISPLAY);
3191 if (!Ext.isEmpty(display)) {
3192 data(this.dom, 'originalDisplay', display);
3199 * Puts a mask over this element to disable user interaction. Requires core.css.
3200 * This method can only be applied to elements which accept child nodes.
3201 * @param {String} msg (optional) A message to display in the mask
3202 * @param {String} msgCls (optional) A css class to apply to the msg element
3203 * @return {Element} The mask element
3205 mask : function(msg, msgCls) {
3209 EXTELMASKMSG = "ext-el-mask-msg",
3213 if (!(/^body/i.test(dom.tagName) && me.getStyle('position') == 'static')) {
3214 me.addClass(XMASKEDRELATIVE);
3216 if (el = data(dom, 'maskMsg')) {
3219 if (el = data(dom, 'mask')) {
3223 mask = dh.append(dom, {cls : "ext-el-mask"}, true);
3224 data(dom, 'mask', mask);
3226 me.addClass(XMASKED);
3227 mask.setDisplayed(true);
3229 if (typeof msg == 'string') {
3230 var mm = dh.append(dom, {cls : EXTELMASKMSG, cn:{tag:'div'}}, true);
3231 data(dom, 'maskMsg', mm);
3232 mm.dom.className = msgCls ? EXTELMASKMSG + " " + msgCls : EXTELMASKMSG;
3233 mm.dom.firstChild.innerHTML = msg;
3234 mm.setDisplayed(true);
3238 // ie will not expand full height automatically
3239 if (Ext.isIE && !(Ext.isIE7 && Ext.isStrict) && me.getStyle('height') == 'auto') {
3240 mask.setSize(undefined, me.getHeight());
3247 * Removes a previously applied mask.
3249 unmask : function() {
3252 mask = data(dom, 'mask'),
3253 maskMsg = data(dom, 'maskMsg');
3258 data(dom, 'maskMsg', undefined);
3262 data(dom, 'mask', undefined);
3263 me.removeClass([XMASKED, XMASKEDRELATIVE]);
3268 * Returns true if this element is masked
3271 isMasked : function() {
3272 var m = data(this.dom, 'mask');
3273 return m && m.isVisible();
3277 * Creates an iframe shim for this element to keep selects and other windowed objects from
3279 * @return {Ext.Element} The new shim element
3281 createShim : function() {
3282 var el = document.createElement('iframe'),
3285 el.frameBorder = '0';
3286 el.className = 'ext-shim';
3287 el.src = Ext.SSL_SECURE_URL;
3288 shim = Ext.get(this.dom.parentNode.insertBefore(el, this.dom));
3289 shim.autoBoxAdjust = false;
3295 * @class Ext.Element
3297 Ext.Element.addMethods({
3299 * Convenience method for constructing a KeyMap
3300 * @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:
3301 * <code>{key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}</code>
3302 * @param {Function} fn The function to call
3303 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the specified function is executed. Defaults to this Element.
3304 * @return {Ext.KeyMap} The KeyMap created
3306 addKeyListener : function(key, fn, scope){
3308 if(typeof key != 'object' || Ext.isArray(key)){
3324 return new Ext.KeyMap(this, config);
3328 * Creates a KeyMap for this element
3329 * @param {Object} config The KeyMap config. See {@link Ext.KeyMap} for more details
3330 * @return {Ext.KeyMap} The KeyMap created
3332 addKeyMap : function(config){
3333 return new Ext.KeyMap(this, config);
3337 //Import the newly-added Ext.Element functions into CompositeElementLite. We call this here because
3338 //Element.keys.js is the last extra Ext.Element include in the ext-all.js build
3339 Ext.CompositeElementLite.importElementMethods();/**
3340 * @class Ext.CompositeElementLite
3342 Ext.apply(Ext.CompositeElementLite.prototype, {
3343 addElements : function(els, root){
3347 if(typeof els == "string"){
3348 els = Ext.Element.selectorFunction(els, root);
3350 var yels = this.elements;
3351 Ext.each(els, function(e) {
3352 yels.push(Ext.get(e));
3358 * Returns the first Element
3359 * @return {Ext.Element}
3362 return this.item(0);
3366 * Returns the last Element
3367 * @return {Ext.Element}
3370 return this.item(this.getCount()-1);
3374 * Returns true if this composite contains the passed element
3375 * @param el {Mixed} The id of an element, or an Ext.Element, or an HtmlElement to find within the composite collection.
3378 contains : function(el){
3379 return this.indexOf(el) != -1;
3383 * Removes the specified element(s).
3384 * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
3385 * or an array of any of those.
3386 * @param {Boolean} removeDom (optional) True to also remove the element from the document
3387 * @return {CompositeElement} this
3389 removeElement : function(keys, removeDom){
3391 els = this.elements,
3393 Ext.each(keys, function(val){
3394 if ((el = (els[val] || els[val = me.indexOf(val)]))) {
3409 * @class Ext.CompositeElement
3410 * @extends Ext.CompositeElementLite
3411 * <p>This class encapsulates a <i>collection</i> of DOM elements, providing methods to filter
3412 * members, or to perform collective actions upon the whole set.</p>
3413 * <p>Although they are not listed, this class supports all of the methods of {@link Ext.Element} and
3414 * {@link Ext.Fx}. The methods from these classes will be performed on all the elements in this collection.</p>
3415 * <p>All methods return <i>this</i> and can be chained.</p>
3418 var els = Ext.select("#some-el div.some-class", true);
3419 // or select directly from an existing element
3420 var el = Ext.get('some-el');
3421 el.select('div.some-class', true);
3423 els.setWidth(100); // all elements become 100 width
3424 els.hide(true); // all elements fade out and hide
3426 els.setWidth(100).hide(true);
3429 Ext.CompositeElement = Ext.extend(Ext.CompositeElementLite, {
3431 constructor : function(els, root){
3433 this.add(els, root);
3437 getElement : function(el){
3438 // In this case just return it, since we already have a reference to it
3443 transformElement : function(el){
3448 * Adds elements to this composite.
3449 * @param {String/Array} els A string CSS selector, an array of elements or an element
3450 * @return {CompositeElement} this
3454 * Returns the Element object at the specified index
3455 * @param {Number} index
3456 * @return {Ext.Element}
3460 * Iterates each <code>element</code> in this <code>composite</code>
3461 * calling the supplied function using {@link Ext#each}.
3462 * @param {Function} fn The function to be called with each
3463 * <code>element</code>. If the supplied function returns <tt>false</tt>,
3464 * iteration stops. This function is called with the following arguments:
3465 * <div class="mdetail-params"><ul>
3466 * <li><code>element</code> : <i>Ext.Element</i><div class="sub-desc">The element at the current <code>index</code>
3467 * in the <code>composite</code></div></li>
3468 * <li><code>composite</code> : <i>Object</i> <div class="sub-desc">This composite.</div></li>
3469 * <li><code>index</code> : <i>Number</i> <div class="sub-desc">The current index within the <code>composite</code> </div></li>
3471 * @param {Object} scope (optional) The scope (<code><this</code> reference) in which the specified function is executed.
3472 * Defaults to the <code>element</code> at the current <code>index</code>
3473 * within the composite.
3474 * @return {CompositeElement} this
3479 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
3480 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
3481 * {@link Ext.CompositeElementLite CompositeElementLite} object.
3482 * @param {String/Array} selector The CSS selector or an array of elements
3483 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
3484 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
3485 * @return {CompositeElementLite/CompositeElement}
3486 * @member Ext.Element
3489 Ext.Element.select = function(selector, unique, root){
3491 if(typeof selector == "string"){
3492 els = Ext.Element.selectorFunction(selector, root);
3493 }else if(selector.length !== undefined){
3496 throw "Invalid selector";
3499 return (unique === true) ? new Ext.CompositeElement(els) : new Ext.CompositeElementLite(els);
3503 * Selects elements based on the passed CSS selector to enable {@link Ext.Element Element} methods
3504 * to be applied to many related elements in one statement through the returned {@link Ext.CompositeElement CompositeElement} or
3505 * {@link Ext.CompositeElementLite CompositeElementLite} object.
3506 * @param {String/Array} selector The CSS selector or an array of elements
3507 * @param {Boolean} unique (optional) true to create a unique Ext.Element for each element (defaults to a shared flyweight object)
3508 * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
3509 * @return {CompositeElementLite/CompositeElement}
3513 Ext.select = Ext.Element.select;/**
3514 * @class Ext.Updater
3515 * @extends Ext.util.Observable
3516 * Provides AJAX-style update capabilities for Element objects. Updater can be used to {@link #update}
3517 * an {@link Ext.Element} once, or you can use {@link #startAutoRefresh} to set up an auto-updating
3518 * {@link Ext.Element Element} on a specific interval.<br><br>
3521 * var el = Ext.get("foo"); // Get Ext.Element object
3522 * var mgr = el.getUpdater();
3524 url: "http://myserver.com/index.php",
3531 * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
3533 * // or directly (returns the same Updater instance)
3534 * var mgr = new Ext.Updater("myElementId");
3535 * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
3536 * mgr.on("update", myFcnNeedsToKnow);
3538 * // short handed call directly from the element object
3539 * Ext.get("foo").load({
3542 params: "param1=foo&param2=bar",
3543 text: "Loading Foo..."
3547 * Create new Updater directly.
3548 * @param {Mixed} el The element to update
3549 * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already
3550 * has an Updater and if it does it returns the same instance. This will skip that check (useful for extending this class).
3552 Ext.UpdateManager = Ext.Updater = Ext.extend(Ext.util.Observable,
3554 var BEFOREUPDATE = "beforeupdate",
3556 FAILURE = "failure";
3559 function processSuccess(response){
3561 me.transaction = null;
3562 if (response.argument.form && response.argument.reset) {
3563 try { // put in try/catch since some older FF releases had problems with this
3564 response.argument.form.reset();
3567 if (me.loadScripts) {
3568 me.renderer.render(me.el, response, me,
3569 updateComplete.createDelegate(me, [response]));
3571 me.renderer.render(me.el, response, me);
3572 updateComplete.call(me, response);
3577 function updateComplete(response, type, success){
3578 this.fireEvent(type || UPDATE, this.el, response);
3579 if(Ext.isFunction(response.argument.callback)){
3580 response.argument.callback.call(response.argument.scope, this.el, Ext.isEmpty(success) ? true : false, response, response.argument.options);
3585 function processFailure(response){
3586 updateComplete.call(this, response, FAILURE, !!(this.transaction = null));
3590 constructor: function(el, forceNew){
3593 if(!forceNew && el.updateManager){
3594 return el.updateManager;
3597 * The Element object
3602 * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
3605 me.defaultUrl = null;
3609 * @event beforeupdate
3610 * Fired before an update is made, return false from your handler and the update is cancelled.
3611 * @param {Ext.Element} el
3612 * @param {String/Object/Function} url
3613 * @param {String/Object} params
3618 * Fired after successful update is made.
3619 * @param {Ext.Element} el
3620 * @param {Object} oResponseObject The response Object
3625 * Fired on update failure.
3626 * @param {Ext.Element} el
3627 * @param {Object} oResponseObject The response Object
3632 Ext.apply(me, Ext.Updater.defaults);
3634 * Blank page URL to use with SSL file uploads (defaults to {@link Ext.Updater.defaults#sslBlankUrl}).
3635 * @property sslBlankUrl
3639 * Whether to append unique parameter on get request to disable caching (defaults to {@link Ext.Updater.defaults#disableCaching}).
3640 * @property disableCaching
3644 * Text for loading indicator (defaults to {@link Ext.Updater.defaults#indicatorText}).
3645 * @property indicatorText
3649 * Whether to show indicatorText when loading (defaults to {@link Ext.Updater.defaults#showLoadIndicator}).
3650 * @property showLoadIndicator
3654 * Timeout for requests or form posts in seconds (defaults to {@link Ext.Updater.defaults#timeout}).
3659 * True to process scripts in the output (defaults to {@link Ext.Updater.defaults#loadScripts}).
3660 * @property loadScripts
3665 * Transaction object of the current executing transaction, or null if there is no active transaction.
3667 me.transaction = null;
3669 * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
3672 me.refreshDelegate = me.refresh.createDelegate(me);
3674 * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
3677 me.updateDelegate = me.update.createDelegate(me);
3679 * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
3682 me.formUpdateDelegate = (me.formUpdate || function(){}).createDelegate(me);
3685 * The renderer for this Updater (defaults to {@link Ext.Updater.BasicRenderer}).
3687 me.renderer = me.renderer || me.getDefaultRenderer();
3689 Ext.Updater.superclass.constructor.call(me);
3693 * Sets the content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
3694 * @param {Object} renderer The object implementing the render() method
3696 setRenderer : function(renderer){
3697 this.renderer = renderer;
3701 * Returns the current content renderer for this Updater. See {@link Ext.Updater.BasicRenderer#render} for more details.
3704 getRenderer : function(){
3705 return this.renderer;
3709 * This is an overrideable method which returns a reference to a default
3710 * renderer class if none is specified when creating the Ext.Updater.
3711 * Defaults to {@link Ext.Updater.BasicRenderer}
3713 getDefaultRenderer: function() {
3714 return new Ext.Updater.BasicRenderer();
3718 * Sets the default URL used for updates.
3719 * @param {String/Function} defaultUrl The url or a function to call to get the url
3721 setDefaultUrl : function(defaultUrl){
3722 this.defaultUrl = defaultUrl;
3726 * Get the Element this Updater is bound to
3727 * @return {Ext.Element} The element
3734 * Performs an <b>asynchronous</b> request, updating this element with the response.
3735 * If params are specified it uses POST, otherwise it uses GET.<br><br>
3736 * <b>Note:</b> Due to the asynchronous nature of remote server requests, the Element
3737 * will not have been fully updated when the function returns. To post-process the returned
3738 * data, use the callback option, or an <b><code>update</code></b> event handler.
3739 * @param {Object} options A config object containing any of the following options:<ul>
3740 * <li>url : <b>String/Function</b><p class="sub-desc">The URL to request or a function which
3741 * <i>returns</i> the URL (defaults to the value of {@link Ext.Ajax#url} if not specified).</p></li>
3742 * <li>method : <b>String</b><p class="sub-desc">The HTTP method to
3743 * use. Defaults to POST if the <code>params</code> argument is present, otherwise GET.</p></li>
3744 * <li>params : <b>String/Object/Function</b><p class="sub-desc">The
3745 * parameters to pass to the server (defaults to none). These may be specified as a url-encoded
3746 * string, or as an object containing properties which represent parameters,
3747 * or as a function, which returns such an object.</p></li>
3748 * <li>scripts : <b>Boolean</b><p class="sub-desc">If <code>true</code>
3749 * any <script> tags embedded in the response text will be extracted
3750 * and executed (defaults to {@link Ext.Updater.defaults#loadScripts}). If this option is specified,
3751 * the callback will be called <i>after</i> the execution of the scripts.</p></li>
3752 * <li>callback : <b>Function</b><p class="sub-desc">A function to
3753 * be called when the response from the server arrives. The following
3754 * parameters are passed:<ul>
3755 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
3756 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
3757 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li>
3758 * <li><b>options</b> : Object<p class="sub-desc">The config object passed to the update call.</p></li></ul>
3760 * <li>scope : <b>Object</b><p class="sub-desc">The scope in which
3761 * to execute the callback (The callback's <code>this</code> reference.) If the
3762 * <code>params</code> argument is a function, this scope is used for that function also.</p></li>
3763 * <li>discardUrl : <b>Boolean</b><p class="sub-desc">By default, the URL of this request becomes
3764 * the default URL for this Updater object, and will be subsequently used in {@link #refresh}
3765 * calls. To bypass this behavior, pass <code>discardUrl:true</code> (defaults to false).</p></li>
3766 * <li>timeout : <b>Number</b><p class="sub-desc">The number of seconds to wait for a response before
3767 * timing out (defaults to {@link Ext.Updater.defaults#timeout}).</p></li>
3768 * <li>text : <b>String</b><p class="sub-desc">The text to use as the innerHTML of the
3769 * {@link Ext.Updater.defaults#indicatorText} div (defaults to 'Loading...'). To replace the entire div, not
3770 * just the text, override {@link Ext.Updater.defaults#indicatorText} directly.</p></li>
3771 * <li>nocache : <b>Boolean</b><p class="sub-desc">Only needed for GET
3772 * requests, this option causes an extra, auto-generated parameter to be appended to the request
3773 * to defeat caching (defaults to {@link Ext.Updater.defaults#disableCaching}).</p></li></ul>
3778 url: "your-url.php",
3779 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
3780 callback: yourFunction,
3781 scope: yourObject, //(optional scope)
3786 scripts: false // Save time by avoiding RegExp execution.
3790 update : function(url, params, callback, discardUrl){
3795 if(me.fireEvent(BEFOREUPDATE, me.el, url, params) !== false){
3796 if(Ext.isObject(url)){ // must be config object
3799 params = params || cfg.params;
3800 callback = callback || cfg.callback;
3801 discardUrl = discardUrl || cfg.discardUrl;
3802 callerScope = cfg.scope;
3803 if(!Ext.isEmpty(cfg.nocache)){me.disableCaching = cfg.nocache;};
3804 if(!Ext.isEmpty(cfg.text)){me.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
3805 if(!Ext.isEmpty(cfg.scripts)){me.loadScripts = cfg.scripts;};
3806 if(!Ext.isEmpty(cfg.timeout)){me.timeout = cfg.timeout;};
3811 me.defaultUrl = url;
3813 if(Ext.isFunction(url)){
3817 var o = Ext.apply({}, {
3819 params: (Ext.isFunction(params) && callerScope) ? params.createDelegate(callerScope) : params,
3820 success: processSuccess,
3821 failure: processFailure,
3823 callback: undefined,
3824 timeout: (me.timeout*1000),
3825 disableCaching: me.disableCaching,
3830 "callback": callback,
3831 "scope": callerScope || window,
3836 me.transaction = Ext.Ajax.request(o);
3841 * <p>Performs an asynchronous form post, updating this element with the response. If the form has the attribute
3842 * enctype="<a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form-data</a>", it assumes it's a file upload.
3843 * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.</p>
3844 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
3845 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
3846 * DOM <code><form></code> element temporarily modified to have its
3847 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
3848 * to a dynamically generated, hidden <code><iframe></code> which is inserted into the document
3849 * but removed after the return data has been gathered.</p>
3850 * <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>
3851 * and some server technologies (notably JEE) may require some custom processing in order to
3852 * retrieve parameter names and parameter values from the packet content.</p>
3853 * @param {String/HTMLElement} form The form Id or form element
3854 * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
3855 * @param {Boolean} reset (optional) Whether to try to reset the form after the update
3856 * @param {Function} callback (optional) Callback when transaction is complete. The following
3857 * parameters are passed:<ul>
3858 * <li><b>el</b> : Ext.Element<p class="sub-desc">The Element being updated.</p></li>
3859 * <li><b>success</b> : Boolean<p class="sub-desc">True for success, false for failure.</p></li>
3860 * <li><b>response</b> : XMLHttpRequest<p class="sub-desc">The XMLHttpRequest which processed the update.</p></li></ul>
3862 formUpdate : function(form, url, reset, callback){
3864 if(me.fireEvent(BEFOREUPDATE, me.el, form, url) !== false){
3865 if(Ext.isFunction(url)){
3868 form = Ext.getDom(form);
3869 me.transaction = Ext.Ajax.request({
3872 success: processSuccess,
3873 failure: processFailure,
3875 timeout: (me.timeout*1000),
3879 "callback": callback,
3883 me.showLoading.defer(1, me);
3888 * Set this element to auto refresh. Can be canceled by calling {@link #stopAutoRefresh}.
3889 * @param {Number} interval How often to update (in seconds).
3890 * @param {String/Object/Function} url (optional) The url for this request, a config object in the same format
3891 * supported by {@link #load}, or a function to call to get the url (defaults to the last used url). Note that while
3892 * the url used in a load call can be reused by this method, other load config options will not be reused and must be
3893 * sepcified as part of a config object passed as this paramter if needed.
3894 * @param {String/Object} params (optional) The parameters to pass as either a url encoded string
3895 * "¶m1=1¶m2=2" or as an object {param1: 1, param2: 2}
3896 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
3897 * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
3899 startAutoRefresh : function(interval, url, params, callback, refreshNow){
3902 me.update(url || me.defaultUrl, params, callback, true);
3904 if(me.autoRefreshProcId){
3905 clearInterval(me.autoRefreshProcId);
3907 me.autoRefreshProcId = setInterval(me.update.createDelegate(me, [url || me.defaultUrl, params, callback, true]), interval * 1000);
3911 * Stop auto refresh on this element.
3913 stopAutoRefresh : function(){
3914 if(this.autoRefreshProcId){
3915 clearInterval(this.autoRefreshProcId);
3916 delete this.autoRefreshProcId;
3921 * Returns true if the Updater is currently set to auto refresh its content (see {@link #startAutoRefresh}), otherwise false.
3923 isAutoRefreshing : function(){
3924 return !!this.autoRefreshProcId;
3928 * Display the element's "loading" state. By default, the element is updated with {@link #indicatorText}. This
3929 * method may be overridden to perform a custom action while this Updater is actively updating its contents.
3931 showLoading : function(){
3932 if(this.showLoadIndicator){
3933 this.el.dom.innerHTML = this.indicatorText;
3938 * Aborts the currently executing transaction, if any.
3941 if(this.transaction){
3942 Ext.Ajax.abort(this.transaction);
3947 * Returns true if an update is in progress, otherwise false.
3950 isUpdating : function(){
3951 return this.transaction ? Ext.Ajax.isLoading(this.transaction) : false;
3955 * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
3956 * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
3958 refresh : function(callback){
3959 if(this.defaultUrl){
3960 this.update(this.defaultUrl, null, callback, true);
3967 * @class Ext.Updater.defaults
3968 * The defaults collection enables customizing the default properties of Updater
3970 Ext.Updater.defaults = {
3972 * Timeout for requests or form posts in seconds (defaults to 30 seconds).
3977 * True to append a unique parameter to GET requests to disable caching (defaults to false).
3980 disableCaching : false,
3982 * Whether or not to show {@link #indicatorText} during loading (defaults to true).
3985 showLoadIndicator : true,
3987 * Text for loading indicator (defaults to '<div class="loading-indicator">Loading...</div>').
3990 indicatorText : '<div class="loading-indicator">Loading...</div>',
3992 * True to process scripts by default (defaults to false).
3995 loadScripts : false,
3997 * Blank page URL to use with SSL file uploads (defaults to {@link Ext#SSL_SECURE_URL} if set, or "javascript:false").
4000 sslBlankUrl : Ext.SSL_SECURE_URL
4005 * Static convenience method. <b>This method is deprecated in favor of el.load({url:'foo.php', ...})</b>.
4007 * <pre><code>Ext.Updater.updateElement("my-div", "stuff.php");</code></pre>
4008 * @param {Mixed} el The element to update
4009 * @param {String} url The url
4010 * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
4011 * @param {Object} options (optional) A config object with any of the Updater properties you want to set - for
4012 * example: {disableCaching:true, indicatorText: "Loading data..."}
4015 * @member Ext.Updater
4017 Ext.Updater.updateElement = function(el, url, params, options){
4018 var um = Ext.get(el).getUpdater();
4019 Ext.apply(um, options);
4020 um.update(url, params, options ? options.callback : null);
4024 * @class Ext.Updater.BasicRenderer
4025 * <p>This class is a base class implementing a simple render method which updates an element using results from an Ajax request.</p>
4026 * <p>The BasicRenderer updates the element's innerHTML with the responseText. To perform a custom render (i.e. XML or JSON processing),
4027 * create an object with a conforming {@link #render} method and pass it to setRenderer on the Updater.</p>
4029 Ext.Updater.BasicRenderer = function(){};
4031 Ext.Updater.BasicRenderer.prototype = {
4033 * This method is called when an Ajax response is received, and an Element needs updating.
4034 * @param {Ext.Element} el The element being rendered
4035 * @param {Object} xhr The XMLHttpRequest object
4036 * @param {Updater} updateManager The calling update manager
4037 * @param {Function} callback A callback that will need to be called if loadScripts is true on the Updater
4039 render : function(el, response, updateManager, callback){
4040 el.update(response.responseText, updateManager.loadScripts, callback);
4045 * The date parsing and formatting syntax contains a subset of
4046 * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
4047 * supported will provide results equivalent to their PHP versions.
4049 * The following is a list of all currently supported formats:
4051 Format Description Example returned values
4052 ------ ----------------------------------------------------------------------- -----------------------
4053 d Day of the month, 2 digits with leading zeros 01 to 31
4054 D A short textual representation of the day of the week Mon to Sun
4055 j Day of the month without leading zeros 1 to 31
4056 l A full textual representation of the day of the week Sunday to Saturday
4057 N ISO-8601 numeric representation of the day of the week 1 (for Monday) through 7 (for Sunday)
4058 S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
4059 w Numeric representation of the day of the week 0 (for Sunday) to 6 (for Saturday)
4060 z The day of the year (starting from 0) 0 to 364 (365 in leap years)
4061 W ISO-8601 week number of year, weeks starting on Monday 01 to 53
4062 F A full textual representation of a month, such as January or March January to December
4063 m Numeric representation of a month, with leading zeros 01 to 12
4064 M A short textual representation of a month Jan to Dec
4065 n Numeric representation of a month, without leading zeros 1 to 12
4066 t Number of days in the given month 28 to 31
4067 L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
4068 o ISO-8601 year number (identical to (Y), but if the ISO week number (W) Examples: 1998 or 2004
4069 belongs to the previous or next year, that year is used instead)
4070 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
4071 y A two digit representation of a year Examples: 99 or 03
4072 a Lowercase Ante meridiem and Post meridiem am or pm
4073 A Uppercase Ante meridiem and Post meridiem AM or PM
4074 g 12-hour format of an hour without leading zeros 1 to 12
4075 G 24-hour format of an hour without leading zeros 0 to 23
4076 h 12-hour format of an hour with leading zeros 01 to 12
4077 H 24-hour format of an hour with leading zeros 00 to 23
4078 i Minutes, with leading zeros 00 to 59
4079 s Seconds, with leading zeros 00 to 59
4080 u Decimal fraction of a second Examples:
4081 (minimum 1 digit, arbitrary number of digits allowed) 001 (i.e. 0.001s) or
4082 100 (i.e. 0.100s) or
4083 999 (i.e. 0.999s) or
4084 999876543210 (i.e. 0.999876543210s)
4085 O Difference to Greenwich time (GMT) in hours and minutes Example: +1030
4086 P Difference to Greenwich time (GMT) with colon between hours and minutes Example: -08:00
4087 T Timezone abbreviation of the machine running the code Examples: EST, MDT, PDT ...
4088 Z Timezone offset in seconds (negative if west of UTC, positive if east) -43200 to 50400
4091 1) If unspecified, the month / day defaults to the current month / day, 1991 or
4092 the time defaults to midnight, while the timezone defaults to the 1992-10 or
4093 browser's timezone. If a time is specified, it must include both hours 1993-09-20 or
4094 and minutes. The "T" delimiter, seconds, milliseconds and timezone 1994-08-19T16:20+01:00 or
4095 are optional. 1995-07-18T17:21:28-02:00 or
4096 2) The decimal fraction of a second, if specified, must contain at 1996-06-17T18:22:29.98765+03:00 or
4097 least 1 digit (there is no limit to the maximum number 1997-05-16T19:23:30,12345-0400 or
4098 of digits allowed), and may be delimited by either a '.' or a ',' 1998-04-15T20:24:31.2468Z or
4099 Refer to the examples on the right for the various levels of 1999-03-14T20:24:32Z or
4100 date-time granularity which are supported, or see 2000-02-13T21:25:33
4101 http://www.w3.org/TR/NOTE-datetime for more info. 2001-01-12 22:26:34
4102 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) 1193432466 or -2138434463
4103 M$ Microsoft AJAX serialized dates \/Date(1238606590509)\/ (i.e. UTC milliseconds since epoch) or
4104 \/Date(1238606590509+0800)\/
4107 * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
4110 // 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
4112 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
4113 document.write(dt.format('Y-m-d')); // 2007-01-10
4114 document.write(dt.format('F j, Y, g:i a')); // January 10, 2007, 3:05 pm
4115 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
4118 * Here are some standard date/time patterns that you might find helpful. They
4119 * are not part of the source of Date.js, but to use them you can simply copy this
4120 * block of code into any script that is included after Date.js and they will also become
4121 * globally available on the Date object. Feel free to add or remove patterns as needed in your code.
4124 ISO8601Long:"Y-m-d H:i:s",
4125 ISO8601Short:"Y-m-d",
4127 LongDate: "l, F d, Y",
4128 FullDateTime: "l, F d, Y g:i:s A",
4131 LongTime: "g:i:s A",
4132 SortableDateTime: "Y-m-d\\TH:i:s",
4133 UniversalSortableDateTime: "Y-m-d H:i:sO",
4140 var dt = new Date();
4141 document.write(dt.format(Date.patterns.ShortDate));
4143 * <p>Developer-written, custom formats may be used by supplying both a formatting and a parsing function
4144 * which perform to specialized requirements. The functions are stored in {@link #parseFunctions} and {@link #formatFunctions}.</p>
4148 * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
4149 * (see http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/)
4150 * They generate precompiled functions from format patterns instead of parsing and
4151 * processing each pattern every time a date is formatted. These functions are available
4152 * on every Date object.
4158 * Global flag which determines if strict date parsing should be used.
4159 * Strict date parsing will not roll-over invalid dates, which is the
4160 * default behaviour of javascript Date objects.
4161 * (see {@link #parseDate} for more information)
4162 * Defaults to <tt>false</tt>.
4166 Date.useStrict = false;
4169 // create private copy of Ext's String.format() method
4170 // - to remove unnecessary dependency
4171 // - to resolve namespace conflict with M$-Ajax's implementation
4172 function xf(format) {
4173 var args = Array.prototype.slice.call(arguments, 1);
4174 return format.replace(/\{(\d+)\}/g, function(m, i) {
4181 Date.formatCodeToRegex = function(character, currentGroup) {
4182 // Note: currentGroup - position in regex result array (see notes for Date.parseCodes below)
4183 var p = Date.parseCodes[character];
4186 p = typeof p == 'function'? p() : p;
4187 Date.parseCodes[character] = p; // reassign function result to prevent repeated execution
4190 return p ? Ext.applyIf({
4191 c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
4195 s:Ext.escapeRe(character) // treat unrecognised characters as literals
4199 // private shorthand for Date.formatCodeToRegex since we'll be using it fairly often
4200 var $f = Date.formatCodeToRegex;
4204 * <p>An object hash in which each property is a date parsing function. The property name is the
4205 * format string which that function parses.</p>
4206 * <p>This object is automatically populated with date parsing functions as
4207 * date formats are requested for Ext standard formatting strings.</p>
4208 * <p>Custom parsing functions may be inserted into this object, keyed by a name which from then on
4209 * may be used as a format string to {@link #parseDate}.<p>
4210 * <p>Example:</p><pre><code>
4211 Date.parseFunctions['x-date-format'] = myDateParser;
4213 * <p>A parsing function should return a Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
4214 * <li><code>date</code> : String<div class="sub-desc">The date string to parse.</div></li>
4215 * <li><code>strict</code> : Boolean<div class="sub-desc">True to validate date strings while parsing
4216 * (i.e. prevent javascript Date "rollover") (The default must be false).
4217 * Invalid date strings should return null when parsed.</div></li>
4219 * <p>To enable Dates to also be <i>formatted</i> according to that format, a corresponding
4220 * formatting function must be placed into the {@link #formatFunctions} property.
4221 * @property parseFunctions
4226 "M$": function(input, strict) {
4227 // note: the timezone offset is ignored since the M$ Ajax server sends
4228 // a UTC milliseconds-since-Unix-epoch value (negative values are allowed)
4229 var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
4230 var r = (input || '').match(re);
4231 return r? new Date(((r[1] || '') + r[2]) * 1) : null;
4237 * <p>An object hash in which each property is a date formatting function. The property name is the
4238 * format string which corresponds to the produced formatted date string.</p>
4239 * <p>This object is automatically populated with date formatting functions as
4240 * date formats are requested for Ext standard formatting strings.</p>
4241 * <p>Custom formatting functions may be inserted into this object, keyed by a name which from then on
4242 * may be used as a format string to {@link #format}. Example:</p><pre><code>
4243 Date.formatFunctions['x-date-format'] = myDateFormatter;
4245 * <p>A formatting function should return a string representation of the passed Date object, and is passed the following parameters:<div class="mdetail-params"><ul>
4246 * <li><code>date</code> : Date<div class="sub-desc">The Date to format.</div></li>
4248 * <p>To enable date strings to also be <i>parsed</i> according to that format, a corresponding
4249 * parsing function must be placed into the {@link #parseFunctions} property.
4250 * @property formatFunctions
4256 // UTC milliseconds since Unix epoch (M$-AJAX serialized date format (MRSF))
4257 return '\\/Date(' + this.getTime() + ')\\/';
4264 * Date interval constant
4271 * Date interval constant
4278 * Date interval constant
4284 /** Date interval constant
4291 * Date interval constant
4298 * Date interval constant
4305 * Date interval constant
4312 * <p>An object hash containing default date values used during date parsing.</p>
4313 * <p>The following properties are available:<div class="mdetail-params"><ul>
4314 * <li><code>y</code> : Number<div class="sub-desc">The default year value. (defaults to undefined)</div></li>
4315 * <li><code>m</code> : Number<div class="sub-desc">The default 1-based month value. (defaults to undefined)</div></li>
4316 * <li><code>d</code> : Number<div class="sub-desc">The default day value. (defaults to undefined)</div></li>
4317 * <li><code>h</code> : Number<div class="sub-desc">The default hour value. (defaults to undefined)</div></li>
4318 * <li><code>i</code> : Number<div class="sub-desc">The default minute value. (defaults to undefined)</div></li>
4319 * <li><code>s</code> : Number<div class="sub-desc">The default second value. (defaults to undefined)</div></li>
4320 * <li><code>ms</code> : Number<div class="sub-desc">The default millisecond value. (defaults to undefined)</div></li>
4322 * <p>Override these properties to customize the default date values used by the {@link #parseDate} method.</p>
4323 * <p><b>Note: In countries which experience Daylight Saving Time (i.e. DST), the <tt>h</tt>, <tt>i</tt>, <tt>s</tt>
4324 * and <tt>ms</tt> properties may coincide with the exact time in which DST takes effect.
4325 * It is the responsiblity of the developer to account for this.</b></p>
4328 // set default day value to the first day of the month
4329 Date.defaults.d = 1;
4331 // parse a February date string containing only year and month values.
4332 // setting the default day value to 1 prevents weird date rollover issues
4333 // when attempting to parse the following date string on, for example, March 31st 2009.
4334 Date.parseDate('2009-02', 'Y-m'); // returns a Date object representing February 1st 2009
4336 * @property defaults
4343 * An array of textual day names.
4344 * Override these values for international dates.
4367 * An array of textual month names.
4368 * Override these values for international dates.
4396 * An object hash of zero-based javascript month numbers (with short month names as keys. note: keys are case-sensitive).
4397 * Override these values for international dates.
4400 Date.monthNumbers = {
4401 'ShortJanNameInYourLang':0,
4402 'ShortFebNameInYourLang':1,
4425 * Get the short month name for the given month number.
4426 * Override this function for international dates.
4427 * @param {Number} month A zero-based javascript month number.
4428 * @return {String} The short month name.
4431 getShortMonthName : function(month) {
4432 return Date.monthNames[month].substring(0, 3);
4436 * Get the short day name for the given day number.
4437 * Override this function for international dates.
4438 * @param {Number} day A zero-based javascript day number.
4439 * @return {String} The short day name.
4442 getShortDayName : function(day) {
4443 return Date.dayNames[day].substring(0, 3);
4447 * Get the zero-based javascript month number for the given short/full month name.
4448 * Override this function for international dates.
4449 * @param {String} name The short/full month name.
4450 * @return {Number} The zero-based javascript month number.
4453 getMonthNumber : function(name) {
4454 // handle camel casing for english month names (since the keys for the Date.monthNumbers hash are case sensitive)
4455 return Date.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
4459 * The base format-code to formatting-function hashmap used by the {@link #format} method.
4460 * Formatting functions are strings (or functions which return strings) which
4461 * will return the appropriate value when evaluated in the context of the Date object
4462 * from which the {@link #format} method is called.
4463 * Add to / override these mappings for custom date formatting.
4464 * Note: Date.format() treats characters as literals if an appropriate mapping cannot be found.
4467 Date.formatCodes.x = "String.leftPad(this.getDate(), 2, '0')";
4468 (new Date()).format("X"); // returns the current day of the month
4474 d: "String.leftPad(this.getDate(), 2, '0')",
4475 D: "Date.getShortDayName(this.getDay())", // get localised short day name
4476 j: "this.getDate()",
4477 l: "Date.dayNames[this.getDay()]",
4478 N: "(this.getDay() ? this.getDay() : 7)",
4479 S: "this.getSuffix()",
4481 z: "this.getDayOfYear()",
4482 W: "String.leftPad(this.getWeekOfYear(), 2, '0')",
4483 F: "Date.monthNames[this.getMonth()]",
4484 m: "String.leftPad(this.getMonth() + 1, 2, '0')",
4485 M: "Date.getShortMonthName(this.getMonth())", // get localised short month name
4486 n: "(this.getMonth() + 1)",
4487 t: "this.getDaysInMonth()",
4488 L: "(this.isLeapYear() ? 1 : 0)",
4489 o: "(this.getFullYear() + (this.getWeekOfYear() == 1 && this.getMonth() > 0 ? +1 : (this.getWeekOfYear() >= 52 && this.getMonth() < 11 ? -1 : 0)))",
4490 Y: "String.leftPad(this.getFullYear(), 4, '0')",
4491 y: "('' + this.getFullYear()).substring(2, 4)",
4492 a: "(this.getHours() < 12 ? 'am' : 'pm')",
4493 A: "(this.getHours() < 12 ? 'AM' : 'PM')",
4494 g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
4495 G: "this.getHours()",
4496 h: "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
4497 H: "String.leftPad(this.getHours(), 2, '0')",
4498 i: "String.leftPad(this.getMinutes(), 2, '0')",
4499 s: "String.leftPad(this.getSeconds(), 2, '0')",
4500 u: "String.leftPad(this.getMilliseconds(), 3, '0')",
4501 O: "this.getGMTOffset()",
4502 P: "this.getGMTOffset(true)",
4503 T: "this.getTimezone()",
4504 Z: "(this.getTimezoneOffset() * -60)",
4506 c: function() { // ISO-8601 -- GMT format
4507 for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
4508 var e = c.charAt(i);
4509 code.push(e == "T" ? "'T'" : Date.getFormatCode(e)); // treat T as a character literal
4511 return code.join(" + ");
4514 c: function() { // ISO-8601 -- UTC format
4516 "this.getUTCFullYear()", "'-'",
4517 "String.leftPad(this.getUTCMonth() + 1, 2, '0')", "'-'",
4518 "String.leftPad(this.getUTCDate(), 2, '0')",
4520 "String.leftPad(this.getUTCHours(), 2, '0')", "':'",
4521 "String.leftPad(this.getUTCMinutes(), 2, '0')", "':'",
4522 "String.leftPad(this.getUTCSeconds(), 2, '0')",
4528 U: "Math.round(this.getTime() / 1000)"
4532 * Checks if the passed Date parameters will cause a javascript Date "rollover".
4533 * @param {Number} year 4-digit year
4534 * @param {Number} month 1-based month-of-year
4535 * @param {Number} day Day of month
4536 * @param {Number} hour (optional) Hour
4537 * @param {Number} minute (optional) Minute
4538 * @param {Number} second (optional) Second
4539 * @param {Number} millisecond (optional) Millisecond
4540 * @return {Boolean} true if the passed parameters do not cause a Date "rollover", false otherwise.
4543 isValid : function(y, m, d, h, i, s, ms) {
4550 // Special handling for year < 100
4551 var dt = new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0);
4553 return y == dt.getFullYear() &&
4554 m == dt.getMonth() + 1 &&
4555 d == dt.getDate() &&
4556 h == dt.getHours() &&
4557 i == dt.getMinutes() &&
4558 s == dt.getSeconds() &&
4559 ms == dt.getMilliseconds();
4563 * Parses the passed string using the specified date format.
4564 * Note that this function expects normal calendar dates, meaning that months are 1-based (i.e. 1 = January).
4565 * The {@link #defaults} hash will be used for any date value (i.e. year, month, day, hour, minute, second or millisecond)
4566 * which cannot be found in the passed string. If a corresponding default date value has not been specified in the {@link #defaults} hash,
4567 * the current date's year, month, day or DST-adjusted zero-hour time value will be used instead.
4568 * Keep in mind that the input date string must precisely match the specified format string
4569 * in order for the parse operation to be successful (failed parse operations return a null value).
4570 * <p>Example:</p><pre><code>
4571 //dt = Fri May 25 2007 (current date)
4572 var dt = new Date();
4574 //dt = Thu May 25 2006 (today's month/day in 2006)
4575 dt = Date.parseDate("2006", "Y");
4577 //dt = Sun Jan 15 2006 (all date parts specified)
4578 dt = Date.parseDate("2006-01-15", "Y-m-d");
4580 //dt = Sun Jan 15 2006 15:20:01
4581 dt = Date.parseDate("2006-01-15 3:20:01 PM", "Y-m-d g:i:s A");
4583 // attempt to parse Sun Feb 29 2006 03:20:01 in strict mode
4584 dt = Date.parseDate("2006-02-29 03:20:01", "Y-m-d H:i:s", true); // returns null
4586 * @param {String} input The raw date string.
4587 * @param {String} format The expected date string format.
4588 * @param {Boolean} strict (optional) True to validate date strings while parsing (i.e. prevents javascript Date "rollover")
4589 (defaults to false). Invalid date strings will return null when parsed.
4590 * @return {Date} The parsed Date.
4593 parseDate : function(input, format, strict) {
4594 var p = Date.parseFunctions;
4595 if (p[format] == null) {
4596 Date.createParser(format);
4598 return p[format](input, Ext.isDefined(strict) ? strict : Date.useStrict);
4602 getFormatCode : function(character) {
4603 var f = Date.formatCodes[character];
4606 f = typeof f == 'function'? f() : f;
4607 Date.formatCodes[character] = f; // reassign function result to prevent repeated execution
4610 // note: unknown characters are treated as literals
4611 return f || ("'" + String.escape(character) + "'");
4615 createFormat : function(format) {
4620 for (var i = 0; i < format.length; ++i) {
4621 ch = format.charAt(i);
4622 if (!special && ch == "\\") {
4624 } else if (special) {
4626 code.push("'" + String.escape(ch) + "'");
4628 code.push(Date.getFormatCode(ch));
4631 Date.formatFunctions[format] = new Function("return " + code.join('+'));
4635 createParser : function() {
4637 "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
4638 "def = Date.defaults,",
4639 "results = String(input).match(Date.parseRegexes[{0}]);", // either null, or an array of matched strings
4644 "if(u != null){", // i.e. unix time is defined
4645 "v = new Date(u * 1000);", // give top priority to UNIX time
4647 // create Date object representing midnight of the current day;
4648 // this will provide us with our date defaults
4649 // (note: clearTime() handles Daylight Saving Time automatically)
4650 "dt = (new Date()).clearTime();",
4652 // date calculations (note: these calculations create a dependency on Ext.num())
4653 "y = Ext.num(y, Ext.num(def.y, dt.getFullYear()));",
4654 "m = Ext.num(m, Ext.num(def.m - 1, dt.getMonth()));",
4655 "d = Ext.num(d, Ext.num(def.d, dt.getDate()));",
4657 // time calculations (note: these calculations create a dependency on Ext.num())
4658 "h = Ext.num(h, Ext.num(def.h, dt.getHours()));",
4659 "i = Ext.num(i, Ext.num(def.i, dt.getMinutes()));",
4660 "s = Ext.num(s, Ext.num(def.s, dt.getSeconds()));",
4661 "ms = Ext.num(ms, Ext.num(def.ms, dt.getMilliseconds()));",
4663 "if(z >= 0 && y >= 0){",
4664 // both the year and zero-based day of year are defined and >= 0.
4665 // these 2 values alone provide sufficient info to create a full date object
4667 // create Date object representing January 1st for the given year
4668 // handle years < 100 appropriately
4669 "v = new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0);",
4671 // then add day of year, checking for Date "rollover" if necessary
4672 "v = !strict? v : (strict === true && (z <= 364 || (v.isLeapYear() && z <= 365))? v.add(Date.DAY, z) : null);",
4673 "}else if(strict === true && !Date.isValid(y, m + 1, d, h, i, s, ms)){", // check for Date "rollover"
4674 "v = null;", // invalid date, so return null
4676 // plain old Date object
4677 // handle years < 100 properly
4678 "v = new Date(y < 100 ? 100 : y, m, d, h, i, s, ms).add(Date.YEAR, y < 100 ? y - 100 : 0);",
4684 // favour UTC offset over GMT offset
4686 // reset to UTC, then add offset
4687 "v = v.add(Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
4689 // reset to GMT, then add offset
4690 "v = v.add(Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
4697 return function(format) {
4698 var regexNum = Date.parseRegexes.length,
4708 for (; i < format.length; ++i) {
4709 ch = format.charAt(i);
4710 if (!special && ch == "\\") {
4712 } else if (special) {
4714 regex.push(String.escape(ch));
4716 obj = $f(ch, currentGroup);
4717 currentGroup += obj.g;
4719 if (obj.g && obj.c) {
4733 Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
4734 Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
4742 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
4743 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
4744 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
4748 c:"d = parseInt(results[{0}], 10);\n",
4749 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
4753 c:"d = parseInt(results[{0}], 10);\n",
4754 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
4757 for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
4761 s:"(?:" + a.join("|") +")"
4768 s:"(?:" + Date.dayNames.join("|") + ")"
4774 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
4784 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
4788 c:"z = parseInt(results[{0}], 10);\n",
4789 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
4794 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
4799 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
4800 s:"(" + Date.monthNames.join("|") + ")"
4804 for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
4805 return Ext.applyIf({
4806 s:"(" + a.join("|") + ")"
4811 c:"m = parseInt(results[{0}], 10) - 1;\n",
4812 s:"(\\d{2})" // month number with leading zeros (01 - 12)
4816 c:"m = parseInt(results[{0}], 10) - 1;\n",
4817 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
4822 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
4834 c:"y = parseInt(results[{0}], 10);\n",
4835 s:"(\\d{4})" // 4-digit year
4839 c:"var ty = parseInt(results[{0}], 10);\n"
4840 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
4844 * In the am/pm parsing routines, we allow both upper and lower case
4845 * even though it doesn't exactly match the spec. It gives much more flexibility
4846 * in being able to specify case insensitive regexes.
4852 // We need to calculate the hour before we apply AM/PM when parsing
4855 c:"if (/(am)/i.test(results[{0}])) {\n"
4856 + "if (!h || h == 12) { h = 0; }\n"
4857 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4865 c:"h = parseInt(results[{0}], 10);\n",
4866 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
4873 c:"h = parseInt(results[{0}], 10);\n",
4874 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
4878 c:"i = parseInt(results[{0}], 10);\n",
4879 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
4883 c:"s = parseInt(results[{0}], 10);\n",
4884 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
4888 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
4889 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4894 "o = results[{0}];",
4895 "var sn = o.substring(0,1),", // get + / - sign
4896 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4897 "mn = o.substring(3,5) % 60;", // get minutes
4898 "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
4900 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
4905 "o = results[{0}];",
4906 "var sn = o.substring(0,1),", // get + / - sign
4907 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4908 "mn = o.substring(4,6) % 60;", // get minutes
4909 "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
4911 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
4916 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
4920 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
4921 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
4922 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
4928 $f("m", 2), // month
4931 $f("i", 5), // minute
4932 $f("s", 6), // second
4933 {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)
4934 {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
4935 "if(results[8]) {", // timezone specified
4936 "if(results[8] == 'Z'){",
4938 "}else if (results[8].indexOf(':') > -1){",
4939 $f("P", 8).c, // timezone offset with colon separator
4941 $f("O", 8).c, // timezone offset without colon separator
4947 for (var i = 0, l = arr.length; i < l; ++i) {
4948 calc.push(arr[i].c);
4955 arr[0].s, // year (required)
4956 "(?:", "-", arr[1].s, // month (optional)
4957 "(?:", "-", arr[2].s, // day (optional)
4959 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
4960 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
4961 "(?::", arr[5].s, ")?", // seconds (optional)
4962 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
4963 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
4972 c:"u = parseInt(results[{0}], 10);\n",
4973 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
4980 Ext.apply(Date.prototype, {
4982 dateFormat : function(format) {
4983 if (Date.formatFunctions[format] == null) {
4984 Date.createFormat(format);
4986 return Date.formatFunctions[format].call(this);
4990 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
4992 * Note: The date string returned by the javascript Date object's toString() method varies
4993 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
4994 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
4995 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
4996 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
4997 * from the GMT offset portion of the date string.
4998 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
5000 getTimezone : function() {
5001 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
5003 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
5004 // 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)
5005 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
5006 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
5007 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
5009 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
5010 // step 1: (?:\((.*)\) -- find timezone in parentheses
5011 // 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
5012 // step 3: remove all non uppercase characters found in step 1 and 2
5013 return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
5017 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
5018 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
5019 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
5021 getGMTOffset : function(colon) {
5022 return (this.getTimezoneOffset() > 0 ? "-" : "+")
5023 + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
5024 + (colon ? ":" : "")
5025 + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
5029 * Get the numeric day number of the year, adjusted for leap year.
5030 * @return {Number} 0 to 364 (365 in leap years).
5032 getDayOfYear: function() {
5035 m = this.getMonth(),
5038 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
5039 num += d.getDaysInMonth();
5041 return num + this.getDate() - 1;
5045 * Get the numeric ISO-8601 week number of the year.
5046 * (equivalent to the format specifier 'W', but without a leading zero).
5047 * @return {Number} 1 to 53
5049 getWeekOfYear : function() {
5050 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
5051 var ms1d = 864e5, // milliseconds in a day
5052 ms7d = 7 * ms1d; // milliseconds in a week
5054 return function() { // return a closure so constants get calculated only once
5055 var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
5056 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
5057 Wyr = new Date(AWN * ms7d).getUTCFullYear();
5059 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
5064 * Checks if the current date falls within a leap year.
5065 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
5067 isLeapYear : function() {
5068 var year = this.getFullYear();
5069 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
5073 * Get the first day of the current month, adjusted for leap year. The returned value
5074 * is the numeric day index within the week (0-6) which can be used in conjunction with
5075 * the {@link #monthNames} array to retrieve the textual day name.
5078 var dt = new Date('1/10/2007');
5079 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
5081 * @return {Number} The day number (0-6).
5083 getFirstDayOfMonth : function() {
5084 var day = (this.getDay() - (this.getDate() - 1)) % 7;
5085 return (day < 0) ? (day + 7) : day;
5089 * Get the last day of the current month, adjusted for leap year. The returned value
5090 * is the numeric day index within the week (0-6) which can be used in conjunction with
5091 * the {@link #monthNames} array to retrieve the textual day name.
5094 var dt = new Date('1/10/2007');
5095 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
5097 * @return {Number} The day number (0-6).
5099 getLastDayOfMonth : function() {
5100 return this.getLastDateOfMonth().getDay();
5105 * Get the date of the first day of the month in which this date resides.
5108 getFirstDateOfMonth : function() {
5109 return new Date(this.getFullYear(), this.getMonth(), 1);
5113 * Get the date of the last day of the month in which this date resides.
5116 getLastDateOfMonth : function() {
5117 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
5121 * Get the number of days in the current month, adjusted for leap year.
5122 * @return {Number} The number of days in the month.
5124 getDaysInMonth: function() {
5125 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
5127 return function() { // return a closure for efficiency
5128 var m = this.getMonth();
5130 return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
5135 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
5136 * @return {String} 'st, 'nd', 'rd' or 'th'.
5138 getSuffix : function() {
5139 switch (this.getDate()) {
5156 * Creates and returns a new Date instance with the exact same date value as the called instance.
5157 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
5158 * variable will also be changed. When the intention is to create a new variable that will not
5159 * modify the original instance, you should create a clone.
5161 * Example of correctly cloning a date:
5164 var orig = new Date('10/1/2006');
5167 document.write(orig); //returns 'Thu Oct 05 2006'!
5170 var orig = new Date('10/1/2006');
5171 var copy = orig.clone();
5173 document.write(orig); //returns 'Thu Oct 01 2006'
5175 * @return {Date} The new Date instance.
5177 clone : function() {
5178 return new Date(this.getTime());
5182 * Checks if the current date is affected by Daylight Saving Time (DST).
5183 * @return {Boolean} True if the current date is affected by DST.
5185 isDST : function() {
5186 // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
5187 // courtesy of @geoffrey.mcgill
5188 return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
5192 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
5193 * automatically adjusting for Daylight Saving Time (DST) where applicable.
5194 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
5195 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
5196 * @return {Date} this or the clone.
5198 clearTime : function(clone) {
5200 return this.clone().clearTime();
5203 // get current date before clearing time
5204 var d = this.getDate();
5210 this.setMilliseconds(0);
5212 if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
5213 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
5214 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
5216 // increment hour until cloned date == current date
5217 for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
5220 this.setHours(c.getHours());
5227 * Provides a convenient method for performing basic date arithmetic. This method
5228 * does not modify the Date instance being called - it creates and returns
5229 * a new Date instance containing the resulting date value.
5234 var dt = new Date('10/29/2006').add(Date.DAY, 5);
5235 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
5237 // Negative values will be subtracted:
5238 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
5239 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
5241 // You can even chain several calls together in one line:
5242 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
5243 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
5246 * @param {String} interval A valid date interval enum value.
5247 * @param {Number} value The amount to add to the current date.
5248 * @return {Date} The new Date instance.
5250 add : function(interval, value) {
5251 var d = this.clone();
5252 if (!interval || value === 0) return d;
5254 switch(interval.toLowerCase()) {
5256 d.setMilliseconds(this.getMilliseconds() + value);
5259 d.setSeconds(this.getSeconds() + value);
5262 d.setMinutes(this.getMinutes() + value);
5265 d.setHours(this.getHours() + value);
5268 d.setDate(this.getDate() + value);
5271 var day = this.getDate();
5273 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
5276 d.setMonth(this.getMonth() + value);
5279 d.setFullYear(this.getFullYear() + value);
5286 * Checks if this date falls on or between the given start and end dates.
5287 * @param {Date} start Start date
5288 * @param {Date} end End date
5289 * @return {Boolean} true if this date falls on or between the given start and end dates.
5291 between : function(start, end) {
5292 var t = this.getTime();
5293 return start.getTime() <= t && t <= end.getTime();
5299 * Formats a date given the supplied format string.
5300 * @param {String} format The format string.
5301 * @return {String} The formatted date.
5304 Date.prototype.format = Date.prototype.dateFormat;
5308 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
5309 Ext.apply(Date.prototype, {
5310 _xMonth : Date.prototype.setMonth,
5311 _xDate : Date.prototype.setDate,
5313 // Bug in Safari 1.3, 2.0 (WebKit build < 420)
5314 // Date.setMonth does not work consistently if iMonth is not 0-11
5315 setMonth : function(num) {
5317 var n = Math.ceil(-num),
5318 back_year = Math.ceil(n / 12),
5319 month = (n % 12) ? 12 - n % 12 : 0;
5321 this.setFullYear(this.getFullYear() - back_year);
5323 return this._xMonth(month);
5325 return this._xMonth(num);
5329 // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
5330 // The parameter for Date.setDate() is converted to a signed byte integer in Safari
5331 // http://brianary.blogspot.com/2006/03/safari-date-bug.html
5332 setDate : function(d) {
5333 // use setTime() to workaround setDate() bug
5334 // subtract current day of month in milliseconds, then add desired day of month in milliseconds
5335 return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
5342 /* Some basic Date tests... (requires Firebug)
5344 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
5345 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
5348 console.group('Standard Date.parseDate() Tests');
5349 console.log('Date.parseDate("2009-01-05T11:38:56", "c") = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
5350 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
5351 console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c") = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
5352 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
5353 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
5356 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
5357 // -- accepts ALL 6 levels of date-time granularity
5358 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
5359 console.log('Date.parseDate("1997", "c") = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
5360 console.log('Date.parseDate("1997-07", "c") = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
5361 console.log('Date.parseDate("1997-07-16", "c") = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
5362 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)
5363 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)
5364 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)
5365 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)
5366 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
5371 * @class Ext.util.MixedCollection
5372 * @extends Ext.util.Observable
5373 * A Collection class that maintains both numeric indexes and keys and exposes events.
5375 * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
5376 * function should add function references to the collection. Defaults to
5378 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
5379 * and return the key value for that item. This is used when available to look up the key on items that
5380 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
5381 * equivalent to providing an implementation for the {@link #getKey} method.
5383 Ext.util.MixedCollection = function(allowFunctions, keyFn){
5391 * Fires when the collection is cleared.
5396 * Fires when an item is added to the collection.
5397 * @param {Number} index The index at which the item was added.
5398 * @param {Object} o The item added.
5399 * @param {String} key The key associated with the added item.
5404 * Fires when an item is replaced in the collection.
5405 * @param {String} key he key associated with the new added.
5406 * @param {Object} old The item being replaced.
5407 * @param {Object} new The new item.
5412 * Fires when an item is removed from the collection.
5413 * @param {Object} o The item being removed.
5414 * @param {String} key (optional) The key associated with the removed item.
5419 this.allowFunctions = allowFunctions === true;
5421 this.getKey = keyFn;
5423 Ext.util.MixedCollection.superclass.constructor.call(this);
5426 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
5429 * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
5430 * function should add function references to the collection. Defaults to
5433 allowFunctions : false,
5436 * Adds an item to the collection. Fires the {@link #add} event when complete.
5437 * @param {String} key <p>The key to associate with the item, or the new item.</p>
5438 * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
5439 * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
5440 * the MixedCollection will be able to <i>derive</i> the key for the new item.
5441 * In this case just pass the new item in this parameter.</p>
5442 * @param {Object} o The item to add.
5443 * @return {Object} The item added.
5445 add : function(key, o){
5446 if(arguments.length == 1){
5448 key = this.getKey(o);
5450 if(typeof key != 'undefined' && key !== null){
5451 var old = this.map[key];
5452 if(typeof old != 'undefined'){
5453 return this.replace(key, o);
5459 this.keys.push(key);
5460 this.fireEvent('add', this.length-1, o, key);
5465 * MixedCollection has a generic way to fetch keys if you implement getKey. The default implementation
5466 * simply returns <b><code>item.id</code></b> but you can provide your own implementation
5467 * to return a different value as in the following examples:<pre><code>
5469 var mc = new Ext.util.MixedCollection();
5470 mc.add(someEl.dom.id, someEl);
5471 mc.add(otherEl.dom.id, otherEl);
5475 var mc = new Ext.util.MixedCollection();
5476 mc.getKey = function(el){
5482 // or via the constructor
5483 var mc = new Ext.util.MixedCollection(false, function(el){
5489 * @param {Object} item The item for which to find the key.
5490 * @return {Object} The key for the passed item.
5492 getKey : function(o){
5497 * Replaces an item in the collection. Fires the {@link #replace} event when complete.
5498 * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
5499 * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
5500 * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
5501 * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
5502 * with one having the same key value, then just pass the replacement item in this parameter.</p>
5503 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
5505 * @return {Object} The new item.
5507 replace : function(key, o){
5508 if(arguments.length == 1){
5510 key = this.getKey(o);
5512 var old = this.map[key];
5513 if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
5514 return this.add(key, o);
5516 var index = this.indexOfKey(key);
5517 this.items[index] = o;
5519 this.fireEvent('replace', key, old, o);
5524 * Adds all elements of an Array or an Object to the collection.
5525 * @param {Object/Array} objs An Object containing properties which will be added
5526 * to the collection, or an Array of values, each of which are added to the collection.
5527 * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
5528 * has been set to <tt>true</tt>.
5530 addAll : function(objs){
5531 if(arguments.length > 1 || Ext.isArray(objs)){
5532 var args = arguments.length > 1 ? arguments : objs;
5533 for(var i = 0, len = args.length; i < len; i++){
5537 for(var key in objs){
5538 if(this.allowFunctions || typeof objs[key] != 'function'){
5539 this.add(key, objs[key]);
5546 * Executes the specified function once for every item in the collection, passing the following arguments:
5547 * <div class="mdetail-params"><ul>
5548 * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
5549 * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
5550 * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
5552 * The function should return a boolean value. Returning false from the function will stop the iteration.
5553 * @param {Function} fn The function to execute for each item.
5554 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
5556 each : function(fn, scope){
5557 var items = [].concat(this.items); // each safe for removal
5558 for(var i = 0, len = items.length; i < len; i++){
5559 if(fn.call(scope || items[i], items[i], i, len) === false){
5566 * Executes the specified function once for every key in the collection, passing each
5567 * key, and its associated item as the first two parameters.
5568 * @param {Function} fn The function to execute for each item.
5569 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
5571 eachKey : function(fn, scope){
5572 for(var i = 0, len = this.keys.length; i < len; i++){
5573 fn.call(scope || window, this.keys[i], this.items[i], i, len);
5578 * Returns the first item in the collection which elicits a true return value from the
5579 * passed selection function.
5580 * @param {Function} fn The selection function to execute for each item.
5581 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
5582 * @return {Object} The first item in the collection which returned true from the selection function.
5584 find : function(fn, scope){
5585 for(var i = 0, len = this.items.length; i < len; i++){
5586 if(fn.call(scope || window, this.items[i], this.keys[i])){
5587 return this.items[i];
5594 * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
5595 * @param {Number} index The index to insert the item at.
5596 * @param {String} key The key to associate with the new item, or the item itself.
5597 * @param {Object} o (optional) If the second parameter was a key, the new item.
5598 * @return {Object} The item inserted.
5600 insert : function(index, key, o){
5601 if(arguments.length == 2){
5603 key = this.getKey(o);
5605 if(this.containsKey(key)){
5606 this.suspendEvents();
5607 this.removeKey(key);
5608 this.resumeEvents();
5610 if(index >= this.length){
5611 return this.add(key, o);
5614 this.items.splice(index, 0, o);
5615 if(typeof key != 'undefined' && key !== null){
5618 this.keys.splice(index, 0, key);
5619 this.fireEvent('add', index, o, key);
5624 * Remove an item from the collection.
5625 * @param {Object} o The item to remove.
5626 * @return {Object} The item removed or false if no item was removed.
5628 remove : function(o){
5629 return this.removeAt(this.indexOf(o));
5633 * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
5634 * @param {Number} index The index within the collection of the item to remove.
5635 * @return {Object} The item removed or false if no item was removed.
5637 removeAt : function(index){
5638 if(index < this.length && index >= 0){
5640 var o = this.items[index];
5641 this.items.splice(index, 1);
5642 var key = this.keys[index];
5643 if(typeof key != 'undefined'){
5644 delete this.map[key];
5646 this.keys.splice(index, 1);
5647 this.fireEvent('remove', o, key);
5654 * Removed an item associated with the passed key fom the collection.
5655 * @param {String} key The key of the item to remove.
5656 * @return {Object} The item removed or false if no item was removed.
5658 removeKey : function(key){
5659 return this.removeAt(this.indexOfKey(key));
5663 * Returns the number of items in the collection.
5664 * @return {Number} the number of items in the collection.
5666 getCount : function(){
5671 * Returns index within the collection of the passed Object.
5672 * @param {Object} o The item to find the index of.
5673 * @return {Number} index of the item. Returns -1 if not found.
5675 indexOf : function(o){
5676 return this.items.indexOf(o);
5680 * Returns index within the collection of the passed key.
5681 * @param {String} key The key to find the index of.
5682 * @return {Number} index of the key.
5684 indexOfKey : function(key){
5685 return this.keys.indexOf(key);
5689 * Returns the item associated with the passed key OR index.
5690 * Key has priority over index. This is the equivalent
5691 * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
5692 * @param {String/Number} key The key or index of the item.
5693 * @return {Object} If the item is found, returns the item. If the item was not found, returns <tt>undefined</tt>.
5694 * If an item was found, but is a Class, returns <tt>null</tt>.
5696 item : function(key){
5697 var mk = this.map[key],
5698 item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
5699 return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
5703 * Returns the item at the specified index.
5704 * @param {Number} index The index of the item.
5705 * @return {Object} The item at the specified index.
5707 itemAt : function(index){
5708 return this.items[index];
5712 * Returns the item associated with the passed key.
5713 * @param {String/Number} key The key of the item.
5714 * @return {Object} The item associated with the passed key.
5716 key : function(key){
5717 return this.map[key];
5721 * Returns true if the collection contains the passed Object as an item.
5722 * @param {Object} o The Object to look for in the collection.
5723 * @return {Boolean} True if the collection contains the Object as an item.
5725 contains : function(o){
5726 return this.indexOf(o) != -1;
5730 * Returns true if the collection contains the passed Object as a key.
5731 * @param {String} key The key to look for in the collection.
5732 * @return {Boolean} True if the collection contains the Object as a key.
5734 containsKey : function(key){
5735 return typeof this.map[key] != 'undefined';
5739 * Removes all items from the collection. Fires the {@link #clear} event when complete.
5746 this.fireEvent('clear');
5750 * Returns the first item in the collection.
5751 * @return {Object} the first item in the collection..
5754 return this.items[0];
5758 * Returns the last item in the collection.
5759 * @return {Object} the last item in the collection..
5762 return this.items[this.length-1];
5767 * Performs the actual sorting based on a direction and a sorting function. Internally,
5768 * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
5769 * the sorted array data back into this.items and this.keys
5770 * @param {String} property Property to sort by ('key', 'value', or 'index')
5771 * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
5772 * @param {Function} fn (optional) Comparison function that defines the sort order.
5773 * Defaults to sorting by numeric value.
5775 _sort : function(property, dir, fn){
5777 dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
5779 //this is a temporary array used to apply the sorting function
5784 //default to a simple sorter function if one is not provided
5785 fn = fn || function(a, b) {
5789 //copy all the items into a temporary array, which we will sort
5790 for(i = 0, len = items.length; i < len; i++){
5798 //sort the temporary array
5799 c.sort(function(a, b){
5800 var v = fn(a[property], b[property]) * dsc;
5802 v = (a.index < b.index ? -1 : 1);
5807 //copy the temporary array back into the main this.items and this.keys objects
5808 for(i = 0, len = c.length; i < len; i++){
5809 items[i] = c[i].value;
5813 this.fireEvent('sort', this);
5817 * Sorts this collection by <b>item</b> value with the passed comparison function.
5818 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
5819 * @param {Function} fn (optional) Comparison function that defines the sort order.
5820 * Defaults to sorting by numeric value.
5822 sort : function(dir, fn){
5823 this._sort('value', dir, fn);
5827 * Reorders each of the items based on a mapping from old index to new index. Internally this
5828 * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
5829 * @param {Object} mapping Mapping from old item index to new item index
5831 reorder: function(mapping) {
5832 this.suspendEvents();
5834 var items = this.items,
5836 length = items.length,
5841 //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
5842 for (oldIndex in mapping) {
5843 order[mapping[oldIndex]] = items[oldIndex];
5846 for (index = 0; index < length; index++) {
5847 if (mapping[index] == undefined) {
5848 remaining.push(items[index]);
5852 for (index = 0; index < length; index++) {
5853 if (order[index] == undefined) {
5854 order[index] = remaining.shift();
5861 this.resumeEvents();
5862 this.fireEvent('sort', this);
5866 * Sorts this collection by <b>key</b>s.
5867 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
5868 * @param {Function} fn (optional) Comparison function that defines the sort order.
5869 * Defaults to sorting by case insensitive string.
5871 keySort : function(dir, fn){
5872 this._sort('key', dir, fn || function(a, b){
5873 var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
5874 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5879 * Returns a range of items in this collection
5880 * @param {Number} startIndex (optional) The starting index. Defaults to 0.
5881 * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
5882 * @return {Array} An array of items
5884 getRange : function(start, end){
5885 var items = this.items;
5886 if(items.length < 1){
5890 end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
5893 for(i = start; i <= end; i++) {
5894 r[r.length] = items[i];
5897 for(i = start; i >= end; i--) {
5898 r[r.length] = items[i];
5905 * Filter the <i>objects</i> in this collection by a specific property.
5906 * Returns a new collection that has been filtered.
5907 * @param {String} property A property on your objects
5908 * @param {String/RegExp} value Either string that the property values
5909 * should start with or a RegExp to test against the property
5910 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
5911 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
5912 * @return {MixedCollection} The new filtered collection
5914 filter : function(property, value, anyMatch, caseSensitive){
5915 if(Ext.isEmpty(value, false)){
5916 return this.clone();
5918 value = this.createValueMatcher(value, anyMatch, caseSensitive);
5919 return this.filterBy(function(o){
5920 return o && value.test(o[property]);
5925 * Filter by a function. Returns a <i>new</i> collection that has been filtered.
5926 * The passed function will be called with each object in the collection.
5927 * If the function returns true, the value is included otherwise it is filtered.
5928 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
5929 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
5930 * @return {MixedCollection} The new filtered collection
5932 filterBy : function(fn, scope){
5933 var r = new Ext.util.MixedCollection();
5934 r.getKey = this.getKey;
5935 var k = this.keys, it = this.items;
5936 for(var i = 0, len = it.length; i < len; i++){
5937 if(fn.call(scope||this, it[i], k[i])){
5945 * Finds the index of the first matching object in this collection by a specific property/value.
5946 * @param {String} property The name of a property on your objects.
5947 * @param {String/RegExp} value A string that the property values
5948 * should start with or a RegExp to test against the property.
5949 * @param {Number} start (optional) The index to start searching at (defaults to 0).
5950 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
5951 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
5952 * @return {Number} The matched index or -1
5954 findIndex : function(property, value, start, anyMatch, caseSensitive){
5955 if(Ext.isEmpty(value, false)){
5958 value = this.createValueMatcher(value, anyMatch, caseSensitive);
5959 return this.findIndexBy(function(o){
5960 return o && value.test(o[property]);
5965 * Find the index of the first matching object in this collection by a function.
5966 * If the function returns <i>true</i> it is considered a match.
5967 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
5968 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
5969 * @param {Number} start (optional) The index to start searching at (defaults to 0).
5970 * @return {Number} The matched index or -1
5972 findIndexBy : function(fn, scope, start){
5973 var k = this.keys, it = this.items;
5974 for(var i = (start||0), len = it.length; i < len; i++){
5975 if(fn.call(scope||this, it[i], k[i])){
5983 * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
5984 * and by Ext.data.Store#filter
5986 * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
5987 * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
5988 * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
5989 * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
5991 createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
5992 if (!value.exec) { // not a regex
5993 var er = Ext.escapeRe;
5994 value = String(value);
5996 if (anyMatch === true) {
5999 value = '^' + er(value);
6000 if (exactMatch === true) {
6004 value = new RegExp(value, caseSensitive ? '' : 'i');
6010 * Creates a shallow copy of this collection
6011 * @return {MixedCollection}
6014 var r = new Ext.util.MixedCollection();
6015 var k = this.keys, it = this.items;
6016 for(var i = 0, len = it.length; i < len; i++){
6019 r.getKey = this.getKey;
6024 * This method calls {@link #item item()}.
6025 * Returns the item associated with the passed key OR index. Key has priority
6026 * over index. This is the equivalent of calling {@link #key} first, then if
6027 * nothing matched calling {@link #itemAt}.
6028 * @param {String/Number} key The key or index of the item.
6029 * @return {Object} If the item is found, returns the item. If the item was
6030 * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
6031 * returns <tt>null</tt>.
6033 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;
6035 * @class Ext.AbstractManager
6037 * Base Manager class - extended by ComponentMgr and PluginMgr
6039 Ext.AbstractManager = Ext.extend(Object, {
6042 constructor: function(config) {
6043 Ext.apply(this, config || {});
6046 * Contains all of the items currently managed
6048 * @type Ext.util.MixedCollection
6050 this.all = new Ext.util.MixedCollection();
6056 * Returns a component by {@link Ext.Component#id id}.
6057 * For additional details see {@link Ext.util.MixedCollection#get}.
6058 * @param {String} id The component {@link Ext.Component#id id}
6059 * @return Ext.Component The Component, <code>undefined</code> if not found, or <code>null</code> if a
6063 return this.all.get(id);
6067 * Registers an item to be managed
6068 * @param {Mixed} item The item to register
6070 register: function(item) {
6075 * Unregisters a component by removing it from this manager
6076 * @param {Mixed} item The item to unregister
6078 unregister: function(item) {
6079 this.all.remove(item);
6083 * <p>Registers a new Component constructor, keyed by a new
6084 * {@link Ext.Component#xtype}.</p>
6085 * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
6086 * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
6088 * see {@link Ext.Container#items}</p>
6089 * @param {String} xtype The mnemonic string by which the Component class may be looked up.
6090 * @param {Constructor} cls The new Component class.
6092 registerType : function(type, cls){
6093 this.types[type] = cls;
6094 cls[this.typeName] = type;
6098 * Checks if a Component type is registered.
6099 * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
6100 * @return {Boolean} Whether the type is registered.
6102 isRegistered : function(type){
6103 return this.types[type] !== undefined;
6107 * Creates and returns an instance of whatever this manager manages, based on the supplied type and config object
6108 * @param {Object} config The config object
6109 * @param {String} defaultType If no type is discovered in the config object, we fall back to this type
6110 * @return {Mixed} The instance of whatever this manager is managing
6112 create: function(config, defaultType) {
6113 var type = config[this.typeName] || config.type || defaultType,
6114 Constructor = this.types[type];
6116 if (Constructor == undefined) {
6117 throw new Error(String.format("The '{0}' type has not been registered with this manager", type));
6120 return new Constructor(config);
6124 * Registers a function that will be called when a Component with the specified id is added to the manager. This will happen on instantiation.
6125 * @param {String} id The component {@link Ext.Component#id id}
6126 * @param {Function} fn The callback function
6127 * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed. Defaults to the Component.
6129 onAvailable : function(id, fn, scope){
6132 all.on("add", function(index, o){
6134 fn.call(scope || o, o);
6135 all.un("add", fn, scope);
6140 * @class Ext.util.Format
6141 * Reusable data formatting functions
6144 Ext.util.Format = function() {
6145 var trimRe = /^\s+|\s+$/g,
6146 stripTagsRE = /<\/?[^>]+>/gi,
6147 stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
6152 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
6153 * @param {String} value The string to truncate
6154 * @param {Number} length The maximum length to allow before truncating
6155 * @param {Boolean} word True to try to find a common work break
6156 * @return {String} The converted text
6158 ellipsis : function(value, len, word) {
6159 if (value && value.length > len) {
6161 var vs = value.substr(0, len - 2),
6162 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
6163 if (index == -1 || index < (len - 15)) {
6164 return value.substr(0, len - 3) + "...";
6166 return vs.substr(0, index) + "...";
6169 return value.substr(0, len - 3) + "...";
6176 * Checks a reference and converts it to empty string if it is undefined
6177 * @param {Mixed} value Reference to check
6178 * @return {Mixed} Empty string if converted, otherwise the original value
6180 undef : function(value) {
6181 return value !== undefined ? value : "";
6185 * Checks a reference and converts it to the default value if it's empty
6186 * @param {Mixed} value Reference to check
6187 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
6190 defaultValue : function(value, defaultValue) {
6191 return value !== undefined && value !== '' ? value : defaultValue;
6195 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
6196 * @param {String} value The string to encode
6197 * @return {String} The encoded text
6199 htmlEncode : function(value) {
6200 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
6204 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
6205 * @param {String} value The string to decode
6206 * @return {String} The decoded text
6208 htmlDecode : function(value) {
6209 return !value ? value : String(value).replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&");
6213 * Trims any whitespace from either side of a string
6214 * @param {String} value The text to trim
6215 * @return {String} The trimmed text
6217 trim : function(value) {
6218 return String(value).replace(trimRe, "");
6222 * Returns a substring from within an original string
6223 * @param {String} value The original text
6224 * @param {Number} start The start index of the substring
6225 * @param {Number} length The length of the substring
6226 * @return {String} The substring
6228 substr : function(value, start, length) {
6229 return String(value).substr(start, length);
6233 * Converts a string to all lower case letters
6234 * @param {String} value The text to convert
6235 * @return {String} The converted text
6237 lowercase : function(value) {
6238 return String(value).toLowerCase();
6242 * Converts a string to all upper case letters
6243 * @param {String} value The text to convert
6244 * @return {String} The converted text
6246 uppercase : function(value) {
6247 return String(value).toUpperCase();
6251 * Converts the first character only of a string to upper case
6252 * @param {String} value The text to convert
6253 * @return {String} The converted text
6255 capitalize : function(value) {
6256 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
6260 call : function(value, fn) {
6261 if (arguments.length > 2) {
6262 var args = Array.prototype.slice.call(arguments, 2);
6263 args.unshift(value);
6264 return eval(fn).apply(window, args);
6266 return eval(fn).call(window, value);
6271 * Format a number as US currency
6272 * @param {Number/String} value The numeric value to format
6273 * @return {String} The formatted currency string
6275 usMoney : function(v) {
6276 v = (Math.round((v-0)*100))/100;
6277 v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
6279 var ps = v.split('.'),
6281 sub = ps[1] ? '.'+ ps[1] : '.00',
6283 while (r.test(whole)) {
6284 whole = whole.replace(r, '$1' + ',' + '$2');
6287 if (v.charAt(0) == '-') {
6288 return '-$' + v.substr(1);
6294 * Parse a value into a formatted date using the specified format pattern.
6295 * @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)
6296 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
6297 * @return {String} The formatted date string
6299 date : function(v, format) {
6303 if (!Ext.isDate(v)) {
6304 v = new Date(Date.parse(v));
6306 return v.dateFormat(format || "m/d/Y");
6310 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
6311 * @param {String} format Any valid date format string
6312 * @return {Function} The date formatting function
6314 dateRenderer : function(format) {
6315 return function(v) {
6316 return Ext.util.Format.date(v, format);
6321 * Strips all HTML tags
6322 * @param {Mixed} value The text from which to strip tags
6323 * @return {String} The stripped text
6325 stripTags : function(v) {
6326 return !v ? v : String(v).replace(stripTagsRE, "");
6330 * Strips all script tags
6331 * @param {Mixed} value The text from which to strip script tags
6332 * @return {String} The stripped text
6334 stripScripts : function(v) {
6335 return !v ? v : String(v).replace(stripScriptsRe, "");
6339 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
6340 * @param {Number/String} size The numeric value to format
6341 * @return {String} The formatted file size
6343 fileSize : function(size) {
6345 return size + " bytes";
6346 } else if (size < 1048576) {
6347 return (Math.round(((size*10) / 1024))/10) + " KB";
6349 return (Math.round(((size*10) / 1048576))/10) + " MB";
6354 * It does simple math for use in a template, for example:<pre><code>
6355 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
6357 * @return {Function} A function that operates on the passed value.
6362 return function(v, a){
6364 fns[a] = new Function('v', 'return v ' + a + ';');
6371 * Rounds the passed number to the required decimal precision.
6372 * @param {Number/String} value The numeric value to round.
6373 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
6374 * @return {Number} The rounded value.
6376 round : function(value, precision) {
6377 var result = Number(value);
6378 if (typeof precision == 'number') {
6379 precision = Math.pow(10, precision);
6380 result = Math.round(value * precision) / precision;
6386 * Formats the number according to the format string.
6387 * <div style="margin-left:40px">examples (123456.789):
6388 * <div style="margin-left:10px">
6389 * 0 - (123456) show only digits, no precision<br>
6390 * 0.00 - (123456.78) show only digits, 2 precision<br>
6391 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
6392 * 0,000 - (123,456) show comma and digits, no precision<br>
6393 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
6394 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
6395 * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
6396 * For example: 0.000,00/i
6398 * @param {Number} v The number to format.
6399 * @param {String} format The way you would like to format this text.
6400 * @return {String} The formatted number.
6402 number: function(v, format) {
6406 v = Ext.num(v, NaN);
6416 if (format.substr(format.length - 2) == '/i') {
6417 format = format.substr(0, format.length - 2);
6423 var hasComma = format.indexOf(comma) != -1,
6424 psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
6426 if (1 < psplit.length) {
6427 v = v.toFixed(psplit[1].length);
6428 } else if(2 < psplit.length) {
6429 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
6434 var fnum = v.toString();
6436 psplit = fnum.split('.');
6439 var cnum = psplit[0],
6442 m = Math.floor(j / 3),
6443 n = cnum.length % 3 || 3,
6446 for (i = 0; i < j; i += n) {
6451 parr[parr.length] = cnum.substr(i, n);
6454 fnum = parr.join(comma);
6456 fnum += dec + psplit[1];
6460 fnum = psplit[0] + dec + psplit[1];
6464 return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
6468 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
6469 * @param {String} format Any valid number format string for {@link #number}
6470 * @return {Function} The number formatting function
6472 numberRenderer : function(format) {
6473 return function(v) {
6474 return Ext.util.Format.number(v, format);
6479 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
6480 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
6481 * if the value is 0 or greater than 1.
6482 * @param {Number} value The value to compare against
6483 * @param {String} singular The singular form of the word
6484 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
6486 plural : function(v, s, p) {
6487 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
6491 * Converts newline characters to the HTML tag <br/>
6492 * @param {String} The string value to format.
6493 * @return {String} The string with embedded <br/> tags in place of newlines.
6495 nl2br : function(v) {
6496 return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
6501 * @class Ext.XTemplate
6502 * @extends Ext.Template
6503 * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
6504 * <li>Autofilling arrays using templates and sub-templates</li>
6505 * <li>Conditional processing with basic comparison operators</li>
6506 * <li>Basic math function support</li>
6507 * <li>Execute arbitrary inline code with special built-in template variables</li>
6508 * <li>Custom member functions</li>
6509 * <li>Many special tags and built-in operators that aren't defined as part of
6510 * the API, but are supported in the templates that can be created</li>
6512 * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
6513 * <li>{@link Ext.DataView}</li>
6514 * <li>{@link Ext.ListView}</li>
6515 * <li>{@link Ext.form.ComboBox}</li>
6516 * <li>{@link Ext.grid.TemplateColumn}</li>
6517 * <li>{@link Ext.grid.GroupingView}</li>
6518 * <li>{@link Ext.menu.Item}</li>
6519 * <li>{@link Ext.layout.MenuLayout}</li>
6520 * <li>{@link Ext.ColorPalette}</li>
6523 * <p>For example usage {@link #XTemplate see the constructor}.</p>
6526 * The {@link Ext.Template#Template Ext.Template constructor} describes
6527 * the acceptable parameters to pass to the constructor. The following
6528 * examples demonstrate all of the supported features.</p>
6530 * <div class="mdetail-params"><ul>
6532 * <li><b><u>Sample Data</u></b>
6533 * <div class="sub-desc">
6534 * <p>This is the data object used for reference in each code example:</p>
6537 name: 'Jack Slocum',
6538 title: 'Lead Developer',
6539 company: 'Ext JS, LLC',
6540 email: 'jack@extjs.com',
6541 address: '4 Red Bulls Drive',
6545 drinks: ['Red Bull', 'Coffee', 'Water'],
6562 * <li><b><u>Auto filling of arrays</u></b>
6563 * <div class="sub-desc">
6564 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
6565 * to process the provided data object:
6567 * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
6568 * repeating the template block inside the <tt>tpl</tt> tag for each item in the
6570 * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
6571 * <li>While processing an array, the special variable <tt>{#}</tt>
6572 * will provide the current array index + 1 (starts at 1, not 0).</li>
6576 <tpl <b>for</b>=".">...</tpl> // loop through array at root node
6577 <tpl <b>for</b>="foo">...</tpl> // loop through array at foo node
6578 <tpl <b>for</b>="foo.bar">...</tpl> // loop through array at foo.bar node
6580 * Using the sample data above:
6582 var tpl = new Ext.XTemplate(
6584 '<tpl <b>for</b>=".">', // process the data.kids node
6585 '<p>{#}. {name}</p>', // use current array index to autonumber
6588 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
6590 * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
6591 * to access specified members of the provided data object to populate the template:</p>
6593 var tpl = new Ext.XTemplate(
6594 '<p>Name: {name}</p>',
6595 '<p>Title: {title}</p>',
6596 '<p>Company: {company}</p>',
6598 '<tpl <b>for="kids"</b>>', // interrogate the kids property within the data
6599 '<p>{name}</p>',
6602 tpl.overwrite(panel.body, data); // pass the root node of the data object
6604 * <p>Flat arrays that contain values (and not objects) can be auto-rendered
6605 * using the special <b><tt>{.}</tt></b> variable inside a loop. This variable
6606 * will represent the value of the array at the current index:</p>
6608 var tpl = new Ext.XTemplate(
6609 '<p>{name}\'s favorite beverages:</p>',
6610 '<tpl for="drinks">',
6611 '<div> - {.}</div>',
6614 tpl.overwrite(panel.body, data);
6616 * <p>When processing a sub-template, for example while looping through a child array,
6617 * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
6619 var tpl = new Ext.XTemplate(
6620 '<p>Name: {name}</p>',
6622 '<tpl for="kids">',
6623 '<tpl if="age > 1">',
6624 '<p>{name}</p>',
6625 '<p>Dad: {<b>parent</b>.name}</p>',
6629 tpl.overwrite(panel.body, data);
6635 * <li><b><u>Conditional processing with basic comparison operators</u></b>
6636 * <div class="sub-desc">
6637 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
6638 * to provide conditional checks for deciding whether or not to render specific
6639 * parts of the template. Notes:<div class="sub-desc"><ul>
6640 * <li>Double quotes must be encoded if used within the conditional</li>
6641 * <li>There is no <tt>else</tt> operator — if needed, two opposite
6642 * <tt>if</tt> statements should be used.</li>
6645 <tpl if="age > 1 && age < 10">Child</tpl>
6646 <tpl if="age >= 10 && age < 18">Teenager</tpl>
6647 <tpl <b>if</b>="this.isGirl(name)">...</tpl>
6648 <tpl <b>if</b>="id==\'download\'">...</tpl>
6649 <tpl <b>if</b>="needsIcon"><img src="{icon}" class="{iconCls}"/></tpl>
6651 <tpl if="name == "Jack"">Hello</tpl>
6652 // encode " if it is part of the condition, e.g.
6653 <tpl if="name == &quot;Jack&quot;">Hello</tpl>
6655 * Using the sample data above:
6657 var tpl = new Ext.XTemplate(
6658 '<p>Name: {name}</p>',
6660 '<tpl for="kids">',
6661 '<tpl if="age > 1">',
6662 '<p>{name}</p>',
6666 tpl.overwrite(panel.body, data);
6672 * <li><b><u>Basic math support</u></b>
6673 * <div class="sub-desc">
6674 * <p>The following basic math operators may be applied directly on numeric
6675 * data values:</p><pre>
6680 var tpl = new Ext.XTemplate(
6681 '<p>Name: {name}</p>',
6683 '<tpl for="kids">',
6684 '<tpl if="age &gt; 1">', // <-- Note that the > is encoded
6685 '<p>{#}: {name}</p>', // <-- Auto-number each item
6686 '<p>In 5 Years: {age+5}</p>', // <-- Basic math
6687 '<p>Dad: {parent.name}</p>',
6691 tpl.overwrite(panel.body, data);
6697 * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
6698 * <div class="sub-desc">
6699 * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
6700 * in the scope of the template. There are some special variables available in that code:
6702 * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
6703 * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
6704 * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
6705 * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
6706 * loop you are in (1-based).</li>
6707 * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
6708 * of the array you are looping.</li>
6709 * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
6711 * This example demonstrates basic row striping using an inline code block and the
6712 * <tt>xindex</tt> variable:</p>
6714 var tpl = new Ext.XTemplate(
6715 '<p>Name: {name}</p>',
6716 '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
6718 '<tpl for="kids">',
6719 '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
6724 tpl.overwrite(panel.body, data);
6729 * <li><b><u>Template member functions</u></b>
6730 * <div class="sub-desc">
6731 * <p>One or more member functions can be specified in a configuration
6732 * object passed into the XTemplate constructor for more complex processing:</p>
6734 var tpl = new Ext.XTemplate(
6735 '<p>Name: {name}</p>',
6737 '<tpl for="kids">',
6738 '<tpl if="this.isGirl(name)">',
6739 '<p>Girl: {name} - {age}</p>',
6741 // use opposite if statement to simulate 'else' processing:
6742 '<tpl if="this.isGirl(name) == false">',
6743 '<p>Boy: {name} - {age}</p>',
6745 '<tpl if="this.isBaby(age)">',
6746 '<p>{name} is a baby!</p>',
6750 // XTemplate configuration:
6752 disableFormats: true,
6753 // member functions:
6754 isGirl: function(name){
6755 return name == 'Sara Grace';
6757 isBaby: function(age){
6762 tpl.overwrite(panel.body, data);
6769 * @param {Mixed} config
6771 Ext.XTemplate = function(){
6772 Ext.XTemplate.superclass.constructor.apply(this, arguments);
6776 re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
6777 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
6778 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
6779 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
6788 WITHVALUES = 'with(values){ ';
6790 s = ['<tpl>', s, '</tpl>'].join('');
6792 while((m = s.match(re))){
6793 var m2 = m[0].match(nameRe),
6794 m3 = m[0].match(ifRe),
6795 m4 = m[0].match(execRe),
6799 name = m2 && m2[1] ? m2[1] : '';
6802 exp = m3 && m3[1] ? m3[1] : null;
6804 fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
6808 exp = m4 && m4[1] ? m4[1] : null;
6810 exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
6815 case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
6816 case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
6817 default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
6827 s = s.replace(m[0], '{xtpl'+ id + '}');
6830 for(var i = tpls.length-1; i >= 0; --i){
6831 me.compileTpl(tpls[i]);
6833 me.master = tpls[tpls.length-1];
6836 Ext.extend(Ext.XTemplate, Ext.Template, {
6838 re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
6840 codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
6843 applySubTemplate : function(id, values, parent, xindex, xcount){
6849 if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
6850 (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
6853 vs = t.target ? t.target.call(me, values, parent) : values;
6855 parent = t.target ? values : parent;
6856 if(t.target && Ext.isArray(vs)){
6857 for(var i = 0, len = vs.length; i < len; i++){
6858 buf[buf.length] = t.compiled.call(me, vs[i], parent, i+1, len);
6860 return buf.join('');
6862 return t.compiled.call(me, vs, parent, xindex, xcount);
6866 compileTpl : function(tpl){
6867 var fm = Ext.util.Format,
6868 useF = this.disableFormats !== true,
6869 sep = Ext.isGecko ? "+" : ",",
6872 function fn(m, name, format, args, math){
6873 if(name.substr(0, 4) == 'xtpl'){
6874 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
6879 }else if(name === '#'){
6881 }else if(name.indexOf('.') != -1){
6884 v = "values['" + name + "']";
6887 v = '(' + v + math + ')';
6889 if (format && useF) {
6890 args = args ? ',' + args : "";
6891 if(format.substr(0, 5) != "this."){
6892 format = "fm." + format + '(';
6894 format = 'this.call("'+ format.substr(5) + '", ';
6898 args= ''; format = "("+v+" === undefined ? '' : ";
6900 return "'"+ sep + format + v + args + ")"+sep+"'";
6903 function codeFn(m, code){
6904 // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
6905 return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
6908 // branched to use + in gecko and [].join() in others
6910 body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
6911 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
6914 body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
6915 body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
6916 body.push("'].join('');};");
6917 body = body.join('');
6924 * Returns an HTML fragment of this template with the specified values applied.
6925 * @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'})
6926 * @return {String} The HTML fragment
6928 applyTemplate : function(values){
6929 return this.master.compiled.call(this, values, {}, 1, 1);
6933 * Compile the template to a function for optimized performance. Recommended if the template will be used frequently.
6934 * @return {Function} The compiled function
6936 compile : function(){return this;}
6943 * @property disableFormats
6953 * Alias for {@link #applyTemplate}
6954 * Returns an HTML fragment of this template with the specified values applied.
6955 * @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'})
6956 * @return {String} The HTML fragment
6957 * @member Ext.XTemplate
6960 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
6963 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6964 * @param {String/HTMLElement} el A DOM element or its id
6965 * @return {Ext.Template} The created template
6968 Ext.XTemplate.from = function(el){
6969 el = Ext.getDom(el);
6970 return new Ext.XTemplate(el.value || el.innerHTML);
6973 * @class Ext.util.CSS
6974 * Utility class for manipulating CSS rules
6977 Ext.util.CSS = function(){
6981 var camelRe = /(-[a-z])/gi;
6982 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6986 * Creates a stylesheet from a text blob of rules.
6987 * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
6988 * @param {String} cssText The text containing the css rules
6989 * @param {String} id An id to add to the stylesheet for later removal
6990 * @return {StyleSheet}
6992 createStyleSheet : function(cssText, id){
6994 var head = doc.getElementsByTagName("head")[0];
6995 var rules = doc.createElement("style");
6996 rules.setAttribute("type", "text/css");
6998 rules.setAttribute("id", id);
7001 head.appendChild(rules);
7002 ss = rules.styleSheet;
7003 ss.cssText = cssText;
7006 rules.appendChild(doc.createTextNode(cssText));
7008 rules.cssText = cssText;
7010 head.appendChild(rules);
7011 ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
7013 this.cacheStyleSheet(ss);
7018 * Removes a style or link tag by id
7019 * @param {String} id The id of the tag
7021 removeStyleSheet : function(id){
7022 var existing = doc.getElementById(id);
7024 existing.parentNode.removeChild(existing);
7029 * Dynamically swaps an existing stylesheet reference for a new one
7030 * @param {String} id The id of an existing link tag to remove
7031 * @param {String} url The href of the new stylesheet to include
7033 swapStyleSheet : function(id, url){
7034 this.removeStyleSheet(id);
7035 var ss = doc.createElement("link");
7036 ss.setAttribute("rel", "stylesheet");
7037 ss.setAttribute("type", "text/css");
7038 ss.setAttribute("id", id);
7039 ss.setAttribute("href", url);
7040 doc.getElementsByTagName("head")[0].appendChild(ss);
7044 * Refresh the rule cache if you have dynamically added stylesheets
7045 * @return {Object} An object (hash) of rules indexed by selector
7047 refreshCache : function(){
7048 return this.getRules(true);
7052 cacheStyleSheet : function(ss){
7056 try{// try catch for cross domain access issue
7057 var ssRules = ss.cssRules || ss.rules;
7058 for(var j = ssRules.length-1; j >= 0; --j){
7059 rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
7065 * Gets all css rules for the document
7066 * @param {Boolean} refreshCache true to refresh the internal cache
7067 * @return {Object} An object (hash) of rules indexed by selector
7069 getRules : function(refreshCache){
7070 if(rules === null || refreshCache){
7072 var ds = doc.styleSheets;
7073 for(var i =0, len = ds.length; i < len; i++){
7075 this.cacheStyleSheet(ds[i]);
7083 * Gets an an individual CSS rule by selector(s)
7084 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
7085 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
7086 * @return {CSSRule} The CSS rule or null if one is not found
7088 getRule : function(selector, refreshCache){
7089 var rs = this.getRules(refreshCache);
7090 if(!Ext.isArray(selector)){
7091 return rs[selector.toLowerCase()];
7093 for(var i = 0; i < selector.length; i++){
7094 if(rs[selector[i]]){
7095 return rs[selector[i].toLowerCase()];
7103 * Updates a rule property
7104 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
7105 * @param {String} property The css property
7106 * @param {String} value The new value for the property
7107 * @return {Boolean} true If a rule was found and updated
7109 updateRule : function(selector, property, value){
7110 if(!Ext.isArray(selector)){
7111 var rule = this.getRule(selector);
7113 rule.style[property.replace(camelRe, camelFn)] = value;
7117 for(var i = 0; i < selector.length; i++){
7118 if(this.updateRule(selector[i], property, value)){
7127 @class Ext.util.ClickRepeater
7128 @extends Ext.util.Observable
7130 A wrapper class which can be applied to any element. Fires a "click" event while the
7131 mouse is pressed. The interval between firings may be specified in the config but
7132 defaults to 20 milliseconds.
7134 Optionally, a CSS class may be applied to the element during the time it is pressed.
7136 @cfg {Mixed} el The element to act as a button.
7137 @cfg {Number} delay The initial delay before the repeating event begins firing.
7138 Similar to an autorepeat key delay.
7139 @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
7140 @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
7141 @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
7142 "interval" and "delay" are ignored.
7143 @cfg {Boolean} preventDefault True to prevent the default click event
7144 @cfg {Boolean} stopDefault True to stop the default click event
7147 2007-02-02 jvs Original code contributed by Nige "Animal" White
7148 2007-02-02 jvs Renamed to ClickRepeater
7149 2007-02-03 jvs Modifications for FF Mac and Safari
7152 @param {Mixed} el The element to listen on
7153 @param {Object} config
7155 Ext.util.ClickRepeater = Ext.extend(Ext.util.Observable, {
7157 constructor : function(el, config){
7158 this.el = Ext.get(el);
7159 this.el.unselectable();
7161 Ext.apply(this, config);
7166 * Fires when the mouse button is depressed.
7167 * @param {Ext.util.ClickRepeater} this
7168 * @param {Ext.EventObject} e
7173 * Fires on a specified interval during the time the element is pressed.
7174 * @param {Ext.util.ClickRepeater} this
7175 * @param {Ext.EventObject} e
7180 * Fires when the mouse key is released.
7181 * @param {Ext.util.ClickRepeater} this
7182 * @param {Ext.EventObject} e
7188 this.disabled = true;
7192 // allow inline handler
7194 this.on("click", this.handler, this.scope || this);
7197 Ext.util.ClickRepeater.superclass.constructor.call(this);
7202 preventDefault : true,
7203 stopDefault : false,
7207 * Enables the repeater and allows events to fire.
7211 this.el.on('mousedown', this.handleMouseDown, this);
7213 this.el.on('dblclick', this.handleDblClick, this);
7215 if(this.preventDefault || this.stopDefault){
7216 this.el.on('click', this.eventOptions, this);
7219 this.disabled = false;
7223 * Disables the repeater and stops events from firing.
7225 disable: function(/* private */ force){
7226 if(force || !this.disabled){
7227 clearTimeout(this.timer);
7228 if(this.pressClass){
7229 this.el.removeClass(this.pressClass);
7231 Ext.getDoc().un('mouseup', this.handleMouseUp, this);
7232 this.el.removeAllListeners();
7234 this.disabled = true;
7238 * Convenience function for setting disabled/enabled by boolean.
7239 * @param {Boolean} disabled
7241 setDisabled: function(disabled){
7242 this[disabled ? 'disable' : 'enable']();
7245 eventOptions: function(e){
7246 if(this.preventDefault){
7249 if(this.stopDefault){
7255 destroy : function() {
7257 Ext.destroy(this.el);
7258 this.purgeListeners();
7261 handleDblClick : function(e){
7262 clearTimeout(this.timer);
7265 this.fireEvent("mousedown", this, e);
7266 this.fireEvent("click", this, e);
7270 handleMouseDown : function(e){
7271 clearTimeout(this.timer);
7273 if(this.pressClass){
7274 this.el.addClass(this.pressClass);
7276 this.mousedownTime = new Date();
7278 Ext.getDoc().on("mouseup", this.handleMouseUp, this);
7279 this.el.on("mouseout", this.handleMouseOut, this);
7281 this.fireEvent("mousedown", this, e);
7282 this.fireEvent("click", this, e);
7284 // Do not honor delay or interval if acceleration wanted.
7285 if (this.accelerate) {
7288 this.timer = this.click.defer(this.delay || this.interval, this, [e]);
7292 click : function(e){
7293 this.fireEvent("click", this, e);
7294 this.timer = this.click.defer(this.accelerate ?
7295 this.easeOutExpo(this.mousedownTime.getElapsed(),
7299 this.interval, this, [e]);
7302 easeOutExpo : function (t, b, c, d) {
7303 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
7307 handleMouseOut : function(){
7308 clearTimeout(this.timer);
7309 if(this.pressClass){
7310 this.el.removeClass(this.pressClass);
7312 this.el.on("mouseover", this.handleMouseReturn, this);
7316 handleMouseReturn : function(){
7317 this.el.un("mouseover", this.handleMouseReturn, this);
7318 if(this.pressClass){
7319 this.el.addClass(this.pressClass);
7325 handleMouseUp : function(e){
7326 clearTimeout(this.timer);
7327 this.el.un("mouseover", this.handleMouseReturn, this);
7328 this.el.un("mouseout", this.handleMouseOut, this);
7329 Ext.getDoc().un("mouseup", this.handleMouseUp, this);
7330 this.el.removeClass(this.pressClass);
7331 this.fireEvent("mouseup", this, e);
7335 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
7336 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
7337 * way to implement custom navigation schemes for any UI component.</p>
7338 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
7339 * pageUp, pageDown, del, home, end. Usage:</p>
7341 var nav = new Ext.KeyNav("my-element", {
7342 "left" : function(e){
7343 this.moveLeft(e.ctrlKey);
7345 "right" : function(e){
7346 this.moveRight(e.ctrlKey);
7348 "enter" : function(e){
7355 * @param {Mixed} el The element to bind to
7356 * @param {Object} config The config
7358 Ext.KeyNav = function(el, config){
7359 this.el = Ext.get(el);
7360 Ext.apply(this, config);
7362 this.disabled = true;
7367 Ext.KeyNav.prototype = {
7369 * @cfg {Boolean} disabled
7370 * True to disable this KeyNav instance (defaults to false)
7374 * @cfg {String} defaultEventAction
7375 * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key. Valid values are
7376 * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
7377 * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
7379 defaultEventAction: "stopEvent",
7381 * @cfg {Boolean} forceKeyDown
7382 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
7383 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
7384 * handle keydown instead of keypress.
7386 forceKeyDown : false,
7389 relay : function(e){
7391 h = this.keyToHandler[k];
7393 if(this.doRelay(e, this[h], h) !== true){
7394 e[this.defaultEventAction]();
7400 doRelay : function(e, h, hname){
7401 return h.call(this.scope || this, e, hname);
7404 // possible handlers
7418 // quick lookup hash
7434 stopKeyUp: function(e) {
7437 if (k >= 37 && k <= 40) {
7438 // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
7439 // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
7445 * Destroy this KeyNav (this is the same as calling disable).
7447 destroy: function(){
7452 * Enable this KeyNav
7454 enable: function() {
7455 if (this.disabled) {
7456 if (Ext.isSafari2) {
7457 // call stopKeyUp() on "keyup" event
7458 this.el.on('keyup', this.stopKeyUp, this);
7461 this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
7462 this.disabled = false;
7467 * Disable this KeyNav
7469 disable: function() {
7470 if (!this.disabled) {
7471 if (Ext.isSafari2) {
7472 // remove "keyup" event handler
7473 this.el.un('keyup', this.stopKeyUp, this);
7476 this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
7477 this.disabled = true;
7482 * Convenience function for setting disabled/enabled by boolean.
7483 * @param {Boolean} disabled
7485 setDisabled : function(disabled){
7486 this[disabled ? "disable" : "enable"]();
7490 isKeydown: function(){
7491 return this.forceKeyDown || Ext.EventManager.useKeydown;
7496 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
7497 * The constructor accepts the same config object as defined by {@link #addBinding}.
7498 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
7499 * combination it will call the function with this signature (if the match is a multi-key
7500 * combination the callback will still be called only once): (String key, Ext.EventObject e)
7501 * A KeyMap can also handle a string representation of keys.<br />
7504 // map one key by key code
7505 var map = new Ext.KeyMap("my-element", {
7506 key: 13, // or Ext.EventObject.ENTER
7511 // map multiple keys to one action by string
7512 var map = new Ext.KeyMap("my-element", {
7518 // map multiple keys to multiple actions by strings and array of codes
7519 var map = new Ext.KeyMap("my-element", [
7522 fn: function(){ alert("Return was pressed"); }
7525 fn: function(){ alert('a, b or c was pressed'); }
7530 fn: function(){ alert('Control + shift + tab was pressed.'); }
7534 * <b>Note: A KeyMap starts enabled</b>
7536 * @param {Mixed} el The element to bind to
7537 * @param {Object} config The config (see {@link #addBinding})
7538 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
7540 Ext.KeyMap = function(el, config, eventName){
7541 this.el = Ext.get(el);
7542 this.eventName = eventName || "keydown";
7545 this.addBinding(config);
7550 Ext.KeyMap.prototype = {
7552 * True to stop the event from bubbling and prevent the default browser action if the
7553 * key was handled by the KeyMap (defaults to false)
7559 * Add a new binding to this KeyMap. The following config object properties are supported:
7561 Property Type Description
7562 ---------- --------------- ----------------------------------------------------------------------
7563 key String/Array A single keycode or an array of keycodes to handle
7564 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)
7565 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)
7566 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)
7567 handler Function The function to call when KeyMap finds the expected key combination
7568 fn Function Alias of handler (for backwards-compatibility)
7569 scope Object The scope of the callback function
7570 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)
7576 var map = new Ext.KeyMap(document, {
7577 key: Ext.EventObject.ENTER,
7582 //Add a new binding to the existing KeyMap later
7590 * @param {Object/Array} config A single KeyMap config or an array of configs
7592 addBinding : function(config){
7593 if(Ext.isArray(config)){
7594 Ext.each(config, function(c){
7599 var keyCode = config.key,
7600 fn = config.fn || config.handler,
7601 scope = config.scope;
7603 if (config.stopEvent) {
7604 this.stopEvent = config.stopEvent;
7607 if(typeof keyCode == "string"){
7609 var keyString = keyCode.toUpperCase();
7610 for(var j = 0, len = keyString.length; j < len; j++){
7611 ks.push(keyString.charCodeAt(j));
7615 var keyArray = Ext.isArray(keyCode);
7617 var handler = function(e){
7618 if(this.checkModifiers(config, e)){
7621 for(var i = 0, len = keyCode.length; i < len; i++){
7622 if(keyCode[i] == k){
7626 fn.call(scope || window, k, e);
7635 fn.call(scope || window, k, e);
7640 this.bindings.push(handler);
7644 checkModifiers: function(config, e){
7645 var val, key, keys = ['shift', 'ctrl', 'alt'];
7646 for (var i = 0, len = keys.length; i < len; ++i){
7649 if(!(val === undefined || (val === e[key + 'Key']))){
7657 * Shorthand for adding a single key listener
7658 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
7659 * following options:
7660 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
7661 * @param {Function} fn The function to call
7662 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
7664 on : function(key, fn, scope){
7665 var keyCode, shift, ctrl, alt;
7666 if(typeof key == "object" && !Ext.isArray(key)){
7685 handleKeyDown : function(e){
7686 if(this.enabled){ //just in case
7687 var b = this.bindings;
7688 for(var i = 0, len = b.length; i < len; i++){
7695 * Returns true if this KeyMap is enabled
7698 isEnabled : function(){
7699 return this.enabled;
7703 * Enables this KeyMap
7707 this.el.on(this.eventName, this.handleKeyDown, this);
7708 this.enabled = true;
7713 * Disable this KeyMap
7715 disable: function(){
7717 this.el.removeListener(this.eventName, this.handleKeyDown, this);
7718 this.enabled = false;
7723 * Convenience function for setting disabled/enabled by boolean.
7724 * @param {Boolean} disabled
7726 setDisabled : function(disabled){
7727 this[disabled ? "disable" : "enable"]();
7730 * @class Ext.util.TextMetrics
7731 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
7732 * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
7733 * should not contain any HTML, otherwise it may not be measured correctly.
7736 Ext.util.TextMetrics = function(){
7740 * Measures the size of the specified text
7741 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
7742 * that can affect the size of the rendered text
7743 * @param {String} text The text to measure
7744 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
7745 * in order to accurately measure the text height
7746 * @return {Object} An object containing the text's size {width: (width), height: (height)}
7748 measure : function(el, text, fixedWidth){
7750 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
7753 shared.setFixedWidth(fixedWidth || 'auto');
7754 return shared.getSize(text);
7758 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
7759 * the overhead of multiple calls to initialize the style properties on each measurement.
7760 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
7761 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
7762 * in order to accurately measure the text height
7763 * @return {Ext.util.TextMetrics.Instance} instance The new instance
7765 createInstance : function(el, fixedWidth){
7766 return Ext.util.TextMetrics.Instance(el, fixedWidth);
7771 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
7772 var ml = new Ext.Element(document.createElement('div'));
7773 document.body.appendChild(ml.dom);
7774 ml.position('absolute');
7775 ml.setLeftTop(-1000, -1000);
7779 ml.setWidth(fixedWidth);
7784 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7785 * Returns the size of the specified text based on the internal element's style and width properties
7786 * @param {String} text The text to measure
7787 * @return {Object} An object containing the text's size {width: (width), height: (height)}
7789 getSize : function(text){
7791 var s = ml.getSize();
7797 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7798 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
7799 * that can affect the size of the rendered text
7800 * @param {String/HTMLElement} el The element, dom node or id
7802 bind : function(el){
7804 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
7809 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7810 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
7811 * to set a fixed width in order to accurately measure the text height.
7812 * @param {Number} width The width to set on the element
7814 setFixedWidth : function(width){
7819 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7820 * Returns the measured width of the specified text
7821 * @param {String} text The text to measure
7822 * @return {Number} width The width in pixels
7824 getWidth : function(text){
7825 ml.dom.style.width = 'auto';
7826 return this.getSize(text).width;
7830 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7831 * Returns the measured height of the specified text. For multiline text, be sure to call
7832 * {@link #setFixedWidth} if necessary.
7833 * @param {String} text The text to measure
7834 * @return {Number} height The height in pixels
7836 getHeight : function(text){
7837 return this.getSize(text).height;
7841 instance.bind(bindTo);
7846 Ext.Element.addMethods({
7848 * Returns the width in pixels of the passed text, or the width of the text in this Element.
7849 * @param {String} text The text to measure. Defaults to the innerHTML of the element.
7850 * @param {Number} min (Optional) The minumum value to return.
7851 * @param {Number} max (Optional) The maximum value to return.
7852 * @return {Number} The text width in pixels.
7853 * @member Ext.Element getTextWidth
7855 getTextWidth : function(text, min, max){
7856 return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
7860 * @class Ext.util.Cookies
7861 * Utility class for managing and interacting with cookies.
7864 Ext.util.Cookies = {
7866 * Create a cookie with the specified name and value. Additional settings
7867 * for the cookie may be optionally specified (for example: expiration,
7868 * access restriction, SSL).
7869 * @param {String} name The name of the cookie to set.
7870 * @param {Mixed} value The value to set for the cookie.
7871 * @param {Object} expires (Optional) Specify an expiration date the
7872 * cookie is to persist until. Note that the specified Date object will
7873 * be converted to Greenwich Mean Time (GMT).
7874 * @param {String} path (Optional) Setting a path on the cookie restricts
7875 * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>).
7876 * @param {String} domain (Optional) Setting a domain restricts access to
7877 * pages on a given domain (typically used to allow cookie access across
7878 * subdomains). For example, "extjs.com" will create a cookie that can be
7879 * accessed from any subdomain of extjs.com, including www.extjs.com,
7880 * support.extjs.com, etc.
7881 * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
7882 * should only be accessible via SSL on a page using the HTTPS protocol.
7883 * Defaults to <tt>false</tt>. Note that this will only work if the page
7884 * calling this code uses the HTTPS protocol, otherwise the cookie will be
7885 * created with default options.
7887 set : function(name, value){
7888 var argv = arguments;
7889 var argc = arguments.length;
7890 var expires = (argc > 2) ? argv[2] : null;
7891 var path = (argc > 3) ? argv[3] : '/';
7892 var domain = (argc > 4) ? argv[4] : null;
7893 var secure = (argc > 5) ? argv[5] : false;
7894 document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
7898 * Retrieves cookies that are accessible by the current page. If a cookie
7899 * does not exist, <code>get()</code> returns <tt>null</tt>. The following
7900 * example retrieves the cookie called "valid" and stores the String value
7901 * in the variable <tt>validStatus</tt>.
7903 * var validStatus = Ext.util.Cookies.get("valid");
7905 * @param {String} name The name of the cookie to get
7906 * @return {Mixed} Returns the cookie value for the specified name;
7907 * null if the cookie name does not exist.
7909 get : function(name){
7910 var arg = name + "=";
7911 var alen = arg.length;
7912 var clen = document.cookie.length;
7917 if(document.cookie.substring(i, j) == arg){
7918 return Ext.util.Cookies.getCookieVal(j);
7920 i = document.cookie.indexOf(" ", i) + 1;
7929 * Removes a cookie with the provided name from the browser
7930 * if found by setting its expiration date to sometime in the past.
7931 * @param {String} name The name of the cookie to remove
7933 clear : function(name){
7934 if(Ext.util.Cookies.get(name)){
7935 document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
7941 getCookieVal : function(offset){
7942 var endstr = document.cookie.indexOf(";", offset);
7944 endstr = document.cookie.length;
7946 return unescape(document.cookie.substring(offset, endstr));
7949 * Framework-wide error-handler. Developers can override this method to provide
7950 * custom exception-handling. Framework errors will often extend from the base
7952 * @param {Object/Error} e The thrown exception object.
7954 Ext.handleError = function(e) {
7961 * <p>A base error class. Future implementations are intended to provide more
7962 * robust error handling throughout the framework (<b>in the debug build only</b>)
7963 * to check for common errors and problems. The messages issued by this class
7964 * will aid error checking. Error checks will be automatically removed in the
7965 * production build so that performance is not negatively impacted.</p>
7966 * <p>Some sample messages currently implemented:</p><pre>
7967 "DataProxy attempted to execute an API-action but found an undefined
7968 url / function. Please review your Proxy url/api-configuration."
7970 "Could not locate your "root" property in your server response.
7971 Please review your JsonReader config to ensure the config-property
7972 "root" matches the property your server-response. See the JsonReader
7973 docs for additional assistance."
7975 * <p>An example of the code used for generating error messages:</p><pre><code>
7984 function generateError(data) {
7985 throw new Ext.Error('foo-error', data);
7988 * @param {String} message
7990 Ext.Error = function(message) {
7991 // Try to read the message from Ext.Error.lang
7992 this.message = (this.lang[message]) ? this.lang[message] : message;
7995 Ext.Error.prototype = new Error();
7996 Ext.apply(Ext.Error.prototype, {
7997 // protected. Extensions place their error-strings here.
8005 getName : function() {
8012 getMessage : function() {
8013 return this.message;
8019 toJson : function() {
8020 return Ext.encode(this);