3 * Copyright(c) 2006-2010 Ext JS, Inc.
5 * http://www.extjs.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,
4705 for (var i = 0; i < format.length; ++i) {
4706 ch = format.charAt(i);
4707 if (!special && ch == "\\") {
4709 } else if (special) {
4711 regex.push(String.escape(ch));
4713 var obj = $f(ch, currentGroup);
4714 currentGroup += obj.g;
4716 if (obj.g && obj.c) {
4722 Date.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
4723 Date.parseFunctions[format] = new Function("input", "strict", xf(code, regexNum, calc.join('')));
4731 * g = {Number} calculation group (0 or 1. only group 1 contributes to date calculations.)
4732 * c = {String} calculation method (required for group 1. null for group 0. {0} = currentGroup - position in regex result array)
4733 * s = {String} regex pattern. all matches are stored in results[], and are accessible by the calculation mapped to 'c'
4737 c:"d = parseInt(results[{0}], 10);\n",
4738 s:"(\\d{2})" // day of month with leading zeroes (01 - 31)
4742 c:"d = parseInt(results[{0}], 10);\n",
4743 s:"(\\d{1,2})" // day of month without leading zeroes (1 - 31)
4746 for (var a = [], i = 0; i < 7; a.push(Date.getShortDayName(i)), ++i); // get localised short day names
4750 s:"(?:" + a.join("|") +")"
4757 s:"(?:" + Date.dayNames.join("|") + ")"
4763 s:"[1-7]" // ISO-8601 day number (1 (monday) - 7 (sunday))
4773 s:"[0-6]" // javascript day number (0 (sunday) - 6 (saturday))
4777 c:"z = parseInt(results[{0}], 10);\n",
4778 s:"(\\d{1,3})" // day of the year (0 - 364 (365 in leap years))
4783 s:"(?:\\d{2})" // ISO-8601 week number (with leading zero)
4788 c:"m = parseInt(Date.getMonthNumber(results[{0}]), 10);\n", // get localised month number
4789 s:"(" + Date.monthNames.join("|") + ")"
4793 for (var a = [], i = 0; i < 12; a.push(Date.getShortMonthName(i)), ++i); // get localised short month names
4794 return Ext.applyIf({
4795 s:"(" + a.join("|") + ")"
4800 c:"m = parseInt(results[{0}], 10) - 1;\n",
4801 s:"(\\d{2})" // month number with leading zeros (01 - 12)
4805 c:"m = parseInt(results[{0}], 10) - 1;\n",
4806 s:"(\\d{1,2})" // month number without leading zeros (1 - 12)
4811 s:"(?:\\d{2})" // no. of days in the month (28 - 31)
4823 c:"y = parseInt(results[{0}], 10);\n",
4824 s:"(\\d{4})" // 4-digit year
4828 c:"var ty = parseInt(results[{0}], 10);\n"
4829 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", // 2-digit year
4833 * In the am/pm parsing routines, we allow both upper and lower case
4834 * even though it doesn't exactly match the spec. It gives much more flexibility
4835 * in being able to specify case insensitive regexes.
4839 c:"if (/(am)/i.test(results[{0}])) {\n"
4840 + "if (!h || h == 12) { h = 0; }\n"
4841 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4846 c:"if (/(am)/i.test(results[{0}])) {\n"
4847 + "if (!h || h == 12) { h = 0; }\n"
4848 + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
4856 c:"h = parseInt(results[{0}], 10);\n",
4857 s:"(\\d{1,2})" // 24-hr format of an hour without leading zeroes (0 - 23)
4864 c:"h = parseInt(results[{0}], 10);\n",
4865 s:"(\\d{2})" // 24-hr format of an hour with leading zeroes (00 - 23)
4869 c:"i = parseInt(results[{0}], 10);\n",
4870 s:"(\\d{2})" // minutes with leading zeros (00 - 59)
4874 c:"s = parseInt(results[{0}], 10);\n",
4875 s:"(\\d{2})" // seconds with leading zeros (00 - 59)
4879 c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
4880 s:"(\\d+)" // decimal fraction of a second (minimum = 1 digit, maximum = unlimited)
4885 "o = results[{0}];",
4886 "var sn = o.substring(0,1),", // get + / - sign
4887 "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4888 "mn = o.substring(3,5) % 60;", // get minutes
4889 "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
4891 s: "([+\-]\\d{4})" // GMT offset in hrs and mins
4896 "o = results[{0}];",
4897 "var sn = o.substring(0,1),", // get + / - sign
4898 "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", // get hours (performs minutes-to-hour conversion also, just in case)
4899 "mn = o.substring(4,6) % 60;", // get minutes
4900 "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
4902 s: "([+\-]\\d{2}:\\d{2})" // GMT offset in hrs and mins (with colon separator)
4907 s:"[A-Z]{1,4}" // timezone abbrev. may be between 1 - 4 chars
4911 c:"zz = results[{0}] * 1;\n" // -43200 <= UTC offset <= 50400
4912 + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
4913 s:"([+\-]?\\d{1,5})" // leading '+' sign is optional for UTC offset
4919 $f("m", 2), // month
4922 $f("i", 5), // minute
4923 $f("s", 6), // second
4924 {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)
4925 {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
4926 "if(results[8]) {", // timezone specified
4927 "if(results[8] == 'Z'){",
4929 "}else if (results[8].indexOf(':') > -1){",
4930 $f("P", 8).c, // timezone offset with colon separator
4932 $f("O", 8).c, // timezone offset without colon separator
4938 for (var i = 0, l = arr.length; i < l; ++i) {
4939 calc.push(arr[i].c);
4946 arr[0].s, // year (required)
4947 "(?:", "-", arr[1].s, // month (optional)
4948 "(?:", "-", arr[2].s, // day (optional)
4950 "(?:T| )?", // time delimiter -- either a "T" or a single blank space
4951 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
4952 "(?::", arr[5].s, ")?", // seconds (optional)
4953 "(?:(?:\\.|,)(\\d+))?", // decimal fraction of a second (e.g. ",12345" or ".98765") (optional)
4954 "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", // "Z" (UTC) or "-0530" (UTC offset without colon delimiter) or "+08:00" (UTC offset with colon delimiter) (optional)
4963 c:"u = parseInt(results[{0}], 10);\n",
4964 s:"(-?\\d+)" // leading minus sign indicates seconds before UNIX epoch
4971 Ext.apply(Date.prototype, {
4973 dateFormat : function(format) {
4974 if (Date.formatFunctions[format] == null) {
4975 Date.createFormat(format);
4977 return Date.formatFunctions[format].call(this);
4981 * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
4983 * Note: The date string returned by the javascript Date object's toString() method varies
4984 * between browsers (e.g. FF vs IE) and system region settings (e.g. IE in Asia vs IE in America).
4985 * For a given date string e.g. "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)",
4986 * getTimezone() first tries to get the timezone abbreviation from between a pair of parentheses
4987 * (which may or may not be present), failing which it proceeds to get the timezone abbreviation
4988 * from the GMT offset portion of the date string.
4989 * @return {String} The abbreviated timezone name (e.g. 'CST', 'PDT', 'EDT', 'MPST' ...).
4991 getTimezone : function() {
4992 // the following list shows the differences between date strings from different browsers on a WinXP SP2 machine from an Asian locale:
4994 // Opera : "Thu, 25 Oct 2007 22:53:45 GMT+0800" -- shortest (weirdest) date string of the lot
4995 // 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)
4996 // FF : "Thu Oct 25 2007 22:55:35 GMT+0800 (Malay Peninsula Standard Time)" -- value in parentheses always gives the correct timezone
4997 // IE : "Thu Oct 25 22:54:35 UTC+0800 2007" -- (Asian system setting) look for 3-4 letter timezone abbrev
4998 // IE : "Thu Oct 25 17:06:37 PDT 2007" -- (American system setting) look for 3-4 letter timezone abbrev
5000 // this crazy regex attempts to guess the correct timezone abbreviation despite these differences.
5001 // step 1: (?:\((.*)\) -- find timezone in parentheses
5002 // 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
5003 // step 3: remove all non uppercase characters found in step 1 and 2
5004 return this.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
5008 * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
5009 * @param {Boolean} colon (optional) true to separate the hours and minutes with a colon (defaults to false).
5010 * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600').
5012 getGMTOffset : function(colon) {
5013 return (this.getTimezoneOffset() > 0 ? "-" : "+")
5014 + String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset()) / 60), 2, "0")
5015 + (colon ? ":" : "")
5016 + String.leftPad(Math.abs(this.getTimezoneOffset() % 60), 2, "0");
5020 * Get the numeric day number of the year, adjusted for leap year.
5021 * @return {Number} 0 to 364 (365 in leap years).
5023 getDayOfYear: function() {
5026 m = this.getMonth(),
5029 for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
5030 num += d.getDaysInMonth();
5032 return num + this.getDate() - 1;
5036 * Get the numeric ISO-8601 week number of the year.
5037 * (equivalent to the format specifier 'W', but without a leading zero).
5038 * @return {Number} 1 to 53
5040 getWeekOfYear : function() {
5041 // adapted from http://www.merlyn.demon.co.uk/weekcalc.htm
5042 var ms1d = 864e5, // milliseconds in a day
5043 ms7d = 7 * ms1d; // milliseconds in a week
5045 return function() { // return a closure so constants get calculated only once
5046 var DC3 = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate() + 3) / ms1d, // an Absolute Day Number
5047 AWN = Math.floor(DC3 / 7), // an Absolute Week Number
5048 Wyr = new Date(AWN * ms7d).getUTCFullYear();
5050 return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
5055 * Checks if the current date falls within a leap year.
5056 * @return {Boolean} True if the current date falls within a leap year, false otherwise.
5058 isLeapYear : function() {
5059 var year = this.getFullYear();
5060 return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
5064 * Get the first day of the current month, adjusted for leap year. The returned value
5065 * is the numeric day index within the week (0-6) which can be used in conjunction with
5066 * the {@link #monthNames} array to retrieve the textual day name.
5069 var dt = new Date('1/10/2007');
5070 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
5072 * @return {Number} The day number (0-6).
5074 getFirstDayOfMonth : function() {
5075 var day = (this.getDay() - (this.getDate() - 1)) % 7;
5076 return (day < 0) ? (day + 7) : day;
5080 * Get the last day of the current month, adjusted for leap year. The returned value
5081 * is the numeric day index within the week (0-6) which can be used in conjunction with
5082 * the {@link #monthNames} array to retrieve the textual day name.
5085 var dt = new Date('1/10/2007');
5086 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
5088 * @return {Number} The day number (0-6).
5090 getLastDayOfMonth : function() {
5091 return this.getLastDateOfMonth().getDay();
5096 * Get the date of the first day of the month in which this date resides.
5099 getFirstDateOfMonth : function() {
5100 return new Date(this.getFullYear(), this.getMonth(), 1);
5104 * Get the date of the last day of the month in which this date resides.
5107 getLastDateOfMonth : function() {
5108 return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
5112 * Get the number of days in the current month, adjusted for leap year.
5113 * @return {Number} The number of days in the month.
5115 getDaysInMonth: function() {
5116 var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
5118 return function() { // return a closure for efficiency
5119 var m = this.getMonth();
5121 return m == 1 && this.isLeapYear() ? 29 : daysInMonth[m];
5126 * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
5127 * @return {String} 'st, 'nd', 'rd' or 'th'.
5129 getSuffix : function() {
5130 switch (this.getDate()) {
5147 * Creates and returns a new Date instance with the exact same date value as the called instance.
5148 * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
5149 * variable will also be changed. When the intention is to create a new variable that will not
5150 * modify the original instance, you should create a clone.
5152 * Example of correctly cloning a date:
5155 var orig = new Date('10/1/2006');
5158 document.write(orig); //returns 'Thu Oct 05 2006'!
5161 var orig = new Date('10/1/2006');
5162 var copy = orig.clone();
5164 document.write(orig); //returns 'Thu Oct 01 2006'
5166 * @return {Date} The new Date instance.
5168 clone : function() {
5169 return new Date(this.getTime());
5173 * Checks if the current date is affected by Daylight Saving Time (DST).
5174 * @return {Boolean} True if the current date is affected by DST.
5176 isDST : function() {
5177 // adapted from http://extjs.com/forum/showthread.php?p=247172#post247172
5178 // courtesy of @geoffrey.mcgill
5179 return new Date(this.getFullYear(), 0, 1).getTimezoneOffset() != this.getTimezoneOffset();
5183 * Attempts to clear all time information from this Date by setting the time to midnight of the same day,
5184 * automatically adjusting for Daylight Saving Time (DST) where applicable.
5185 * (note: DST timezone information for the browser's host operating system is assumed to be up-to-date)
5186 * @param {Boolean} clone true to create a clone of this date, clear the time and return it (defaults to false).
5187 * @return {Date} this or the clone.
5189 clearTime : function(clone) {
5191 return this.clone().clearTime();
5194 // get current date before clearing time
5195 var d = this.getDate();
5201 this.setMilliseconds(0);
5203 if (this.getDate() != d) { // account for DST (i.e. day of month changed when setting hour = 0)
5204 // note: DST adjustments are assumed to occur in multiples of 1 hour (this is almost always the case)
5205 // refer to http://www.timeanddate.com/time/aboutdst.html for the (rare) exceptions to this rule
5207 // increment hour until cloned date == current date
5208 for (var hr = 1, c = this.add(Date.HOUR, hr); c.getDate() != d; hr++, c = this.add(Date.HOUR, hr));
5211 this.setHours(c.getHours());
5218 * Provides a convenient method for performing basic date arithmetic. This method
5219 * does not modify the Date instance being called - it creates and returns
5220 * a new Date instance containing the resulting date value.
5225 var dt = new Date('10/29/2006').add(Date.DAY, 5);
5226 document.write(dt); //returns 'Fri Nov 03 2006 00:00:00'
5228 // Negative values will be subtracted:
5229 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
5230 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
5232 // You can even chain several calls together in one line:
5233 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
5234 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
5237 * @param {String} interval A valid date interval enum value.
5238 * @param {Number} value The amount to add to the current date.
5239 * @return {Date} The new Date instance.
5241 add : function(interval, value) {
5242 var d = this.clone();
5243 if (!interval || value === 0) return d;
5245 switch(interval.toLowerCase()) {
5247 d.setMilliseconds(this.getMilliseconds() + value);
5250 d.setSeconds(this.getSeconds() + value);
5253 d.setMinutes(this.getMinutes() + value);
5256 d.setHours(this.getHours() + value);
5259 d.setDate(this.getDate() + value);
5262 var day = this.getDate();
5264 day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
5267 d.setMonth(this.getMonth() + value);
5270 d.setFullYear(this.getFullYear() + value);
5277 * Checks if this date falls on or between the given start and end dates.
5278 * @param {Date} start Start date
5279 * @param {Date} end End date
5280 * @return {Boolean} true if this date falls on or between the given start and end dates.
5282 between : function(start, end) {
5283 var t = this.getTime();
5284 return start.getTime() <= t && t <= end.getTime();
5290 * Formats a date given the supplied format string.
5291 * @param {String} format The format string.
5292 * @return {String} The formatted date.
5295 Date.prototype.format = Date.prototype.dateFormat;
5299 if (Ext.isSafari && (navigator.userAgent.match(/WebKit\/(\d+)/)[1] || NaN) < 420) {
5300 Ext.apply(Date.prototype, {
5301 _xMonth : Date.prototype.setMonth,
5302 _xDate : Date.prototype.setDate,
5304 // Bug in Safari 1.3, 2.0 (WebKit build < 420)
5305 // Date.setMonth does not work consistently if iMonth is not 0-11
5306 setMonth : function(num) {
5308 var n = Math.ceil(-num),
5309 back_year = Math.ceil(n / 12),
5310 month = (n % 12) ? 12 - n % 12 : 0;
5312 this.setFullYear(this.getFullYear() - back_year);
5314 return this._xMonth(month);
5316 return this._xMonth(num);
5320 // Bug in setDate() method (resolved in WebKit build 419.3, so to be safe we target Webkit builds < 420)
5321 // The parameter for Date.setDate() is converted to a signed byte integer in Safari
5322 // http://brianary.blogspot.com/2006/03/safari-date-bug.html
5323 setDate : function(d) {
5324 // use setTime() to workaround setDate() bug
5325 // subtract current day of month in milliseconds, then add desired day of month in milliseconds
5326 return this.setTime(this.getTime() - (this.getDate() - d) * 864e5);
5333 /* Some basic Date tests... (requires Firebug)
5335 Date.parseDate('', 'c'); // call Date.parseDate() once to force computation of regex string so we can console.log() it
5336 console.log('Insane Regex for "c" format: %o', Date.parseCodes.c.s); // view the insane regex for the "c" format specifier
5339 console.group('Standard Date.parseDate() Tests');
5340 console.log('Date.parseDate("2009-01-05T11:38:56", "c") = %o', Date.parseDate("2009-01-05T11:38:56", "c")); // assumes browser's timezone setting
5341 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
5342 console.log('Date.parseDate("2009-03-03T13:36:54,101000Z", "c") = %o', Date.parseDate("2009-03-03T13:36:54,101000Z", "c")); // UTC
5343 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
5344 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
5347 // ISO-8601 format as specified in http://www.w3.org/TR/NOTE-datetime
5348 // -- accepts ALL 6 levels of date-time granularity
5349 console.group('ISO-8601 Granularity Test (see http://www.w3.org/TR/NOTE-datetime)');
5350 console.log('Date.parseDate("1997", "c") = %o', Date.parseDate("1997", "c")); // YYYY (e.g. 1997)
5351 console.log('Date.parseDate("1997-07", "c") = %o', Date.parseDate("1997-07", "c")); // YYYY-MM (e.g. 1997-07)
5352 console.log('Date.parseDate("1997-07-16", "c") = %o', Date.parseDate("1997-07-16", "c")); // YYYY-MM-DD (e.g. 1997-07-16)
5353 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)
5354 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)
5355 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)
5356 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)
5357 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
5362 * @class Ext.util.MixedCollection
5363 * @extends Ext.util.Observable
5364 * A Collection class that maintains both numeric indexes and keys and exposes events.
5366 * @param {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
5367 * function should add function references to the collection. Defaults to
5369 * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
5370 * and return the key value for that item. This is used when available to look up the key on items that
5371 * were passed without an explicit key parameter to a MixedCollection method. Passing this parameter is
5372 * equivalent to providing an implementation for the {@link #getKey} method.
5374 Ext.util.MixedCollection = function(allowFunctions, keyFn){
5382 * Fires when the collection is cleared.
5387 * Fires when an item is added to the collection.
5388 * @param {Number} index The index at which the item was added.
5389 * @param {Object} o The item added.
5390 * @param {String} key The key associated with the added item.
5395 * Fires when an item is replaced in the collection.
5396 * @param {String} key he key associated with the new added.
5397 * @param {Object} old The item being replaced.
5398 * @param {Object} new The new item.
5403 * Fires when an item is removed from the collection.
5404 * @param {Object} o The item being removed.
5405 * @param {String} key (optional) The key associated with the removed item.
5410 this.allowFunctions = allowFunctions === true;
5412 this.getKey = keyFn;
5414 Ext.util.MixedCollection.superclass.constructor.call(this);
5417 Ext.extend(Ext.util.MixedCollection, Ext.util.Observable, {
5420 * @cfg {Boolean} allowFunctions Specify <tt>true</tt> if the {@link #addAll}
5421 * function should add function references to the collection. Defaults to
5424 allowFunctions : false,
5427 * Adds an item to the collection. Fires the {@link #add} event when complete.
5428 * @param {String} key <p>The key to associate with the item, or the new item.</p>
5429 * <p>If a {@link #getKey} implementation was specified for this MixedCollection,
5430 * or if the key of the stored items is in a property called <tt><b>id</b></tt>,
5431 * the MixedCollection will be able to <i>derive</i> the key for the new item.
5432 * In this case just pass the new item in this parameter.</p>
5433 * @param {Object} o The item to add.
5434 * @return {Object} The item added.
5436 add : function(key, o){
5437 if(arguments.length == 1){
5439 key = this.getKey(o);
5441 if(typeof key != 'undefined' && key !== null){
5442 var old = this.map[key];
5443 if(typeof old != 'undefined'){
5444 return this.replace(key, o);
5450 this.keys.push(key);
5451 this.fireEvent('add', this.length-1, o, key);
5456 * MixedCollection has a generic way to fetch keys if you implement getKey. The default implementation
5457 * simply returns <b><code>item.id</code></b> but you can provide your own implementation
5458 * to return a different value as in the following examples:<pre><code>
5460 var mc = new Ext.util.MixedCollection();
5461 mc.add(someEl.dom.id, someEl);
5462 mc.add(otherEl.dom.id, otherEl);
5466 var mc = new Ext.util.MixedCollection();
5467 mc.getKey = function(el){
5473 // or via the constructor
5474 var mc = new Ext.util.MixedCollection(false, function(el){
5480 * @param {Object} item The item for which to find the key.
5481 * @return {Object} The key for the passed item.
5483 getKey : function(o){
5488 * Replaces an item in the collection. Fires the {@link #replace} event when complete.
5489 * @param {String} key <p>The key associated with the item to replace, or the replacement item.</p>
5490 * <p>If you supplied a {@link #getKey} implementation for this MixedCollection, or if the key
5491 * of your stored items is in a property called <tt><b>id</b></tt>, then the MixedCollection
5492 * will be able to <i>derive</i> the key of the replacement item. If you want to replace an item
5493 * with one having the same key value, then just pass the replacement item in this parameter.</p>
5494 * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate
5496 * @return {Object} The new item.
5498 replace : function(key, o){
5499 if(arguments.length == 1){
5501 key = this.getKey(o);
5503 var old = this.map[key];
5504 if(typeof key == 'undefined' || key === null || typeof old == 'undefined'){
5505 return this.add(key, o);
5507 var index = this.indexOfKey(key);
5508 this.items[index] = o;
5510 this.fireEvent('replace', key, old, o);
5515 * Adds all elements of an Array or an Object to the collection.
5516 * @param {Object/Array} objs An Object containing properties which will be added
5517 * to the collection, or an Array of values, each of which are added to the collection.
5518 * Functions references will be added to the collection if <code>{@link #allowFunctions}</code>
5519 * has been set to <tt>true</tt>.
5521 addAll : function(objs){
5522 if(arguments.length > 1 || Ext.isArray(objs)){
5523 var args = arguments.length > 1 ? arguments : objs;
5524 for(var i = 0, len = args.length; i < len; i++){
5528 for(var key in objs){
5529 if(this.allowFunctions || typeof objs[key] != 'function'){
5530 this.add(key, objs[key]);
5537 * Executes the specified function once for every item in the collection, passing the following arguments:
5538 * <div class="mdetail-params"><ul>
5539 * <li><b>item</b> : Mixed<p class="sub-desc">The collection item</p></li>
5540 * <li><b>index</b> : Number<p class="sub-desc">The item's index</p></li>
5541 * <li><b>length</b> : Number<p class="sub-desc">The total number of items in the collection</p></li>
5543 * The function should return a boolean value. Returning false from the function will stop the iteration.
5544 * @param {Function} fn The function to execute for each item.
5545 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current item in the iteration.
5547 each : function(fn, scope){
5548 var items = [].concat(this.items); // each safe for removal
5549 for(var i = 0, len = items.length; i < len; i++){
5550 if(fn.call(scope || items[i], items[i], i, len) === false){
5557 * Executes the specified function once for every key in the collection, passing each
5558 * key, and its associated item as the first two parameters.
5559 * @param {Function} fn The function to execute for each item.
5560 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
5562 eachKey : function(fn, scope){
5563 for(var i = 0, len = this.keys.length; i < len; i++){
5564 fn.call(scope || window, this.keys[i], this.items[i], i, len);
5569 * Returns the first item in the collection which elicits a true return value from the
5570 * passed selection function.
5571 * @param {Function} fn The selection function to execute for each item.
5572 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
5573 * @return {Object} The first item in the collection which returned true from the selection function.
5575 find : function(fn, scope){
5576 for(var i = 0, len = this.items.length; i < len; i++){
5577 if(fn.call(scope || window, this.items[i], this.keys[i])){
5578 return this.items[i];
5585 * Inserts an item at the specified index in the collection. Fires the {@link #add} event when complete.
5586 * @param {Number} index The index to insert the item at.
5587 * @param {String} key The key to associate with the new item, or the item itself.
5588 * @param {Object} o (optional) If the second parameter was a key, the new item.
5589 * @return {Object} The item inserted.
5591 insert : function(index, key, o){
5592 if(arguments.length == 2){
5594 key = this.getKey(o);
5596 if(this.containsKey(key)){
5597 this.suspendEvents();
5598 this.removeKey(key);
5599 this.resumeEvents();
5601 if(index >= this.length){
5602 return this.add(key, o);
5605 this.items.splice(index, 0, o);
5606 if(typeof key != 'undefined' && key !== null){
5609 this.keys.splice(index, 0, key);
5610 this.fireEvent('add', index, o, key);
5615 * Remove an item from the collection.
5616 * @param {Object} o The item to remove.
5617 * @return {Object} The item removed or false if no item was removed.
5619 remove : function(o){
5620 return this.removeAt(this.indexOf(o));
5624 * Remove an item from a specified index in the collection. Fires the {@link #remove} event when complete.
5625 * @param {Number} index The index within the collection of the item to remove.
5626 * @return {Object} The item removed or false if no item was removed.
5628 removeAt : function(index){
5629 if(index < this.length && index >= 0){
5631 var o = this.items[index];
5632 this.items.splice(index, 1);
5633 var key = this.keys[index];
5634 if(typeof key != 'undefined'){
5635 delete this.map[key];
5637 this.keys.splice(index, 1);
5638 this.fireEvent('remove', o, key);
5645 * Removed an item associated with the passed key fom the collection.
5646 * @param {String} key The key of the item to remove.
5647 * @return {Object} The item removed or false if no item was removed.
5649 removeKey : function(key){
5650 return this.removeAt(this.indexOfKey(key));
5654 * Returns the number of items in the collection.
5655 * @return {Number} the number of items in the collection.
5657 getCount : function(){
5662 * Returns index within the collection of the passed Object.
5663 * @param {Object} o The item to find the index of.
5664 * @return {Number} index of the item. Returns -1 if not found.
5666 indexOf : function(o){
5667 return this.items.indexOf(o);
5671 * Returns index within the collection of the passed key.
5672 * @param {String} key The key to find the index of.
5673 * @return {Number} index of the key.
5675 indexOfKey : function(key){
5676 return this.keys.indexOf(key);
5680 * Returns the item associated with the passed key OR index.
5681 * Key has priority over index. This is the equivalent
5682 * of calling {@link #key} first, then if nothing matched calling {@link #itemAt}.
5683 * @param {String/Number} key The key or index of the item.
5684 * @return {Object} If the item is found, returns the item. If the item was not found, returns <tt>undefined</tt>.
5685 * If an item was found, but is a Class, returns <tt>null</tt>.
5687 item : function(key){
5688 var mk = this.map[key],
5689 item = mk !== undefined ? mk : (typeof key == 'number') ? this.items[key] : undefined;
5690 return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
5694 * Returns the item at the specified index.
5695 * @param {Number} index The index of the item.
5696 * @return {Object} The item at the specified index.
5698 itemAt : function(index){
5699 return this.items[index];
5703 * Returns the item associated with the passed key.
5704 * @param {String/Number} key The key of the item.
5705 * @return {Object} The item associated with the passed key.
5707 key : function(key){
5708 return this.map[key];
5712 * Returns true if the collection contains the passed Object as an item.
5713 * @param {Object} o The Object to look for in the collection.
5714 * @return {Boolean} True if the collection contains the Object as an item.
5716 contains : function(o){
5717 return this.indexOf(o) != -1;
5721 * Returns true if the collection contains the passed Object as a key.
5722 * @param {String} key The key to look for in the collection.
5723 * @return {Boolean} True if the collection contains the Object as a key.
5725 containsKey : function(key){
5726 return typeof this.map[key] != 'undefined';
5730 * Removes all items from the collection. Fires the {@link #clear} event when complete.
5737 this.fireEvent('clear');
5741 * Returns the first item in the collection.
5742 * @return {Object} the first item in the collection..
5745 return this.items[0];
5749 * Returns the last item in the collection.
5750 * @return {Object} the last item in the collection..
5753 return this.items[this.length-1];
5758 * Performs the actual sorting based on a direction and a sorting function. Internally,
5759 * this creates a temporary array of all items in the MixedCollection, sorts it and then writes
5760 * the sorted array data back into this.items and this.keys
5761 * @param {String} property Property to sort by ('key', 'value', or 'index')
5762 * @param {String} dir (optional) Direction to sort 'ASC' or 'DESC'. Defaults to 'ASC'.
5763 * @param {Function} fn (optional) Comparison function that defines the sort order.
5764 * Defaults to sorting by numeric value.
5766 _sort : function(property, dir, fn){
5768 dsc = String(dir).toUpperCase() == 'DESC' ? -1 : 1,
5770 //this is a temporary array used to apply the sorting function
5775 //default to a simple sorter function if one is not provided
5776 fn = fn || function(a, b) {
5780 //copy all the items into a temporary array, which we will sort
5781 for(i = 0, len = items.length; i < len; i++){
5789 //sort the temporary array
5790 c.sort(function(a, b){
5791 var v = fn(a[property], b[property]) * dsc;
5793 v = (a.index < b.index ? -1 : 1);
5798 //copy the temporary array back into the main this.items and this.keys objects
5799 for(i = 0, len = c.length; i < len; i++){
5800 items[i] = c[i].value;
5804 this.fireEvent('sort', this);
5808 * Sorts this collection by <b>item</b> value with the passed comparison function.
5809 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
5810 * @param {Function} fn (optional) Comparison function that defines the sort order.
5811 * Defaults to sorting by numeric value.
5813 sort : function(dir, fn){
5814 this._sort('value', dir, fn);
5818 * Reorders each of the items based on a mapping from old index to new index. Internally this
5819 * just translates into a sort. The 'sort' event is fired whenever reordering has occured.
5820 * @param {Object} mapping Mapping from old item index to new item index
5822 reorder: function(mapping) {
5823 this.suspendEvents();
5825 var items = this.items,
5827 length = items.length,
5832 //object of {oldPosition: newPosition} reversed to {newPosition: oldPosition}
5833 for (oldIndex in mapping) {
5834 order[mapping[oldIndex]] = items[oldIndex];
5837 for (index = 0; index < length; index++) {
5838 if (mapping[index] == undefined) {
5839 remaining.push(items[index]);
5843 for (index = 0; index < length; index++) {
5844 if (order[index] == undefined) {
5845 order[index] = remaining.shift();
5852 this.resumeEvents();
5853 this.fireEvent('sort', this);
5857 * Sorts this collection by <b>key</b>s.
5858 * @param {String} direction (optional) 'ASC' or 'DESC'. Defaults to 'ASC'.
5859 * @param {Function} fn (optional) Comparison function that defines the sort order.
5860 * Defaults to sorting by case insensitive string.
5862 keySort : function(dir, fn){
5863 this._sort('key', dir, fn || function(a, b){
5864 var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
5865 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5870 * Returns a range of items in this collection
5871 * @param {Number} startIndex (optional) The starting index. Defaults to 0.
5872 * @param {Number} endIndex (optional) The ending index. Defaults to the last item.
5873 * @return {Array} An array of items
5875 getRange : function(start, end){
5876 var items = this.items;
5877 if(items.length < 1){
5881 end = Math.min(typeof end == 'undefined' ? this.length-1 : end, this.length-1);
5884 for(i = start; i <= end; i++) {
5885 r[r.length] = items[i];
5888 for(i = start; i >= end; i--) {
5889 r[r.length] = items[i];
5896 * Filter the <i>objects</i> in this collection by a specific property.
5897 * Returns a new collection that has been filtered.
5898 * @param {String} property A property on your objects
5899 * @param {String/RegExp} value Either string that the property values
5900 * should start with or a RegExp to test against the property
5901 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning
5902 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison (defaults to False).
5903 * @return {MixedCollection} The new filtered collection
5905 filter : function(property, value, anyMatch, caseSensitive){
5906 if(Ext.isEmpty(value, false)){
5907 return this.clone();
5909 value = this.createValueMatcher(value, anyMatch, caseSensitive);
5910 return this.filterBy(function(o){
5911 return o && value.test(o[property]);
5916 * Filter by a function. Returns a <i>new</i> collection that has been filtered.
5917 * The passed function will be called with each object in the collection.
5918 * If the function returns true, the value is included otherwise it is filtered.
5919 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
5920 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
5921 * @return {MixedCollection} The new filtered collection
5923 filterBy : function(fn, scope){
5924 var r = new Ext.util.MixedCollection();
5925 r.getKey = this.getKey;
5926 var k = this.keys, it = this.items;
5927 for(var i = 0, len = it.length; i < len; i++){
5928 if(fn.call(scope||this, it[i], k[i])){
5936 * Finds the index of the first matching object in this collection by a specific property/value.
5937 * @param {String} property The name of a property on your objects.
5938 * @param {String/RegExp} value A string that the property values
5939 * should start with or a RegExp to test against the property.
5940 * @param {Number} start (optional) The index to start searching at (defaults to 0).
5941 * @param {Boolean} anyMatch (optional) True to match any part of the string, not just the beginning.
5942 * @param {Boolean} caseSensitive (optional) True for case sensitive comparison.
5943 * @return {Number} The matched index or -1
5945 findIndex : function(property, value, start, anyMatch, caseSensitive){
5946 if(Ext.isEmpty(value, false)){
5949 value = this.createValueMatcher(value, anyMatch, caseSensitive);
5950 return this.findIndexBy(function(o){
5951 return o && value.test(o[property]);
5956 * Find the index of the first matching object in this collection by a function.
5957 * If the function returns <i>true</i> it is considered a match.
5958 * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key).
5959 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to this MixedCollection.
5960 * @param {Number} start (optional) The index to start searching at (defaults to 0).
5961 * @return {Number} The matched index or -1
5963 findIndexBy : function(fn, scope, start){
5964 var k = this.keys, it = this.items;
5965 for(var i = (start||0), len = it.length; i < len; i++){
5966 if(fn.call(scope||this, it[i], k[i])){
5974 * Returns a regular expression based on the given value and matching options. This is used internally for finding and filtering,
5975 * and by Ext.data.Store#filter
5977 * @param {String} value The value to create the regex for. This is escaped using Ext.escapeRe
5978 * @param {Boolean} anyMatch True to allow any match - no regex start/end line anchors will be added. Defaults to false
5979 * @param {Boolean} caseSensitive True to make the regex case sensitive (adds 'i' switch to regex). Defaults to false.
5980 * @param {Boolean} exactMatch True to force exact match (^ and $ characters added to the regex). Defaults to false. Ignored if anyMatch is true.
5982 createValueMatcher : function(value, anyMatch, caseSensitive, exactMatch) {
5983 if (!value.exec) { // not a regex
5984 var er = Ext.escapeRe;
5985 value = String(value);
5987 if (anyMatch === true) {
5990 value = '^' + er(value);
5991 if (exactMatch === true) {
5995 value = new RegExp(value, caseSensitive ? '' : 'i');
6001 * Creates a shallow copy of this collection
6002 * @return {MixedCollection}
6005 var r = new Ext.util.MixedCollection();
6006 var k = this.keys, it = this.items;
6007 for(var i = 0, len = it.length; i < len; i++){
6010 r.getKey = this.getKey;
6015 * This method calls {@link #item item()}.
6016 * Returns the item associated with the passed key OR index. Key has priority
6017 * over index. This is the equivalent of calling {@link #key} first, then if
6018 * nothing matched calling {@link #itemAt}.
6019 * @param {String/Number} key The key or index of the item.
6020 * @return {Object} If the item is found, returns the item. If the item was
6021 * not found, returns <tt>undefined</tt>. If an item was found, but is a Class,
6022 * returns <tt>null</tt>.
6024 Ext.util.MixedCollection.prototype.get = Ext.util.MixedCollection.prototype.item;
6026 * @class Ext.AbstractManager
6028 * Base Manager class - extended by ComponentMgr and PluginMgr
6030 Ext.AbstractManager = Ext.extend(Object, {
6033 constructor: function(config) {
6034 Ext.apply(this, config || {});
6037 * Contains all of the items currently managed
6039 * @type Ext.util.MixedCollection
6041 this.all = new Ext.util.MixedCollection();
6047 * Returns a component by {@link Ext.Component#id id}.
6048 * For additional details see {@link Ext.util.MixedCollection#get}.
6049 * @param {String} id The component {@link Ext.Component#id id}
6050 * @return Ext.Component The Component, <code>undefined</code> if not found, or <code>null</code> if a
6054 return this.all.get(id);
6058 * Registers an item to be managed
6059 * @param {Mixed} item The item to register
6061 register: function(item) {
6066 * Unregisters a component by removing it from this manager
6067 * @param {Mixed} item The item to unregister
6069 unregister: function(item) {
6070 this.all.remove(item);
6074 * <p>Registers a new Component constructor, keyed by a new
6075 * {@link Ext.Component#xtype}.</p>
6076 * <p>Use this method (or its alias {@link Ext#reg Ext.reg}) to register new
6077 * subclasses of {@link Ext.Component} so that lazy instantiation may be used when specifying
6079 * see {@link Ext.Container#items}</p>
6080 * @param {String} xtype The mnemonic string by which the Component class may be looked up.
6081 * @param {Constructor} cls The new Component class.
6083 registerType : function(type, cls){
6084 this.types[type] = cls;
6085 cls[this.typeName] = type;
6089 * Checks if a Component type is registered.
6090 * @param {Ext.Component} xtype The mnemonic string by which the Component class may be looked up
6091 * @return {Boolean} Whether the type is registered.
6093 isRegistered : function(type){
6094 return this.types[type] !== undefined;
6098 * Creates and returns an instance of whatever this manager manages, based on the supplied type and config object
6099 * @param {Object} config The config object
6100 * @param {String} defaultType If no type is discovered in the config object, we fall back to this type
6101 * @return {Mixed} The instance of whatever this manager is managing
6103 create: function(config, defaultType) {
6104 var type = config[this.typeName] || config.type || defaultType,
6105 Constructor = this.types[type];
6107 if (Constructor == undefined) {
6108 throw new Error(String.format("The '{0}' type has not been registered with this manager", type));
6111 return new Constructor(config);
6115 * Registers a function that will be called when a Component with the specified id is added to the manager. This will happen on instantiation.
6116 * @param {String} id The component {@link Ext.Component#id id}
6117 * @param {Function} fn The callback function
6118 * @param {Object} scope The scope (<code>this</code> reference) in which the callback is executed. Defaults to the Component.
6120 onAvailable : function(id, fn, scope){
6123 all.on("add", function(index, o){
6125 fn.call(scope || o, o);
6126 all.un("add", fn, scope);
6131 * @class Ext.util.Format
6132 * Reusable data formatting functions
6135 Ext.util.Format = function() {
6136 var trimRe = /^\s+|\s+$/g,
6137 stripTagsRE = /<\/?[^>]+>/gi,
6138 stripScriptsRe = /(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig,
6143 * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
6144 * @param {String} value The string to truncate
6145 * @param {Number} length The maximum length to allow before truncating
6146 * @param {Boolean} word True to try to find a common work break
6147 * @return {String} The converted text
6149 ellipsis : function(value, len, word) {
6150 if (value && value.length > len) {
6152 var vs = value.substr(0, len - 2),
6153 index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
6154 if (index == -1 || index < (len - 15)) {
6155 return value.substr(0, len - 3) + "...";
6157 return vs.substr(0, index) + "...";
6160 return value.substr(0, len - 3) + "...";
6167 * Checks a reference and converts it to empty string if it is undefined
6168 * @param {Mixed} value Reference to check
6169 * @return {Mixed} Empty string if converted, otherwise the original value
6171 undef : function(value) {
6172 return value !== undefined ? value : "";
6176 * Checks a reference and converts it to the default value if it's empty
6177 * @param {Mixed} value Reference to check
6178 * @param {String} defaultValue The value to insert of it's undefined (defaults to "")
6181 defaultValue : function(value, defaultValue) {
6182 return value !== undefined && value !== '' ? value : defaultValue;
6186 * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
6187 * @param {String} value The string to encode
6188 * @return {String} The encoded text
6190 htmlEncode : function(value) {
6191 return !value ? value : String(value).replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
6195 * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
6196 * @param {String} value The string to decode
6197 * @return {String} The decoded text
6199 htmlDecode : function(value) {
6200 return !value ? value : String(value).replace(/>/g, ">").replace(/</g, "<").replace(/"/g, '"').replace(/&/g, "&");
6204 * Trims any whitespace from either side of a string
6205 * @param {String} value The text to trim
6206 * @return {String} The trimmed text
6208 trim : function(value) {
6209 return String(value).replace(trimRe, "");
6213 * Returns a substring from within an original string
6214 * @param {String} value The original text
6215 * @param {Number} start The start index of the substring
6216 * @param {Number} length The length of the substring
6217 * @return {String} The substring
6219 substr : function(value, start, length) {
6220 return String(value).substr(start, length);
6224 * Converts a string to all lower case letters
6225 * @param {String} value The text to convert
6226 * @return {String} The converted text
6228 lowercase : function(value) {
6229 return String(value).toLowerCase();
6233 * Converts a string to all upper case letters
6234 * @param {String} value The text to convert
6235 * @return {String} The converted text
6237 uppercase : function(value) {
6238 return String(value).toUpperCase();
6242 * Converts the first character only of a string to upper case
6243 * @param {String} value The text to convert
6244 * @return {String} The converted text
6246 capitalize : function(value) {
6247 return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
6251 call : function(value, fn) {
6252 if (arguments.length > 2) {
6253 var args = Array.prototype.slice.call(arguments, 2);
6254 args.unshift(value);
6255 return eval(fn).apply(window, args);
6257 return eval(fn).call(window, value);
6262 * Format a number as US currency
6263 * @param {Number/String} value The numeric value to format
6264 * @return {String} The formatted currency string
6266 usMoney : function(v) {
6267 v = (Math.round((v-0)*100))/100;
6268 v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
6270 var ps = v.split('.'),
6272 sub = ps[1] ? '.'+ ps[1] : '.00',
6274 while (r.test(whole)) {
6275 whole = whole.replace(r, '$1' + ',' + '$2');
6278 if (v.charAt(0) == '-') {
6279 return '-$' + v.substr(1);
6285 * Parse a value into a formatted date using the specified format pattern.
6286 * @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)
6287 * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
6288 * @return {String} The formatted date string
6290 date : function(v, format) {
6294 if (!Ext.isDate(v)) {
6295 v = new Date(Date.parse(v));
6297 return v.dateFormat(format || "m/d/Y");
6301 * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
6302 * @param {String} format Any valid date format string
6303 * @return {Function} The date formatting function
6305 dateRenderer : function(format) {
6306 return function(v) {
6307 return Ext.util.Format.date(v, format);
6312 * Strips all HTML tags
6313 * @param {Mixed} value The text from which to strip tags
6314 * @return {String} The stripped text
6316 stripTags : function(v) {
6317 return !v ? v : String(v).replace(stripTagsRE, "");
6321 * Strips all script tags
6322 * @param {Mixed} value The text from which to strip script tags
6323 * @return {String} The stripped text
6325 stripScripts : function(v) {
6326 return !v ? v : String(v).replace(stripScriptsRe, "");
6330 * Simple format for a file size (xxx bytes, xxx KB, xxx MB)
6331 * @param {Number/String} size The numeric value to format
6332 * @return {String} The formatted file size
6334 fileSize : function(size) {
6336 return size + " bytes";
6337 } else if (size < 1048576) {
6338 return (Math.round(((size*10) / 1024))/10) + " KB";
6340 return (Math.round(((size*10) / 1048576))/10) + " MB";
6345 * It does simple math for use in a template, for example:<pre><code>
6346 * var tpl = new Ext.Template('{value} * 10 = {value:math("* 10")}');
6348 * @return {Function} A function that operates on the passed value.
6353 return function(v, a){
6355 fns[a] = new Function('v', 'return v ' + a + ';');
6362 * Rounds the passed number to the required decimal precision.
6363 * @param {Number/String} value The numeric value to round.
6364 * @param {Number} precision The number of decimal places to which to round the first parameter's value.
6365 * @return {Number} The rounded value.
6367 round : function(value, precision) {
6368 var result = Number(value);
6369 if (typeof precision == 'number') {
6370 precision = Math.pow(10, precision);
6371 result = Math.round(value * precision) / precision;
6377 * Formats the number according to the format string.
6378 * <div style="margin-left:40px">examples (123456.789):
6379 * <div style="margin-left:10px">
6380 * 0 - (123456) show only digits, no precision<br>
6381 * 0.00 - (123456.78) show only digits, 2 precision<br>
6382 * 0.0000 - (123456.7890) show only digits, 4 precision<br>
6383 * 0,000 - (123,456) show comma and digits, no precision<br>
6384 * 0,000.00 - (123,456.78) show comma and digits, 2 precision<br>
6385 * 0,0.00 - (123,456.78) shortcut method, show comma and digits, 2 precision<br>
6386 * To reverse the grouping (,) and decimal (.) for international numbers, add /i to the end.
6387 * For example: 0.000,00/i
6389 * @param {Number} v The number to format.
6390 * @param {String} format The way you would like to format this text.
6391 * @return {String} The formatted number.
6393 number: function(v, format) {
6397 v = Ext.num(v, NaN);
6407 if (format.substr(format.length - 2) == '/i') {
6408 format = format.substr(0, format.length - 2);
6414 var hasComma = format.indexOf(comma) != -1,
6415 psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);
6417 if (1 < psplit.length) {
6418 v = v.toFixed(psplit[1].length);
6419 } else if(2 < psplit.length) {
6420 throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
6425 var fnum = v.toString();
6427 psplit = fnum.split('.');
6430 var cnum = psplit[0],
6433 m = Math.floor(j / 3),
6434 n = cnum.length % 3 || 3,
6437 for (i = 0; i < j; i += n) {
6442 parr[parr.length] = cnum.substr(i, n);
6445 fnum = parr.join(comma);
6447 fnum += dec + psplit[1];
6451 fnum = psplit[0] + dec + psplit[1];
6455 return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
6459 * Returns a number rendering function that can be reused to apply a number format multiple times efficiently
6460 * @param {String} format Any valid number format string for {@link #number}
6461 * @return {Function} The number formatting function
6463 numberRenderer : function(format) {
6464 return function(v) {
6465 return Ext.util.Format.number(v, format);
6470 * Selectively do a plural form of a word based on a numeric value. For example, in a template,
6471 * {commentCount:plural("Comment")} would result in "1 Comment" if commentCount was 1 or would be "x Comments"
6472 * if the value is 0 or greater than 1.
6473 * @param {Number} value The value to compare against
6474 * @param {String} singular The singular form of the word
6475 * @param {String} plural (optional) The plural form of the word (defaults to the singular with an "s")
6477 plural : function(v, s, p) {
6478 return v +' ' + (v == 1 ? s : (p ? p : s+'s'));
6482 * Converts newline characters to the HTML tag <br/>
6483 * @param {String} The string value to format.
6484 * @return {String} The string with embedded <br/> tags in place of newlines.
6486 nl2br : function(v) {
6487 return Ext.isEmpty(v) ? '' : v.replace(nl2brRe, '<br/>');
6492 * @class Ext.XTemplate
6493 * @extends Ext.Template
6494 * <p>A template class that supports advanced functionality like:<div class="mdetail-params"><ul>
6495 * <li>Autofilling arrays using templates and sub-templates</li>
6496 * <li>Conditional processing with basic comparison operators</li>
6497 * <li>Basic math function support</li>
6498 * <li>Execute arbitrary inline code with special built-in template variables</li>
6499 * <li>Custom member functions</li>
6500 * <li>Many special tags and built-in operators that aren't defined as part of
6501 * the API, but are supported in the templates that can be created</li>
6503 * <p>XTemplate provides the templating mechanism built into:<div class="mdetail-params"><ul>
6504 * <li>{@link Ext.DataView}</li>
6505 * <li>{@link Ext.ListView}</li>
6506 * <li>{@link Ext.form.ComboBox}</li>
6507 * <li>{@link Ext.grid.TemplateColumn}</li>
6508 * <li>{@link Ext.grid.GroupingView}</li>
6509 * <li>{@link Ext.menu.Item}</li>
6510 * <li>{@link Ext.layout.MenuLayout}</li>
6511 * <li>{@link Ext.ColorPalette}</li>
6514 * <p>For example usage {@link #XTemplate see the constructor}.</p>
6517 * The {@link Ext.Template#Template Ext.Template constructor} describes
6518 * the acceptable parameters to pass to the constructor. The following
6519 * examples demonstrate all of the supported features.</p>
6521 * <div class="mdetail-params"><ul>
6523 * <li><b><u>Sample Data</u></b>
6524 * <div class="sub-desc">
6525 * <p>This is the data object used for reference in each code example:</p>
6528 name: 'Jack Slocum',
6529 title: 'Lead Developer',
6530 company: 'Ext JS, LLC',
6531 email: 'jack@extjs.com',
6532 address: '4 Red Bulls Drive',
6536 drinks: ['Red Bull', 'Coffee', 'Water'],
6553 * <li><b><u>Auto filling of arrays</u></b>
6554 * <div class="sub-desc">
6555 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>for</tt></b> operator are used
6556 * to process the provided data object:
6558 * <li>If the value specified in <tt>for</tt> is an array, it will auto-fill,
6559 * repeating the template block inside the <tt>tpl</tt> tag for each item in the
6561 * <li>If <tt>for="."</tt> is specified, the data object provided is examined.</li>
6562 * <li>While processing an array, the special variable <tt>{#}</tt>
6563 * will provide the current array index + 1 (starts at 1, not 0).</li>
6567 <tpl <b>for</b>=".">...</tpl> // loop through array at root node
6568 <tpl <b>for</b>="foo">...</tpl> // loop through array at foo node
6569 <tpl <b>for</b>="foo.bar">...</tpl> // loop through array at foo.bar node
6571 * Using the sample data above:
6573 var tpl = new Ext.XTemplate(
6575 '<tpl <b>for</b>=".">', // process the data.kids node
6576 '<p>{#}. {name}</p>', // use current array index to autonumber
6579 tpl.overwrite(panel.body, data.kids); // pass the kids property of the data object
6581 * <p>An example illustrating how the <b><tt>for</tt></b> property can be leveraged
6582 * to access specified members of the provided data object to populate the template:</p>
6584 var tpl = new Ext.XTemplate(
6585 '<p>Name: {name}</p>',
6586 '<p>Title: {title}</p>',
6587 '<p>Company: {company}</p>',
6589 '<tpl <b>for="kids"</b>>', // interrogate the kids property within the data
6590 '<p>{name}</p>',
6593 tpl.overwrite(panel.body, data); // pass the root node of the data object
6595 * <p>Flat arrays that contain values (and not objects) can be auto-rendered
6596 * using the special <b><tt>{.}</tt></b> variable inside a loop. This variable
6597 * will represent the value of the array at the current index:</p>
6599 var tpl = new Ext.XTemplate(
6600 '<p>{name}\'s favorite beverages:</p>',
6601 '<tpl for="drinks">',
6602 '<div> - {.}</div>',
6605 tpl.overwrite(panel.body, data);
6607 * <p>When processing a sub-template, for example while looping through a child array,
6608 * you can access the parent object's members via the <b><tt>parent</tt></b> object:</p>
6610 var tpl = new Ext.XTemplate(
6611 '<p>Name: {name}</p>',
6613 '<tpl for="kids">',
6614 '<tpl if="age > 1">',
6615 '<p>{name}</p>',
6616 '<p>Dad: {<b>parent</b>.name}</p>',
6620 tpl.overwrite(panel.body, data);
6626 * <li><b><u>Conditional processing with basic comparison operators</u></b>
6627 * <div class="sub-desc">
6628 * <p>The <b><tt>tpl</tt></b> tag and the <b><tt>if</tt></b> operator are used
6629 * to provide conditional checks for deciding whether or not to render specific
6630 * parts of the template. Notes:<div class="sub-desc"><ul>
6631 * <li>Double quotes must be encoded if used within the conditional</li>
6632 * <li>There is no <tt>else</tt> operator — if needed, two opposite
6633 * <tt>if</tt> statements should be used.</li>
6636 <tpl if="age > 1 && age < 10">Child</tpl>
6637 <tpl if="age >= 10 && age < 18">Teenager</tpl>
6638 <tpl <b>if</b>="this.isGirl(name)">...</tpl>
6639 <tpl <b>if</b>="id==\'download\'">...</tpl>
6640 <tpl <b>if</b>="needsIcon"><img src="{icon}" class="{iconCls}"/></tpl>
6642 <tpl if="name == "Jack"">Hello</tpl>
6643 // encode " if it is part of the condition, e.g.
6644 <tpl if="name == &quot;Jack&quot;">Hello</tpl>
6646 * Using the sample data above:
6648 var tpl = new Ext.XTemplate(
6649 '<p>Name: {name}</p>',
6651 '<tpl for="kids">',
6652 '<tpl if="age > 1">',
6653 '<p>{name}</p>',
6657 tpl.overwrite(panel.body, data);
6663 * <li><b><u>Basic math support</u></b>
6664 * <div class="sub-desc">
6665 * <p>The following basic math operators may be applied directly on numeric
6666 * data values:</p><pre>
6671 var tpl = new Ext.XTemplate(
6672 '<p>Name: {name}</p>',
6674 '<tpl for="kids">',
6675 '<tpl if="age &gt; 1">', // <-- Note that the > is encoded
6676 '<p>{#}: {name}</p>', // <-- Auto-number each item
6677 '<p>In 5 Years: {age+5}</p>', // <-- Basic math
6678 '<p>Dad: {parent.name}</p>',
6682 tpl.overwrite(panel.body, data);
6688 * <li><b><u>Execute arbitrary inline code with special built-in template variables</u></b>
6689 * <div class="sub-desc">
6690 * <p>Anything between <code>{[ ... ]}</code> is considered code to be executed
6691 * in the scope of the template. There are some special variables available in that code:
6693 * <li><b><tt>values</tt></b>: The values in the current scope. If you are using
6694 * scope changing sub-templates, you can change what <tt>values</tt> is.</li>
6695 * <li><b><tt>parent</tt></b>: The scope (values) of the ancestor template.</li>
6696 * <li><b><tt>xindex</tt></b>: If you are in a looping template, the index of the
6697 * loop you are in (1-based).</li>
6698 * <li><b><tt>xcount</tt></b>: If you are in a looping template, the total length
6699 * of the array you are looping.</li>
6700 * <li><b><tt>fm</tt></b>: An alias for <tt>Ext.util.Format</tt>.</li>
6702 * This example demonstrates basic row striping using an inline code block and the
6703 * <tt>xindex</tt> variable:</p>
6705 var tpl = new Ext.XTemplate(
6706 '<p>Name: {name}</p>',
6707 '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
6709 '<tpl for="kids">',
6710 '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
6715 tpl.overwrite(panel.body, data);
6720 * <li><b><u>Template member functions</u></b>
6721 * <div class="sub-desc">
6722 * <p>One or more member functions can be specified in a configuration
6723 * object passed into the XTemplate constructor for more complex processing:</p>
6725 var tpl = new Ext.XTemplate(
6726 '<p>Name: {name}</p>',
6728 '<tpl for="kids">',
6729 '<tpl if="this.isGirl(name)">',
6730 '<p>Girl: {name} - {age}</p>',
6732 // use opposite if statement to simulate 'else' processing:
6733 '<tpl if="this.isGirl(name) == false">',
6734 '<p>Boy: {name} - {age}</p>',
6736 '<tpl if="this.isBaby(age)">',
6737 '<p>{name} is a baby!</p>',
6741 // XTemplate configuration:
6743 disableFormats: true,
6744 // member functions:
6745 isGirl: function(name){
6746 return name == 'Sara Grace';
6748 isBaby: function(age){
6753 tpl.overwrite(panel.body, data);
6760 * @param {Mixed} config
6762 Ext.XTemplate = function(){
6763 Ext.XTemplate.superclass.constructor.apply(this, arguments);
6767 re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
6768 nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
6769 ifRe = /^<tpl\b[^>]*?if="(.*?)"/,
6770 execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
6779 WITHVALUES = 'with(values){ ';
6781 s = ['<tpl>', s, '</tpl>'].join('');
6783 while((m = s.match(re))){
6784 var m2 = m[0].match(nameRe),
6785 m3 = m[0].match(ifRe),
6786 m4 = m[0].match(execRe),
6790 name = m2 && m2[1] ? m2[1] : '';
6793 exp = m3 && m3[1] ? m3[1] : null;
6795 fn = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES + RETURN +(Ext.util.Format.htmlDecode(exp))+'; }');
6799 exp = m4 && m4[1] ? m4[1] : null;
6801 exec = new Function(VALUES, PARENT, XINDEX, XCOUNT, WITHVALUES +(Ext.util.Format.htmlDecode(exp))+'; }');
6806 case '.': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + VALUES + '; }'); break;
6807 case '..': name = new Function(VALUES, PARENT, WITHVALUES + RETURN + PARENT + '; }'); break;
6808 default: name = new Function(VALUES, PARENT, WITHVALUES + RETURN + name + '; }');
6818 s = s.replace(m[0], '{xtpl'+ id + '}');
6821 for(var i = tpls.length-1; i >= 0; --i){
6822 me.compileTpl(tpls[i]);
6824 me.master = tpls[tpls.length-1];
6827 Ext.extend(Ext.XTemplate, Ext.Template, {
6829 re : /\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g,
6831 codeRe : /\{\[((?:\\\]|.|\n)*?)\]\}/g,
6834 applySubTemplate : function(id, values, parent, xindex, xcount){
6840 if ((t.test && !t.test.call(me, values, parent, xindex, xcount)) ||
6841 (t.exec && t.exec.call(me, values, parent, xindex, xcount))) {
6844 vs = t.target ? t.target.call(me, values, parent) : values;
6846 parent = t.target ? values : parent;
6847 if(t.target && Ext.isArray(vs)){
6848 for(var i = 0, len = vs.length; i < len; i++){
6849 buf[buf.length] = t.compiled.call(me, vs[i], parent, i+1, len);
6851 return buf.join('');
6853 return t.compiled.call(me, vs, parent, xindex, xcount);
6857 compileTpl : function(tpl){
6858 var fm = Ext.util.Format,
6859 useF = this.disableFormats !== true,
6860 sep = Ext.isGecko ? "+" : ",",
6863 function fn(m, name, format, args, math){
6864 if(name.substr(0, 4) == 'xtpl'){
6865 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'";
6870 }else if(name === '#'){
6872 }else if(name.indexOf('.') != -1){
6875 v = "values['" + name + "']";
6878 v = '(' + v + math + ')';
6880 if (format && useF) {
6881 args = args ? ',' + args : "";
6882 if(format.substr(0, 5) != "this."){
6883 format = "fm." + format + '(';
6885 format = 'this.call("'+ format.substr(5) + '", ';
6889 args= ''; format = "("+v+" === undefined ? '' : ";
6891 return "'"+ sep + format + v + args + ")"+sep+"'";
6894 function codeFn(m, code){
6895 // Single quotes get escaped when the template is compiled, however we want to undo this when running code.
6896 return "'" + sep + '(' + code.replace(/\\'/g, "'") + ')' + sep + "'";
6899 // branched to use + in gecko and [].join() in others
6901 body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" +
6902 tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) +
6905 body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"];
6906 body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn));
6907 body.push("'].join('');};");
6908 body = body.join('');
6915 * Returns an HTML fragment of this template with the specified values applied.
6916 * @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'})
6917 * @return {String} The HTML fragment
6919 applyTemplate : function(values){
6920 return this.master.compiled.call(this, values, {}, 1, 1);
6924 * Compile the template to a function for optimized performance. Recommended if the template will be used frequently.
6925 * @return {Function} The compiled function
6927 compile : function(){return this;}
6934 * @property disableFormats
6944 * Alias for {@link #applyTemplate}
6945 * Returns an HTML fragment of this template with the specified values applied.
6946 * @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'})
6947 * @return {String} The HTML fragment
6948 * @member Ext.XTemplate
6951 Ext.XTemplate.prototype.apply = Ext.XTemplate.prototype.applyTemplate;
6954 * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6955 * @param {String/HTMLElement} el A DOM element or its id
6956 * @return {Ext.Template} The created template
6959 Ext.XTemplate.from = function(el){
6960 el = Ext.getDom(el);
6961 return new Ext.XTemplate(el.value || el.innerHTML);
6964 * @class Ext.util.CSS
6965 * Utility class for manipulating CSS rules
6968 Ext.util.CSS = function(){
6972 var camelRe = /(-[a-z])/gi;
6973 var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6977 * Creates a stylesheet from a text blob of rules.
6978 * These rules will be wrapped in a STYLE tag and appended to the HEAD of the document.
6979 * @param {String} cssText The text containing the css rules
6980 * @param {String} id An id to add to the stylesheet for later removal
6981 * @return {StyleSheet}
6983 createStyleSheet : function(cssText, id){
6985 var head = doc.getElementsByTagName("head")[0];
6986 var rules = doc.createElement("style");
6987 rules.setAttribute("type", "text/css");
6989 rules.setAttribute("id", id);
6992 head.appendChild(rules);
6993 ss = rules.styleSheet;
6994 ss.cssText = cssText;
6997 rules.appendChild(doc.createTextNode(cssText));
6999 rules.cssText = cssText;
7001 head.appendChild(rules);
7002 ss = rules.styleSheet ? rules.styleSheet : (rules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
7004 this.cacheStyleSheet(ss);
7009 * Removes a style or link tag by id
7010 * @param {String} id The id of the tag
7012 removeStyleSheet : function(id){
7013 var existing = doc.getElementById(id);
7015 existing.parentNode.removeChild(existing);
7020 * Dynamically swaps an existing stylesheet reference for a new one
7021 * @param {String} id The id of an existing link tag to remove
7022 * @param {String} url The href of the new stylesheet to include
7024 swapStyleSheet : function(id, url){
7025 this.removeStyleSheet(id);
7026 var ss = doc.createElement("link");
7027 ss.setAttribute("rel", "stylesheet");
7028 ss.setAttribute("type", "text/css");
7029 ss.setAttribute("id", id);
7030 ss.setAttribute("href", url);
7031 doc.getElementsByTagName("head")[0].appendChild(ss);
7035 * Refresh the rule cache if you have dynamically added stylesheets
7036 * @return {Object} An object (hash) of rules indexed by selector
7038 refreshCache : function(){
7039 return this.getRules(true);
7043 cacheStyleSheet : function(ss){
7047 try{// try catch for cross domain access issue
7048 var ssRules = ss.cssRules || ss.rules;
7049 for(var j = ssRules.length-1; j >= 0; --j){
7050 rules[ssRules[j].selectorText.toLowerCase()] = ssRules[j];
7056 * Gets all css rules for the document
7057 * @param {Boolean} refreshCache true to refresh the internal cache
7058 * @return {Object} An object (hash) of rules indexed by selector
7060 getRules : function(refreshCache){
7061 if(rules === null || refreshCache){
7063 var ds = doc.styleSheets;
7064 for(var i =0, len = ds.length; i < len; i++){
7066 this.cacheStyleSheet(ds[i]);
7074 * Gets an an individual CSS rule by selector(s)
7075 * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
7076 * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
7077 * @return {CSSRule} The CSS rule or null if one is not found
7079 getRule : function(selector, refreshCache){
7080 var rs = this.getRules(refreshCache);
7081 if(!Ext.isArray(selector)){
7082 return rs[selector.toLowerCase()];
7084 for(var i = 0; i < selector.length; i++){
7085 if(rs[selector[i]]){
7086 return rs[selector[i].toLowerCase()];
7094 * Updates a rule property
7095 * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
7096 * @param {String} property The css property
7097 * @param {String} value The new value for the property
7098 * @return {Boolean} true If a rule was found and updated
7100 updateRule : function(selector, property, value){
7101 if(!Ext.isArray(selector)){
7102 var rule = this.getRule(selector);
7104 rule.style[property.replace(camelRe, camelFn)] = value;
7108 for(var i = 0; i < selector.length; i++){
7109 if(this.updateRule(selector[i], property, value)){
7118 @class Ext.util.ClickRepeater
7119 @extends Ext.util.Observable
7121 A wrapper class which can be applied to any element. Fires a "click" event while the
7122 mouse is pressed. The interval between firings may be specified in the config but
7123 defaults to 20 milliseconds.
7125 Optionally, a CSS class may be applied to the element during the time it is pressed.
7127 @cfg {Mixed} el The element to act as a button.
7128 @cfg {Number} delay The initial delay before the repeating event begins firing.
7129 Similar to an autorepeat key delay.
7130 @cfg {Number} interval The interval between firings of the "click" event. Default 20 ms.
7131 @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
7132 @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
7133 "interval" and "delay" are ignored.
7134 @cfg {Boolean} preventDefault True to prevent the default click event
7135 @cfg {Boolean} stopDefault True to stop the default click event
7138 2007-02-02 jvs Original code contributed by Nige "Animal" White
7139 2007-02-02 jvs Renamed to ClickRepeater
7140 2007-02-03 jvs Modifications for FF Mac and Safari
7143 @param {Mixed} el The element to listen on
7144 @param {Object} config
7146 Ext.util.ClickRepeater = Ext.extend(Ext.util.Observable, {
7148 constructor : function(el, config){
7149 this.el = Ext.get(el);
7150 this.el.unselectable();
7152 Ext.apply(this, config);
7157 * Fires when the mouse button is depressed.
7158 * @param {Ext.util.ClickRepeater} this
7159 * @param {Ext.EventObject} e
7164 * Fires on a specified interval during the time the element is pressed.
7165 * @param {Ext.util.ClickRepeater} this
7166 * @param {Ext.EventObject} e
7171 * Fires when the mouse key is released.
7172 * @param {Ext.util.ClickRepeater} this
7173 * @param {Ext.EventObject} e
7179 this.disabled = true;
7183 // allow inline handler
7185 this.on("click", this.handler, this.scope || this);
7188 Ext.util.ClickRepeater.superclass.constructor.call(this);
7193 preventDefault : true,
7194 stopDefault : false,
7198 * Enables the repeater and allows events to fire.
7202 this.el.on('mousedown', this.handleMouseDown, this);
7204 this.el.on('dblclick', this.handleDblClick, this);
7206 if(this.preventDefault || this.stopDefault){
7207 this.el.on('click', this.eventOptions, this);
7210 this.disabled = false;
7214 * Disables the repeater and stops events from firing.
7216 disable: function(/* private */ force){
7217 if(force || !this.disabled){
7218 clearTimeout(this.timer);
7219 if(this.pressClass){
7220 this.el.removeClass(this.pressClass);
7222 Ext.getDoc().un('mouseup', this.handleMouseUp, this);
7223 this.el.removeAllListeners();
7225 this.disabled = true;
7229 * Convenience function for setting disabled/enabled by boolean.
7230 * @param {Boolean} disabled
7232 setDisabled: function(disabled){
7233 this[disabled ? 'disable' : 'enable']();
7236 eventOptions: function(e){
7237 if(this.preventDefault){
7240 if(this.stopDefault){
7246 destroy : function() {
7248 Ext.destroy(this.el);
7249 this.purgeListeners();
7252 handleDblClick : function(e){
7253 clearTimeout(this.timer);
7256 this.fireEvent("mousedown", this, e);
7257 this.fireEvent("click", this, e);
7261 handleMouseDown : function(e){
7262 clearTimeout(this.timer);
7264 if(this.pressClass){
7265 this.el.addClass(this.pressClass);
7267 this.mousedownTime = new Date();
7269 Ext.getDoc().on("mouseup", this.handleMouseUp, this);
7270 this.el.on("mouseout", this.handleMouseOut, this);
7272 this.fireEvent("mousedown", this, e);
7273 this.fireEvent("click", this, e);
7275 // Do not honor delay or interval if acceleration wanted.
7276 if (this.accelerate) {
7279 this.timer = this.click.defer(this.delay || this.interval, this, [e]);
7283 click : function(e){
7284 this.fireEvent("click", this, e);
7285 this.timer = this.click.defer(this.accelerate ?
7286 this.easeOutExpo(this.mousedownTime.getElapsed(),
7290 this.interval, this, [e]);
7293 easeOutExpo : function (t, b, c, d) {
7294 return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
7298 handleMouseOut : function(){
7299 clearTimeout(this.timer);
7300 if(this.pressClass){
7301 this.el.removeClass(this.pressClass);
7303 this.el.on("mouseover", this.handleMouseReturn, this);
7307 handleMouseReturn : function(){
7308 this.el.un("mouseover", this.handleMouseReturn, this);
7309 if(this.pressClass){
7310 this.el.addClass(this.pressClass);
7316 handleMouseUp : function(e){
7317 clearTimeout(this.timer);
7318 this.el.un("mouseover", this.handleMouseReturn, this);
7319 this.el.un("mouseout", this.handleMouseOut, this);
7320 Ext.getDoc().un("mouseup", this.handleMouseUp, this);
7321 this.el.removeClass(this.pressClass);
7322 this.fireEvent("mouseup", this, e);
7326 * <p>Provides a convenient wrapper for normalized keyboard navigation. KeyNav allows you to bind
7327 * navigation keys to function calls that will get called when the keys are pressed, providing an easy
7328 * way to implement custom navigation schemes for any UI component.</p>
7329 * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
7330 * pageUp, pageDown, del, home, end. Usage:</p>
7332 var nav = new Ext.KeyNav("my-element", {
7333 "left" : function(e){
7334 this.moveLeft(e.ctrlKey);
7336 "right" : function(e){
7337 this.moveRight(e.ctrlKey);
7339 "enter" : function(e){
7346 * @param {Mixed} el The element to bind to
7347 * @param {Object} config The config
7349 Ext.KeyNav = function(el, config){
7350 this.el = Ext.get(el);
7351 Ext.apply(this, config);
7353 this.disabled = true;
7358 Ext.KeyNav.prototype = {
7360 * @cfg {Boolean} disabled
7361 * True to disable this KeyNav instance (defaults to false)
7365 * @cfg {String} defaultEventAction
7366 * The method to call on the {@link Ext.EventObject} after this KeyNav intercepts a key. Valid values are
7367 * {@link Ext.EventObject#stopEvent}, {@link Ext.EventObject#preventDefault} and
7368 * {@link Ext.EventObject#stopPropagation} (defaults to 'stopEvent')
7370 defaultEventAction: "stopEvent",
7372 * @cfg {Boolean} forceKeyDown
7373 * Handle the keydown event instead of keypress (defaults to false). KeyNav automatically does this for IE since
7374 * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
7375 * handle keydown instead of keypress.
7377 forceKeyDown : false,
7380 relay : function(e){
7382 h = this.keyToHandler[k];
7384 if(this.doRelay(e, this[h], h) !== true){
7385 e[this.defaultEventAction]();
7391 doRelay : function(e, h, hname){
7392 return h.call(this.scope || this, e, hname);
7395 // possible handlers
7409 // quick lookup hash
7425 stopKeyUp: function(e) {
7428 if (k >= 37 && k <= 40) {
7429 // *** bugfix - safari 2.x fires 2 keyup events on cursor keys
7430 // *** (note: this bugfix sacrifices the "keyup" event originating from keyNav elements in Safari 2)
7436 * Destroy this KeyNav (this is the same as calling disable).
7438 destroy: function(){
7443 * Enable this KeyNav
7445 enable: function() {
7446 if (this.disabled) {
7447 if (Ext.isSafari2) {
7448 // call stopKeyUp() on "keyup" event
7449 this.el.on('keyup', this.stopKeyUp, this);
7452 this.el.on(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
7453 this.disabled = false;
7458 * Disable this KeyNav
7460 disable: function() {
7461 if (!this.disabled) {
7462 if (Ext.isSafari2) {
7463 // remove "keyup" event handler
7464 this.el.un('keyup', this.stopKeyUp, this);
7467 this.el.un(this.isKeydown()? 'keydown' : 'keypress', this.relay, this);
7468 this.disabled = true;
7473 * Convenience function for setting disabled/enabled by boolean.
7474 * @param {Boolean} disabled
7476 setDisabled : function(disabled){
7477 this[disabled ? "disable" : "enable"]();
7481 isKeydown: function(){
7482 return this.forceKeyDown || Ext.EventManager.useKeydown;
7487 * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
7488 * The constructor accepts the same config object as defined by {@link #addBinding}.
7489 * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
7490 * combination it will call the function with this signature (if the match is a multi-key
7491 * combination the callback will still be called only once): (String key, Ext.EventObject e)
7492 * A KeyMap can also handle a string representation of keys.<br />
7495 // map one key by key code
7496 var map = new Ext.KeyMap("my-element", {
7497 key: 13, // or Ext.EventObject.ENTER
7502 // map multiple keys to one action by string
7503 var map = new Ext.KeyMap("my-element", {
7509 // map multiple keys to multiple actions by strings and array of codes
7510 var map = new Ext.KeyMap("my-element", [
7513 fn: function(){ alert("Return was pressed"); }
7516 fn: function(){ alert('a, b or c was pressed'); }
7521 fn: function(){ alert('Control + shift + tab was pressed.'); }
7525 * <b>Note: A KeyMap starts enabled</b>
7527 * @param {Mixed} el The element to bind to
7528 * @param {Object} config The config (see {@link #addBinding})
7529 * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
7531 Ext.KeyMap = function(el, config, eventName){
7532 this.el = Ext.get(el);
7533 this.eventName = eventName || "keydown";
7536 this.addBinding(config);
7541 Ext.KeyMap.prototype = {
7543 * True to stop the event from bubbling and prevent the default browser action if the
7544 * key was handled by the KeyMap (defaults to false)
7550 * Add a new binding to this KeyMap. The following config object properties are supported:
7552 Property Type Description
7553 ---------- --------------- ----------------------------------------------------------------------
7554 key String/Array A single keycode or an array of keycodes to handle
7555 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)
7556 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)
7557 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)
7558 handler Function The function to call when KeyMap finds the expected key combination
7559 fn Function Alias of handler (for backwards-compatibility)
7560 scope Object The scope of the callback function
7561 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)
7567 var map = new Ext.KeyMap(document, {
7568 key: Ext.EventObject.ENTER,
7573 //Add a new binding to the existing KeyMap later
7581 * @param {Object/Array} config A single KeyMap config or an array of configs
7583 addBinding : function(config){
7584 if(Ext.isArray(config)){
7585 Ext.each(config, function(c){
7590 var keyCode = config.key,
7591 fn = config.fn || config.handler,
7592 scope = config.scope;
7594 if (config.stopEvent) {
7595 this.stopEvent = config.stopEvent;
7598 if(typeof keyCode == "string"){
7600 var keyString = keyCode.toUpperCase();
7601 for(var j = 0, len = keyString.length; j < len; j++){
7602 ks.push(keyString.charCodeAt(j));
7606 var keyArray = Ext.isArray(keyCode);
7608 var handler = function(e){
7609 if(this.checkModifiers(config, e)){
7612 for(var i = 0, len = keyCode.length; i < len; i++){
7613 if(keyCode[i] == k){
7617 fn.call(scope || window, k, e);
7626 fn.call(scope || window, k, e);
7631 this.bindings.push(handler);
7635 checkModifiers: function(config, e){
7636 var val, key, keys = ['shift', 'ctrl', 'alt'];
7637 for (var i = 0, len = keys.length; i < len; ++i){
7640 if(!(val === undefined || (val === e[key + 'Key']))){
7648 * Shorthand for adding a single key listener
7649 * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
7650 * following options:
7651 * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
7652 * @param {Function} fn The function to call
7653 * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the browser window.
7655 on : function(key, fn, scope){
7656 var keyCode, shift, ctrl, alt;
7657 if(typeof key == "object" && !Ext.isArray(key)){
7676 handleKeyDown : function(e){
7677 if(this.enabled){ //just in case
7678 var b = this.bindings;
7679 for(var i = 0, len = b.length; i < len; i++){
7686 * Returns true if this KeyMap is enabled
7689 isEnabled : function(){
7690 return this.enabled;
7694 * Enables this KeyMap
7698 this.el.on(this.eventName, this.handleKeyDown, this);
7699 this.enabled = true;
7704 * Disable this KeyMap
7706 disable: function(){
7708 this.el.removeListener(this.eventName, this.handleKeyDown, this);
7709 this.enabled = false;
7714 * Convenience function for setting disabled/enabled by boolean.
7715 * @param {Boolean} disabled
7717 setDisabled : function(disabled){
7718 this[disabled ? "disable" : "enable"]();
7721 * @class Ext.util.TextMetrics
7722 * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
7723 * wide, in pixels, a given block of text will be. Note that when measuring text, it should be plain text and
7724 * should not contain any HTML, otherwise it may not be measured correctly.
7727 Ext.util.TextMetrics = function(){
7731 * Measures the size of the specified text
7732 * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
7733 * that can affect the size of the rendered text
7734 * @param {String} text The text to measure
7735 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
7736 * in order to accurately measure the text height
7737 * @return {Object} An object containing the text's size {width: (width), height: (height)}
7739 measure : function(el, text, fixedWidth){
7741 shared = Ext.util.TextMetrics.Instance(el, fixedWidth);
7744 shared.setFixedWidth(fixedWidth || 'auto');
7745 return shared.getSize(text);
7749 * Return a unique TextMetrics instance that can be bound directly to an element and reused. This reduces
7750 * the overhead of multiple calls to initialize the style properties on each measurement.
7751 * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
7752 * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
7753 * in order to accurately measure the text height
7754 * @return {Ext.util.TextMetrics.Instance} instance The new instance
7756 createInstance : function(el, fixedWidth){
7757 return Ext.util.TextMetrics.Instance(el, fixedWidth);
7762 Ext.util.TextMetrics.Instance = function(bindTo, fixedWidth){
7763 var ml = new Ext.Element(document.createElement('div'));
7764 document.body.appendChild(ml.dom);
7765 ml.position('absolute');
7766 ml.setLeftTop(-1000, -1000);
7770 ml.setWidth(fixedWidth);
7775 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7776 * Returns the size of the specified text based on the internal element's style and width properties
7777 * @param {String} text The text to measure
7778 * @return {Object} An object containing the text's size {width: (width), height: (height)}
7780 getSize : function(text){
7782 var s = ml.getSize();
7788 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7789 * Binds this TextMetrics instance to an element from which to copy existing CSS styles
7790 * that can affect the size of the rendered text
7791 * @param {String/HTMLElement} el The element, dom node or id
7793 bind : function(el){
7795 Ext.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing')
7800 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7801 * Sets a fixed width on the internal measurement element. If the text will be multiline, you have
7802 * to set a fixed width in order to accurately measure the text height.
7803 * @param {Number} width The width to set on the element
7805 setFixedWidth : function(width){
7810 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7811 * Returns the measured width of the specified text
7812 * @param {String} text The text to measure
7813 * @return {Number} width The width in pixels
7815 getWidth : function(text){
7816 ml.dom.style.width = 'auto';
7817 return this.getSize(text).width;
7821 * <p><b>Only available on the instance returned from {@link #createInstance}, <u>not</u> on the singleton.</b></p>
7822 * Returns the measured height of the specified text. For multiline text, be sure to call
7823 * {@link #setFixedWidth} if necessary.
7824 * @param {String} text The text to measure
7825 * @return {Number} height The height in pixels
7827 getHeight : function(text){
7828 return this.getSize(text).height;
7832 instance.bind(bindTo);
7837 Ext.Element.addMethods({
7839 * Returns the width in pixels of the passed text, or the width of the text in this Element.
7840 * @param {String} text The text to measure. Defaults to the innerHTML of the element.
7841 * @param {Number} min (Optional) The minumum value to return.
7842 * @param {Number} max (Optional) The maximum value to return.
7843 * @return {Number} The text width in pixels.
7844 * @member Ext.Element getTextWidth
7846 getTextWidth : function(text, min, max){
7847 return (Ext.util.TextMetrics.measure(this.dom, Ext.value(text, this.dom.innerHTML, true)).width).constrain(min || 0, max || 1000000);
7851 * @class Ext.util.Cookies
7852 * Utility class for managing and interacting with cookies.
7855 Ext.util.Cookies = {
7857 * Create a cookie with the specified name and value. Additional settings
7858 * for the cookie may be optionally specified (for example: expiration,
7859 * access restriction, SSL).
7860 * @param {String} name The name of the cookie to set.
7861 * @param {Mixed} value The value to set for the cookie.
7862 * @param {Object} expires (Optional) Specify an expiration date the
7863 * cookie is to persist until. Note that the specified Date object will
7864 * be converted to Greenwich Mean Time (GMT).
7865 * @param {String} path (Optional) Setting a path on the cookie restricts
7866 * access to pages that match that path. Defaults to all pages (<tt>'/'</tt>).
7867 * @param {String} domain (Optional) Setting a domain restricts access to
7868 * pages on a given domain (typically used to allow cookie access across
7869 * subdomains). For example, "extjs.com" will create a cookie that can be
7870 * accessed from any subdomain of extjs.com, including www.extjs.com,
7871 * support.extjs.com, etc.
7872 * @param {Boolean} secure (Optional) Specify true to indicate that the cookie
7873 * should only be accessible via SSL on a page using the HTTPS protocol.
7874 * Defaults to <tt>false</tt>. Note that this will only work if the page
7875 * calling this code uses the HTTPS protocol, otherwise the cookie will be
7876 * created with default options.
7878 set : function(name, value){
7879 var argv = arguments;
7880 var argc = arguments.length;
7881 var expires = (argc > 2) ? argv[2] : null;
7882 var path = (argc > 3) ? argv[3] : '/';
7883 var domain = (argc > 4) ? argv[4] : null;
7884 var secure = (argc > 5) ? argv[5] : false;
7885 document.cookie = name + "=" + escape(value) + ((expires === null) ? "" : ("; expires=" + expires.toGMTString())) + ((path === null) ? "" : ("; path=" + path)) + ((domain === null) ? "" : ("; domain=" + domain)) + ((secure === true) ? "; secure" : "");
7889 * Retrieves cookies that are accessible by the current page. If a cookie
7890 * does not exist, <code>get()</code> returns <tt>null</tt>. The following
7891 * example retrieves the cookie called "valid" and stores the String value
7892 * in the variable <tt>validStatus</tt>.
7894 * var validStatus = Ext.util.Cookies.get("valid");
7896 * @param {String} name The name of the cookie to get
7897 * @return {Mixed} Returns the cookie value for the specified name;
7898 * null if the cookie name does not exist.
7900 get : function(name){
7901 var arg = name + "=";
7902 var alen = arg.length;
7903 var clen = document.cookie.length;
7908 if(document.cookie.substring(i, j) == arg){
7909 return Ext.util.Cookies.getCookieVal(j);
7911 i = document.cookie.indexOf(" ", i) + 1;
7920 * Removes a cookie with the provided name from the browser
7921 * if found by setting its expiration date to sometime in the past.
7922 * @param {String} name The name of the cookie to remove
7924 clear : function(name){
7925 if(Ext.util.Cookies.get(name)){
7926 document.cookie = name + "=" + "; expires=Thu, 01-Jan-70 00:00:01 GMT";
7932 getCookieVal : function(offset){
7933 var endstr = document.cookie.indexOf(";", offset);
7935 endstr = document.cookie.length;
7937 return unescape(document.cookie.substring(offset, endstr));
7940 * Framework-wide error-handler. Developers can override this method to provide
7941 * custom exception-handling. Framework errors will often extend from the base
7943 * @param {Object/Error} e The thrown exception object.
7945 Ext.handleError = function(e) {
7952 * <p>A base error class. Future implementations are intended to provide more
7953 * robust error handling throughout the framework (<b>in the debug build only</b>)
7954 * to check for common errors and problems. The messages issued by this class
7955 * will aid error checking. Error checks will be automatically removed in the
7956 * production build so that performance is not negatively impacted.</p>
7957 * <p>Some sample messages currently implemented:</p><pre>
7958 "DataProxy attempted to execute an API-action but found an undefined
7959 url / function. Please review your Proxy url/api-configuration."
7961 "Could not locate your "root" property in your server response.
7962 Please review your JsonReader config to ensure the config-property
7963 "root" matches the property your server-response. See the JsonReader
7964 docs for additional assistance."
7966 * <p>An example of the code used for generating error messages:</p><pre><code>
7975 function generateError(data) {
7976 throw new Ext.Error('foo-error', data);
7979 * @param {String} message
7981 Ext.Error = function(message) {
7982 // Try to read the message from Ext.Error.lang
7983 this.message = (this.lang[message]) ? this.lang[message] : message;
7986 Ext.Error.prototype = new Error();
7987 Ext.apply(Ext.Error.prototype, {
7988 // protected. Extensions place their error-strings here.
7996 getName : function() {
8003 getMessage : function() {
8004 return this.message;
8010 toJson : function() {
8011 return Ext.encode(this);